summaryrefslogtreecommitdiffstats
path: root/webkit/glue/plugins/webplugin_delegate_impl_gtk.cc
diff options
context:
space:
mode:
Diffstat (limited to 'webkit/glue/plugins/webplugin_delegate_impl_gtk.cc')
-rw-r--r--webkit/glue/plugins/webplugin_delegate_impl_gtk.cc767
1 files changed, 767 insertions, 0 deletions
diff --git a/webkit/glue/plugins/webplugin_delegate_impl_gtk.cc b/webkit/glue/plugins/webplugin_delegate_impl_gtk.cc
new file mode 100644
index 0000000..609b41e
--- /dev/null
+++ b/webkit/glue/plugins/webplugin_delegate_impl_gtk.cc
@@ -0,0 +1,767 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/webplugin_delegate_impl.h"
+
+#include <string>
+#include <vector>
+
+#include <gtk/gtk.h>
+#include <gdk/gdkx.h>
+
+#include "base/basictypes.h"
+#include "base/file_util.h"
+#include "base/message_loop.h"
+#include "base/process_util.h"
+#include "base/metrics/stats_counters.h"
+#include "base/string_util.h"
+#include "gfx/blit.h"
+#include "skia/ext/platform_canvas.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebCursorInfo.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebInputEvent.h"
+#include "webkit/glue/plugins/gtk_plugin_container.h"
+#include "webkit/glue/plugins/plugin_constants_win.h"
+#include "webkit/glue/plugins/plugin_instance.h"
+#include "webkit/glue/plugins/plugin_lib.h"
+#include "webkit/glue/plugins/plugin_list.h"
+#include "webkit/glue/plugins/plugin_stream_url.h"
+#include "webkit/glue/plugins/webplugin.h"
+#include "webkit/glue/webkit_glue.h"
+
+#include "third_party/npapi/bindings/npapi_x11.h"
+
+using WebKit::WebCursorInfo;
+using WebKit::WebKeyboardEvent;
+using WebKit::WebInputEvent;
+using WebKit::WebMouseEvent;
+
+WebPluginDelegateImpl::WebPluginDelegateImpl(
+ gfx::PluginWindowHandle containing_view,
+ NPAPI::PluginInstance *instance)
+ : windowed_handle_(0),
+ windowed_did_set_window_(false),
+ windowless_(false),
+ plugin_(NULL),
+ instance_(instance),
+ windowless_shm_pixmap_(None),
+ pixmap_(NULL),
+ first_event_time_(-1.0),
+ plug_(NULL),
+ socket_(NULL),
+ parent_(containing_view),
+ 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() == "application/x-shockwave-flash") {
+ // Flash is tied to Firefox's whacky behavior with windowless plugins. See
+ // comments in WindowlessPaint.
+ // TODO(viettrungluu): PLUGIN_QUIRK_WINDOWLESS_NO_RIGHT_CLICK: Don't allow
+ // right-clicks in windowless content since Flash 10.1 (initial release, at
+ // least) hangs in that case. Remove this once Flash is fixed.
+ quirks_ |= PLUGIN_QUIRK_WINDOWLESS_OFFSET_WINDOW_TO_DRAW
+ | PLUGIN_QUIRK_WINDOWLESS_INVALIDATE_AFTER_SET_WINDOW
+ | PLUGIN_QUIRK_WINDOWLESS_NO_RIGHT_CLICK;
+ }
+
+ // TODO(evanm): I played with this for quite a while but couldn't
+ // figure out a way to make Flash not crash unless I didn't call
+ // NPP_SetWindow.
+ // However, after piman's grand refactor of windowed plugins, maybe
+ // this is no longer necessary.
+ quirks_ |= PLUGIN_QUIRK_DONT_SET_NULL_WINDOW_HANDLE_ON_DESTROY;
+}
+
+WebPluginDelegateImpl::~WebPluginDelegateImpl() {
+ DestroyInstance();
+
+ if (!windowless_)
+ WindowedDestroyWindow();
+
+ if (window_.ws_info) {
+ // We only ever use ws_info as an NPSetWindowCallbackStruct.
+ 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() {
+ // Nothing to do here.
+}
+
+void WebPluginDelegateImpl::Paint(WebKit::WebCanvas* canvas,
+ const gfx::Rect& rect) {
+ if (!windowless_)
+ return;
+ cairo_t* context = canvas->beginPlatformPaint();
+ WindowlessPaint(context, rect);
+ canvas->endPlatformPaint();
+}
+
+void WebPluginDelegateImpl::Print(cairo_t* context) {
+ NOTIMPLEMENTED();
+}
+
+void WebPluginDelegateImpl::InstallMissingPlugin() {
+ NOTIMPLEMENTED();
+}
+
+bool WebPluginDelegateImpl::WindowedCreatePlugin() {
+ DCHECK(!windowed_handle_);
+ DCHECK(!plug_);
+
+ // NPP_GetValue() might write 4 bytes of data to this variable. Don't use a
+ // single byte bool, use an int instead and make sure it is initialized.
+ 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;
+ }
+
+ // Passing 0 as the socket XID creates a plug without plugging it in a socket
+ // yet, so that it can be latter added with gtk_socket_add_id().
+ 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_);
+
+ // Prevent the plug from being destroyed if the browser kills the container
+ // window.
+ g_signal_connect(plug_, "delete-event", G_CALLBACK(gtk_true), NULL);
+ // Prevent the socket from being destroyed when the plugin removes itself.
+ 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->display = GDK_DISPLAY();
+ extra->visual = DefaultVisual(GDK_DISPLAY(), 0);
+ extra->depth = DefaultDepth(GDK_DISPLAY(), 0);
+ extra->colormap = DefaultColormap(GDK_DISPLAY(), 0);
+
+ 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_)
+ return;
+
+ if (!windowed_handle_) {
+ NOTREACHED();
+ return;
+ }
+
+ // See https://bugzilla.mozilla.org/show_bug.cgi?id=108347
+ // If we call NPP_SetWindow with a <= 0 width or height, problems arise in
+ // Flash (and possibly other plugins).
+ // TODO(piman): the Mozilla code suggests that for the Java plugin, we should
+ // still call NPP_SetWindow in that case. We need to verify that.
+ 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_.window = windowed_handle_;
+ window_.type = NPWindowTypeWindow;
+
+ // Reset this flag before entering the instance in case of side-effects.
+ 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) {
+ // Only resend to the instance if the geometry has changed.
+ 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_drawable_get_size(pixmap_, &cur_width, &cur_height);
+ if (cur_width >= width && cur_height >= height)
+ return; // We are already the appropriate size.
+
+ // Otherwise, we need to recreate ourselves.
+ g_object_unref(pixmap_);
+ pixmap_ = NULL;
+ }
+
+ // |sys_visual| is owned by gdk; we shouldn't free it.
+ GdkVisual* sys_visual = gdk_visual_get_system();
+ pixmap_ = gdk_pixmap_new(NULL, // use width/height/depth params
+ 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(GDK_DRAWABLE(pixmap_), colormap);
+ // The GdkDrawable now owns the GdkColormap.
+ g_object_unref(colormap);
+}
+
+#ifdef DEBUG_RECTANGLES
+namespace {
+
+// Draw a rectangle on a Cairo context.
+// Useful for debugging various rectangles involved in drawing plugins.
+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);
+}
+
+} // namespace
+#endif
+
+void WebPluginDelegateImpl::WindowlessPaint(cairo_t* context,
+ const gfx::Rect& damage_rect) {
+ // Compare to:
+ // http://mxr.mozilla.org/firefox/source/layout/generic/nsObjectFrame.cpp:
+ // nsPluginInstanceOwner::Renderer::NativeDraw().
+
+ DCHECK(context);
+
+ // TODO(darin): we should avoid calling NPP_SetWindow here since it may
+ // cause page layout to be invalidated.
+
+ // The actual dirty region is just the intersection of the plugin window and
+ // the clip window with the damage region. However, the plugin wants to draw
+ // relative to the containing window's origin, so our pixmap must be from the
+ // window's origin down to the bottom-right edge of the dirty region.
+ //
+ // Typical case:
+ // X-----------------------------------+-----------------------------+
+ // | | |
+ // | pixmap +-------------------+ |
+ // | | damage | window |
+ // | | | |
+ // | +---+-------------------+-------------+ |
+ // | | | | clip | |
+ // | +---+---+-------------------+----------+ | |
+ // | | | | | | | |
+ // | | | | draw | | | |
+ // | | | | | | | |
+ // +-------+---+---+-------------------+----------+--+ |
+ // | | | | | |
+ // | | +-------------------+ | |
+ // | | | |
+ // | | plugin | |
+ // | +--------------------------------------+ |
+ // | |
+ // | |
+ // +-----------------------------------------------------------------+
+ // X = origin
+ //
+ // NPAPI doesn't properly define which coordinates each of
+ // - window.clipRect, window.x and window.y in the SetWindow call
+ // - x and y in GraphicsExpose HandleEvent call
+ // are relative to, nor does it define what the pixmap is relative to.
+ //
+ // Any sane values for them just don't work with the flash plugin. Firefox
+ // has some interesting behavior. Experiments showed that:
+ // - window.clipRect is always in the same space as window.x and window.y
+ // - in the first SetWindow call, or when scrolling, window.x and window.y are
+ // the coordinates of the plugin relative to the window.
+ // - whenever only a part of the plugin is drawn, Firefox issues a SetWindow
+ // call before each GraphicsExpose event, that sets the drawing origin to
+ // (0, 0) as if the plugin was scrolled to be partially out of the view. The
+ // GraphicsExpose event has coordinates relative to the "window" (assuming
+ // that virtual scroll). The pixmap is also relative to the window. It always
+ // sets the clip rect to the draw rect.
+ //
+ // Attempts to deviate from that makes Flash render at the wrong place in the
+ // pixmap, or render the wrong pixels.
+ //
+ // Flash plugin:
+ // X-----------------------------------------------------------------+
+ // | |
+ // | +-------------------+ "real" window |
+ // | | damage | |
+ // | | | |
+ // | +---+-------------------+-------------+ |
+ // | | | | "real" clip | |
+ // | +---+---O===================#==========#==#===============#
+ // | | | H draw | | | H
+ // | | | H = pixmap | | | H
+ // | | | H = "apparent" clip | | | H
+ // | + +---#-------------------+----------+--+ H
+ // | | H | | H
+ // | | H-------------------+ | H
+ // | | H | H
+ // | | H plugin | H
+ // | +-------#------------------------------+ H
+ // | H H
+ // | H "apparent" window H
+ // +---------------#=================================================#
+ // X = "real" origin
+ // O = "apparent" origin
+ // "real" means as seen by Chrome
+ // "apparent" means as seen by the plugin.
+
+ gfx::Rect draw_rect = window_rect_.Intersect(damage_rect);
+
+ // clip_rect_ is relative to the plugin
+ gfx::Rect clip_rect_window = clip_rect_;
+ clip_rect_window.Offset(window_rect_.x(), window_rect_.y());
+ draw_rect = draw_rect.Intersect(clip_rect_window);
+
+ // These offsets represent by how much the view is shifted to accomodate
+ // Flash (the coordinates of X relative to O in the diagram above).
+ 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());
+
+ // Construct the paint message, targeting the pixmap.
+ 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;
+
+ // Make plugin_draw_rect relative to the plugin window.
+ plugin_draw_rect.Offset(-window_rect_.x(), -window_rect_.y());
+
+ // In case the drawing area does not start with the plugin window origin,
+ // we can not let the plugin directly draw over the shared memory pixmap.
+ 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, 0));
+ xgc = XCreateGC(display, windowless_shm_pixmap_, 0, NULL);
+ // Copy the current image into the pixmap, so the plugin can draw over it.
+ 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_;
+ }
+
+ // Tell the plugin to paint into the pixmap.
+ static base::StatsRate plugin_paint("Plugin.Paint");
+ base::StatsScope<base::StatsRate> scope(plugin_paint);
+ NPError err = instance()->NPP_HandleEvent(&np_event);
+ DCHECK_EQ(err, NPERR_NO_ERROR);
+
+ if (pixmap != None) {
+ // Copy the rendered image pixmap back into the shm pixmap
+ // and thus the drawing buffer.
+ 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());
+
+ // Copy the current image into the pixmap, so the plugin can draw over
+ // this background.
+ 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_);
+
+ // Tell the plugin to paint into the pixmap.
+ static base::StatsRate plugin_paint("Plugin.Paint");
+ base::StatsScope<base::StatsRate> scope(plugin_paint);
+ NPError err = instance()->NPP_HandleEvent(&np_event);
+ DCHECK_EQ(err, NPERR_NO_ERROR);
+
+ cairo_save(context);
+ // Now copy the rendered image pixmap back into the drawing buffer.
+ 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
+ // Draw some debugging rectangles.
+ // Pixmap rect = blue.
+ DrawDebugRectangle(context, pixmap_rect, 0, 0, 1);
+ // Drawing rect = red.
+ DrawDebugRectangle(context, draw_rect, 1, 0, 0);
+#endif
+ cairo_restore(context);
+ }
+}
+
+void WebPluginDelegateImpl::WindowlessSetWindow() {
+ if (!instance())
+ return;
+
+ if (window_rect_.IsEmpty()) // wait for geometry to be set.
+ return;
+
+ DCHECK(instance()->windowless());
+ // Mozilla docs say that this window param is not used for windowless
+ // plugins; rather, the window is passed during the GraphicsExpose event.
+ DCHECK(window_.window == 0);
+
+ 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();
+ extra->visual = DefaultVisual(GDK_DISPLAY(), 0);
+ extra->depth = DefaultDepth(GDK_DISPLAY(), 0);
+ extra->colormap = DefaultColormap(GDK_DISPLAY(), 0);
+
+ NPError err = instance()->NPP_SetWindow(&window_);
+ DCHECK(err == NPERR_NO_ERROR);
+ if (quirks_ & PLUGIN_QUIRK_WINDOWLESS_INVALIDATE_AFTER_SET_WINDOW) {
+ // After a NPP_SetWindow, Flash cancels its timer that generates the
+ // invalidates until it gets a paint event, but doesn't explicitly call
+ // NPP_InvalidateRect.
+ 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();
+ // Same values as Firefox. .serial and .window stay 0.
+ event.mode = -1;
+ event.detail = NotifyDetailNone;
+ instance()->NPP_HandleEvent(&np_event);
+ return true;
+}
+
+// Converts a WebInputEvent::Modifiers bitfield into a
+// corresponding X modifier state.
+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;
+ // TODO(piman@google.com): There are other modifiers, e.g. Num Lock, that
+ // should be set (and Firefox does), but we didn't keep the information in
+ // the WebKit event.
+ return x_state;
+}
+
+static bool NPEventFromWebMouseEvent(const WebMouseEvent& event,
+ Time timestamp,
+ NPEvent *np_event) {
+ np_event->xany.display = GDK_DISPLAY();
+ // NOTE: Firefox keeps xany.serial and xany.window as 0.
+
+ 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; // This is what Firefox sets it to.
+ crossing_event.detail = NotifyDetailNone;
+ crossing_event.same_screen = True;
+ // TODO(piman@google.com): set this to the correct value. Firefox does. I
+ // don't know where to get the information though, we get focus
+ // notifications, but no unfocus.
+ 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();
+ // NOTE: Firefox keeps xany.serial and xany.window as 0.
+
+ 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();
+ // NOTE: Firefox keeps xany.serial and xany.window as 0.
+ // TODO(piman@google.com): is this right for multiple screens ?
+ key_event.root = DefaultRootWindow(key_event.display);
+ key_event.time = timestamp;
+ // NOTE: We don't have the correct information for x/y/x_root/y_root. Firefox
+ // doesn't have it either, so we pass the same values.
+ 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, WebCursorInfo* 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;
+ }
+ // See comment about PLUGIN_QUIRK_WINDOWLESS_NO_RIGHT_CLICK in constructor.
+ 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;
+
+ // Flash always returns false, even when the event is handled.
+ ret = true;
+
+#if 0
+ if (event->event == WM_MOUSEMOVE) {
+ // Snag a reference to the current cursor ASAP in case the plugin modified
+ // it. There is a nasty race condition here with the multiprocess browser
+ // as someone might be setting the cursor in the main process as well.
+ *cursor = current_windowless_cursor_;
+ }
+#endif
+
+ return ret;
+}