This source file includes following definitions.
- creation_succeeded_
- PlatformInitialize
- PlatformDestroyInstance
- Paint
- WindowedCreatePlugin
- WindowedDestroyWindow
- WindowedReposition
- WindowedSetWindow
- WindowlessUpdateGeometry
- EnsurePixmapAtLeastSize
- DrawDebugRectangle
- WindowlessPaint
- WindowlessSetWindow
- PlatformSetPluginHasFocus
- GetXModifierState
- NPEventFromWebMouseEvent
- NPEventFromWebKeyboardEvent
- NPEventFromWebInputEvent
- PlatformHandleInputEvent
#include "content/child/npapi/webplugin_delegate_impl.h"
#include <gdk/gdkx.h>
#include <gtk/gtk.h>
#include <string>
#include <vector>
#include "base/metrics/stats_counters.h"
#include "content/child/npapi/plugin_instance.h"
#include "content/child/npapi/webplugin.h"
#include "content/common/cursors/webcursor.h"
#include "content/public/common/content_constants.h"
#include "skia/ext/platform_canvas.h"
#include "third_party/WebKit/public/web/WebInputEvent.h"
#include "ui/gfx/blit.h"
#include "ui/gfx/gtk_compat.h"
#include "third_party/npapi/bindings/npapi_x11.h"
using blink::WebKeyboardEvent;
using blink::WebInputEvent;
using blink::WebMouseEvent;
namespace content {
WebPluginDelegateImpl::WebPluginDelegateImpl(
WebPlugin* plugin,
PluginInstance* instance)
: windowed_handle_(0),
windowed_did_set_window_(false),
windowless_(false),
plugin_(plugin),
instance_(instance),
windowless_shm_pixmap_(None),
pixmap_(NULL),
first_event_time_(-1.0),
plug_(NULL),
socket_(NULL),
quirks_(0),
handle_event_depth_(0),
first_set_window_call_(true),
plugin_has_focus_(false),
has_webkit_focus_(false),
containing_view_has_focus_(true),
creation_succeeded_(false) {
memset(&window_, 0, sizeof(window_));
if (instance_->mime_type() == kFlashPluginSwfMimeType) {
quirks_ |= PLUGIN_QUIRK_WINDOWLESS_OFFSET_WINDOW_TO_DRAW
| PLUGIN_QUIRK_WINDOWLESS_INVALIDATE_AFTER_SET_WINDOW
| PLUGIN_QUIRK_WINDOWLESS_NO_RIGHT_CLICK;
}
quirks_ |= PLUGIN_QUIRK_DONT_SET_NULL_WINDOW_HANDLE_ON_DESTROY;
}
WebPluginDelegateImpl::~WebPluginDelegateImpl() {
DestroyInstance();
if (!windowless_)
WindowedDestroyWindow();
if (window_.ws_info) {
delete static_cast<NPSetWindowCallbackStruct*>(window_.ws_info);
}
if (pixmap_) {
g_object_unref(pixmap_);
pixmap_ = NULL;
}
}
bool WebPluginDelegateImpl::PlatformInitialize() {
gfx::PluginWindowHandle handle =
windowless_ ? 0 : gtk_plug_get_id(GTK_PLUG(plug_));
plugin_->SetWindow(handle);
return true;
}
void WebPluginDelegateImpl::PlatformDestroyInstance() {
}
void WebPluginDelegateImpl::Paint(SkCanvas* canvas, const gfx::Rect& rect) {
if (!windowless_ || !skia::SupportsPlatformPaint(canvas))
return;
skia::ScopedPlatformPaint scoped_platform_paint(canvas);
cairo_t* context = scoped_platform_paint.GetPlatformSurface();
WindowlessPaint(context, rect);
}
bool WebPluginDelegateImpl::WindowedCreatePlugin() {
DCHECK(!windowed_handle_);
DCHECK(!plug_);
int xembed = 0;
NPError err = instance_->NPP_GetValue(NPPVpluginNeedsXEmbed, &xembed);
if (err != NPERR_NO_ERROR || !xembed) {
NOTIMPLEMENTED() << " windowed plugin but without xembed. "
"See http://code.google.com/p/chromium/issues/detail?id=38229";
return false;
}
plug_ = gtk_plug_new(0);
gtk_widget_show(plug_);
socket_ = gtk_socket_new();
gtk_widget_show(socket_);
gtk_container_add(GTK_CONTAINER(plug_), socket_);
gtk_widget_show_all(plug_);
g_signal_connect(plug_, "delete-event", G_CALLBACK(gtk_true), NULL);
g_signal_connect(socket_, "plug_removed", G_CALLBACK(gtk_true), NULL);
windowed_handle_ = gtk_socket_get_id(GTK_SOCKET(socket_));
window_.window = reinterpret_cast<void*>(windowed_handle_);
if (!window_.ws_info)
window_.ws_info = new NPSetWindowCallbackStruct;
NPSetWindowCallbackStruct* extra =
static_cast<NPSetWindowCallbackStruct*>(window_.ws_info);
extra->type = NP_SETWINDOW;
extra->display = GDK_DISPLAY();
int screen = DefaultScreen(GDK_DISPLAY());
extra->visual = DefaultVisual(GDK_DISPLAY(), screen);
extra->depth = DefaultDepth(GDK_DISPLAY(), screen);
extra->colormap = DefaultColormap(GDK_DISPLAY(), screen);
return true;
}
void WebPluginDelegateImpl::WindowedDestroyWindow() {
if (plug_) {
plugin_->WillDestroyWindow(gtk_plug_get_id(GTK_PLUG(plug_)));
gtk_widget_destroy(plug_);
plug_ = NULL;
socket_ = NULL;
windowed_handle_ = 0;
}
}
bool WebPluginDelegateImpl::WindowedReposition(
const gfx::Rect& window_rect,
const gfx::Rect& clip_rect) {
if (window_rect == window_rect_ && clip_rect == clip_rect_)
return false;
window_rect_ = window_rect;
clip_rect_ = clip_rect;
return true;
}
void WebPluginDelegateImpl::WindowedSetWindow() {
if (!instance_.get())
return;
if (!windowed_handle_) {
NOTREACHED();
return;
}
if (window_rect_.width() <= 0 || window_rect_.height() <= 0) {
return;
}
instance()->set_window_handle(windowed_handle_);
DCHECK(!instance()->windowless());
window_.clipRect.top = clip_rect_.y();
window_.clipRect.left = clip_rect_.x();
window_.clipRect.bottom = clip_rect_.y() + clip_rect_.height();
window_.clipRect.right = clip_rect_.x() + clip_rect_.width();
window_.height = window_rect_.height();
window_.width = window_rect_.width();
window_.x = window_rect_.x();
window_.y = window_rect_.y();
window_.type = NPWindowTypeWindow;
windowed_did_set_window_ = true;
NPError err = instance()->NPP_SetWindow(&window_);
DCHECK(err == NPERR_NO_ERROR);
}
void WebPluginDelegateImpl::WindowlessUpdateGeometry(
const gfx::Rect& window_rect,
const gfx::Rect& clip_rect) {
if (window_rect == window_rect_ && clip_rect == clip_rect_)
return;
clip_rect_ = clip_rect;
window_rect_ = window_rect;
WindowlessSetWindow();
}
void WebPluginDelegateImpl::EnsurePixmapAtLeastSize(int width, int height) {
if (pixmap_) {
gint cur_width, cur_height;
gdk_pixmap_get_size(pixmap_, &cur_width, &cur_height);
if (cur_width >= width && cur_height >= height)
return;
g_object_unref(pixmap_);
pixmap_ = NULL;
}
GdkVisual* sys_visual = gdk_visual_get_system();
pixmap_ = gdk_pixmap_new(NULL,
std::max(1, width), std::max(1, height),
sys_visual->depth);
GdkColormap* colormap = gdk_colormap_new(gdk_visual_get_system(),
FALSE);
gdk_drawable_set_colormap(pixmap_, colormap);
g_object_unref(colormap);
}
#ifdef DEBUG_RECTANGLES
namespace {
void DrawDebugRectangle(cairo_t* cairo,
const gfx::Rect& rect,
float r, float g, float b) {
cairo_set_source_rgba(cairo, r, g, b, 0.5);
cairo_rectangle(cairo, rect.x(), rect.y(),
rect.width(), rect.height());
cairo_stroke(cairo);
}
}
#endif
void WebPluginDelegateImpl::WindowlessPaint(cairo_t* context,
const gfx::Rect& damage_rect) {
DCHECK(context);
gfx::Rect draw_rect = gfx::IntersectRects(window_rect_, damage_rect);
gfx::Rect clip_rect_window = clip_rect_;
clip_rect_window.Offset(window_rect_.x(), window_rect_.y());
draw_rect.Intersect(clip_rect_window);
int offset_x = 0;
int offset_y = 0;
if (quirks_ & PLUGIN_QUIRK_WINDOWLESS_OFFSET_WINDOW_TO_DRAW) {
offset_x = -draw_rect.x();
offset_y = -draw_rect.y();
window_.clipRect.top = 0;
window_.clipRect.left = 0;
window_.clipRect.bottom = draw_rect.height();
window_.clipRect.right = draw_rect.width();
window_.height = window_rect_.height();
window_.width = window_rect_.width();
window_.x = window_rect_.x() - draw_rect.x();
window_.y = window_rect_.y() - draw_rect.y();
window_.type = NPWindowTypeDrawable;
DCHECK(window_.ws_info);
NPError err = instance()->NPP_SetWindow(&window_);
DCHECK_EQ(err, NPERR_NO_ERROR);
}
gfx::Rect pixmap_draw_rect = draw_rect;
pixmap_draw_rect.Offset(offset_x, offset_y);
gfx::Rect pixmap_rect(0, 0,
pixmap_draw_rect.right(),
pixmap_draw_rect.bottom());
NPEvent np_event = {0};
XGraphicsExposeEvent& event = np_event.xgraphicsexpose;
event.type = GraphicsExpose;
event.x = pixmap_draw_rect.x();
event.y = pixmap_draw_rect.y();
event.width = pixmap_draw_rect.width();
event.height = pixmap_draw_rect.height();
event.display = GDK_DISPLAY();
if (windowless_shm_pixmap_ != None) {
Pixmap pixmap = None;
GC xgc = NULL;
Display* display = event.display;
gfx::Rect plugin_draw_rect = draw_rect;
plugin_draw_rect.Offset(-window_rect_.x(), -window_rect_.y());
if (plugin_draw_rect.x() != pixmap_draw_rect.x() ||
plugin_draw_rect.y() != pixmap_draw_rect.y()) {
pixmap = XCreatePixmap(display, windowless_shm_pixmap_,
std::max(1, pixmap_rect.width()),
std::max(1, pixmap_rect.height()),
DefaultDepth(display, DefaultScreen(display)));
xgc = XCreateGC(display, windowless_shm_pixmap_, 0, NULL);
XCopyArea(display, windowless_shm_pixmap_, pixmap, xgc,
plugin_draw_rect.x(), plugin_draw_rect.y(),
pixmap_draw_rect.width(), pixmap_draw_rect.height(),
pixmap_draw_rect.x(), pixmap_draw_rect.y());
event.drawable = pixmap;
} else {
event.drawable = windowless_shm_pixmap_;
}
base::StatsRate plugin_paint("Plugin.Paint");
base::StatsScope<base::StatsRate> scope(plugin_paint);
instance()->NPP_HandleEvent(&np_event);
if (pixmap != None) {
XCopyArea(display, pixmap, windowless_shm_pixmap_, xgc,
pixmap_draw_rect.x(), pixmap_draw_rect.y(),
pixmap_draw_rect.width(), pixmap_draw_rect.height(),
plugin_draw_rect.x(), plugin_draw_rect.y());
XSync(display, FALSE);
if (xgc)
XFreeGC(display, xgc);
XFreePixmap(display, pixmap);
} else {
XSync(display, FALSE);
}
} else {
EnsurePixmapAtLeastSize(pixmap_rect.width(), pixmap_rect.height());
cairo_t* cairo = gdk_cairo_create(pixmap_);
BlitContextToContext(cairo, pixmap_draw_rect, context, draw_rect.origin());
cairo_destroy(cairo);
event.drawable = GDK_PIXMAP_XID(pixmap_);
base::StatsRate plugin_paint("Plugin.Paint");
base::StatsScope<base::StatsRate> scope(plugin_paint);
instance()->NPP_HandleEvent(&np_event);
cairo_save(context);
gdk_cairo_set_source_pixmap(context, pixmap_, -offset_x, -offset_y);
cairo_rectangle(context, draw_rect.x(), draw_rect.y(),
draw_rect.width(), draw_rect.height());
cairo_clip(context);
cairo_paint(context);
#ifdef DEBUG_RECTANGLES
DrawDebugRectangle(context, pixmap_rect, 0, 0, 1);
DrawDebugRectangle(context, draw_rect, 1, 0, 0);
#endif
cairo_restore(context);
}
}
void WebPluginDelegateImpl::WindowlessSetWindow() {
if (!instance())
return;
if (window_rect_.IsEmpty())
return;
DCHECK(instance()->windowless());
DCHECK_EQ(window_.window, static_cast<void*>(NULL));
window_.clipRect.top = clip_rect_.y() + window_rect_.y();
window_.clipRect.left = clip_rect_.x() + window_rect_.x();
window_.clipRect.bottom =
clip_rect_.y() + clip_rect_.height() + window_rect_.y();
window_.clipRect.right =
clip_rect_.x() + clip_rect_.width() + window_rect_.x();
window_.height = window_rect_.height();
window_.width = window_rect_.width();
window_.x = window_rect_.x();
window_.y = window_rect_.y();
window_.type = NPWindowTypeDrawable;
if (!window_.ws_info)
window_.ws_info = new NPSetWindowCallbackStruct;
NPSetWindowCallbackStruct* extra =
static_cast<NPSetWindowCallbackStruct*>(window_.ws_info);
extra->display = GDK_DISPLAY();
int screen = DefaultScreen(GDK_DISPLAY());
extra->visual = DefaultVisual(GDK_DISPLAY(), screen);
extra->depth = DefaultDepth(GDK_DISPLAY(), screen);
extra->colormap = DefaultColormap(GDK_DISPLAY(), screen);
NPError err = instance()->NPP_SetWindow(&window_);
DCHECK(err == NPERR_NO_ERROR);
if (quirks_ & PLUGIN_QUIRK_WINDOWLESS_INVALIDATE_AFTER_SET_WINDOW) {
plugin_->InvalidateRect(clip_rect_);
}
}
bool WebPluginDelegateImpl::PlatformSetPluginHasFocus(bool focused) {
DCHECK(instance()->windowless());
NPEvent np_event = {0};
XFocusChangeEvent& event = np_event.xfocus;
event.type = focused ? FocusIn : FocusOut;
event.display = GDK_DISPLAY();
event.mode = -1;
event.detail = NotifyDetailNone;
instance()->NPP_HandleEvent(&np_event);
return true;
}
static int GetXModifierState(int modifiers) {
int x_state = 0;
if (modifiers & WebInputEvent::ControlKey)
x_state |= ControlMask;
if (modifiers & WebInputEvent::ShiftKey)
x_state |= ShiftMask;
if (modifiers & WebInputEvent::AltKey)
x_state |= Mod1Mask;
if (modifiers & WebInputEvent::MetaKey)
x_state |= Mod2Mask;
if (modifiers & WebInputEvent::LeftButtonDown)
x_state |= Button1Mask;
if (modifiers & WebInputEvent::MiddleButtonDown)
x_state |= Button2Mask;
if (modifiers & WebInputEvent::RightButtonDown)
x_state |= Button3Mask;
return x_state;
}
static bool NPEventFromWebMouseEvent(const WebMouseEvent& event,
Time timestamp,
NPEvent* np_event) {
np_event->xany.display = GDK_DISPLAY();
int modifier_state = GetXModifierState(event.modifiers);
Window root = GDK_ROOT_WINDOW();
switch (event.type) {
case WebInputEvent::MouseMove: {
np_event->type = MotionNotify;
XMotionEvent& motion_event = np_event->xmotion;
motion_event.root = root;
motion_event.time = timestamp;
motion_event.x = event.x;
motion_event.y = event.y;
motion_event.x_root = event.globalX;
motion_event.y_root = event.globalY;
motion_event.state = modifier_state;
motion_event.is_hint = NotifyNormal;
motion_event.same_screen = True;
break;
}
case WebInputEvent::MouseLeave:
case WebInputEvent::MouseEnter: {
if (event.type == WebInputEvent::MouseEnter) {
np_event->type = EnterNotify;
} else {
np_event->type = LeaveNotify;
}
XCrossingEvent& crossing_event = np_event->xcrossing;
crossing_event.root = root;
crossing_event.time = timestamp;
crossing_event.x = event.x;
crossing_event.y = event.y;
crossing_event.x_root = event.globalX;
crossing_event.y_root = event.globalY;
crossing_event.mode = -1;
crossing_event.detail = NotifyDetailNone;
crossing_event.same_screen = True;
crossing_event.focus = 0;
crossing_event.state = modifier_state;
break;
}
case WebInputEvent::MouseUp:
case WebInputEvent::MouseDown: {
if (event.type == WebInputEvent::MouseDown) {
np_event->type = ButtonPress;
} else {
np_event->type = ButtonRelease;
}
XButtonEvent& button_event = np_event->xbutton;
button_event.root = root;
button_event.time = timestamp;
button_event.x = event.x;
button_event.y = event.y;
button_event.x_root = event.globalX;
button_event.y_root = event.globalY;
button_event.state = modifier_state;
switch (event.button) {
case WebMouseEvent::ButtonLeft:
button_event.button = Button1;
break;
case WebMouseEvent::ButtonMiddle:
button_event.button = Button2;
break;
case WebMouseEvent::ButtonRight:
button_event.button = Button3;
break;
default:
NOTREACHED();
}
button_event.same_screen = True;
break;
}
default:
NOTREACHED();
return false;
}
return true;
}
static bool NPEventFromWebKeyboardEvent(const WebKeyboardEvent& event,
Time timestamp,
NPEvent* np_event) {
np_event->xany.display = GDK_DISPLAY();
switch (event.type) {
case WebKeyboardEvent::KeyDown:
np_event->type = KeyPress;
break;
case WebKeyboardEvent::KeyUp:
np_event->type = KeyRelease;
break;
default:
NOTREACHED();
return false;
}
XKeyEvent& key_event = np_event->xkey;
key_event.send_event = False;
key_event.display = GDK_DISPLAY();
key_event.root = DefaultRootWindow(key_event.display);
key_event.time = timestamp;
key_event.x = 0;
key_event.y = 0;
key_event.x_root = -1;
key_event.y_root = -1;
key_event.state = GetXModifierState(event.modifiers);
key_event.keycode = event.nativeKeyCode;
key_event.same_screen = True;
return true;
}
static bool NPEventFromWebInputEvent(const WebInputEvent& event,
Time timestamp,
NPEvent* np_event) {
switch (event.type) {
case WebInputEvent::MouseMove:
case WebInputEvent::MouseLeave:
case WebInputEvent::MouseEnter:
case WebInputEvent::MouseDown:
case WebInputEvent::MouseUp:
if (event.size < sizeof(WebMouseEvent)) {
NOTREACHED();
return false;
}
return NPEventFromWebMouseEvent(
*static_cast<const WebMouseEvent*>(&event), timestamp, np_event);
case WebInputEvent::KeyDown:
case WebInputEvent::KeyUp:
if (event.size < sizeof(WebKeyboardEvent)) {
NOTREACHED();
return false;
}
return NPEventFromWebKeyboardEvent(
*static_cast<const WebKeyboardEvent*>(&event), timestamp, np_event);
default:
return false;
}
}
bool WebPluginDelegateImpl::PlatformHandleInputEvent(
const WebInputEvent& event, WebCursor::CursorInfo* cursor_info) {
if (first_event_time_ < 0.0)
first_event_time_ = event.timeStampSeconds;
Time timestamp = static_cast<Time>(
(event.timeStampSeconds - first_event_time_) * 1.0e3);
NPEvent np_event = {0};
if (!NPEventFromWebInputEvent(event, timestamp, &np_event)) {
return false;
}
if (windowless_ &&
(quirks_ & PLUGIN_QUIRK_WINDOWLESS_NO_RIGHT_CLICK) &&
(np_event.type == ButtonPress || np_event.type == ButtonRelease) &&
(np_event.xbutton.button == Button3)) {
return false;
}
bool ret = instance()->NPP_HandleEvent(&np_event) != 0;
ret = true;
#if 0
if (event->event == WM_MOUSEMOVE) {
*cursor = current_windowless_cursor_;
}
#endif
return ret;
}
}