• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "content/child/npapi/webplugin_delegate_impl.h"
6 
7 #include <gtk/gtk.h>
8 #include <gdk/gdkx.h>
9 
10 #include <string>
11 #include <vector>
12 
13 #include "base/metrics/stats_counters.h"
14 #include "content/child/npapi/plugin_instance.h"
15 #include "content/child/npapi/webplugin.h"
16 #include "content/public/common/content_constants.h"
17 #include "skia/ext/platform_canvas.h"
18 #include "third_party/WebKit/public/web/WebInputEvent.h"
19 #include "ui/gfx/blit.h"
20 #include "ui/gfx/gtk_compat.h"
21 #include "webkit/common/cursors/webcursor.h"
22 
23 #include "third_party/npapi/bindings/npapi_x11.h"
24 
25 using blink::WebKeyboardEvent;
26 using blink::WebInputEvent;
27 using blink::WebMouseEvent;
28 
29 namespace content {
30 
WebPluginDelegateImpl(WebPlugin * plugin,PluginInstance * instance)31 WebPluginDelegateImpl::WebPluginDelegateImpl(
32     WebPlugin* plugin,
33     PluginInstance* instance)
34     : windowed_handle_(0),
35       windowed_did_set_window_(false),
36       windowless_(false),
37       plugin_(plugin),
38       instance_(instance),
39       windowless_shm_pixmap_(None),
40       pixmap_(NULL),
41       first_event_time_(-1.0),
42       plug_(NULL),
43       socket_(NULL),
44       quirks_(0),
45       handle_event_depth_(0),
46       first_set_window_call_(true),
47       plugin_has_focus_(false),
48       has_webkit_focus_(false),
49       containing_view_has_focus_(true),
50       creation_succeeded_(false) {
51   memset(&window_, 0, sizeof(window_));
52   if (instance_->mime_type() == kFlashPluginSwfMimeType) {
53     // Flash is tied to Firefox's whacky behavior with windowless plugins. See
54     // comments in WindowlessPaint.
55     // TODO(viettrungluu): PLUGIN_QUIRK_WINDOWLESS_NO_RIGHT_CLICK: Don't allow
56     // right-clicks in windowless content since Flash 10.1 (initial release, at
57     // least) hangs in that case. Remove this once Flash is fixed.
58     quirks_ |= PLUGIN_QUIRK_WINDOWLESS_OFFSET_WINDOW_TO_DRAW
59         | PLUGIN_QUIRK_WINDOWLESS_INVALIDATE_AFTER_SET_WINDOW
60         | PLUGIN_QUIRK_WINDOWLESS_NO_RIGHT_CLICK;
61   }
62 
63   // TODO(evanm): I played with this for quite a while but couldn't
64   // figure out a way to make Flash not crash unless I didn't call
65   // NPP_SetWindow.
66   // However, after piman's grand refactor of windowed plugins, maybe
67   // this is no longer necessary.
68   quirks_ |= PLUGIN_QUIRK_DONT_SET_NULL_WINDOW_HANDLE_ON_DESTROY;
69 }
70 
~WebPluginDelegateImpl()71 WebPluginDelegateImpl::~WebPluginDelegateImpl() {
72   DestroyInstance();
73 
74   if (!windowless_)
75     WindowedDestroyWindow();
76 
77   if (window_.ws_info) {
78     // We only ever use ws_info as an NPSetWindowCallbackStruct.
79     delete static_cast<NPSetWindowCallbackStruct*>(window_.ws_info);
80   }
81 
82   if (pixmap_) {
83     g_object_unref(pixmap_);
84     pixmap_ = NULL;
85   }
86 }
87 
PlatformInitialize()88 bool WebPluginDelegateImpl::PlatformInitialize() {
89   gfx::PluginWindowHandle handle =
90       windowless_ ? 0 : gtk_plug_get_id(GTK_PLUG(plug_));
91   plugin_->SetWindow(handle);
92   return true;
93 }
94 
PlatformDestroyInstance()95 void WebPluginDelegateImpl::PlatformDestroyInstance() {
96   // Nothing to do here.
97 }
98 
Paint(SkCanvas * canvas,const gfx::Rect & rect)99 void WebPluginDelegateImpl::Paint(SkCanvas* canvas, const gfx::Rect& rect) {
100   if (!windowless_ || !skia::SupportsPlatformPaint(canvas))
101     return;
102   skia::ScopedPlatformPaint scoped_platform_paint(canvas);
103   cairo_t* context = scoped_platform_paint.GetPlatformSurface();
104   WindowlessPaint(context, rect);
105 }
106 
WindowedCreatePlugin()107 bool WebPluginDelegateImpl::WindowedCreatePlugin() {
108   DCHECK(!windowed_handle_);
109   DCHECK(!plug_);
110 
111   // NPP_GetValue() might write 4 bytes of data to this variable.  Don't use a
112   // single byte bool, use an int instead and make sure it is initialized.
113   int xembed = 0;
114   NPError err = instance_->NPP_GetValue(NPPVpluginNeedsXEmbed, &xembed);
115   if (err != NPERR_NO_ERROR || !xembed) {
116     NOTIMPLEMENTED() << " windowed plugin but without xembed. "
117       "See http://code.google.com/p/chromium/issues/detail?id=38229";
118     return false;
119   }
120 
121   // Passing 0 as the socket XID creates a plug without plugging it in a socket
122   // yet, so that it can be latter added with gtk_socket_add_id().
123   plug_ = gtk_plug_new(0);
124   gtk_widget_show(plug_);
125   socket_ = gtk_socket_new();
126   gtk_widget_show(socket_);
127   gtk_container_add(GTK_CONTAINER(plug_), socket_);
128   gtk_widget_show_all(plug_);
129 
130   // Prevent the plug from being destroyed if the browser kills the container
131   // window.
132   g_signal_connect(plug_, "delete-event", G_CALLBACK(gtk_true), NULL);
133   // Prevent the socket from being destroyed when the plugin removes itself.
134   g_signal_connect(socket_, "plug_removed", G_CALLBACK(gtk_true), NULL);
135 
136   windowed_handle_ = gtk_socket_get_id(GTK_SOCKET(socket_));
137 
138   window_.window = reinterpret_cast<void*>(windowed_handle_);
139 
140   if (!window_.ws_info)
141     window_.ws_info = new NPSetWindowCallbackStruct;
142   NPSetWindowCallbackStruct* extra =
143       static_cast<NPSetWindowCallbackStruct*>(window_.ws_info);
144   extra->type = NP_SETWINDOW;
145   extra->display = GDK_DISPLAY();
146   int screen = DefaultScreen(GDK_DISPLAY());
147   extra->visual = DefaultVisual(GDK_DISPLAY(), screen);
148   extra->depth = DefaultDepth(GDK_DISPLAY(), screen);
149   extra->colormap = DefaultColormap(GDK_DISPLAY(), screen);
150 
151   return true;
152 }
153 
WindowedDestroyWindow()154 void WebPluginDelegateImpl::WindowedDestroyWindow() {
155   if (plug_) {
156     plugin_->WillDestroyWindow(gtk_plug_get_id(GTK_PLUG(plug_)));
157 
158     gtk_widget_destroy(plug_);
159     plug_ = NULL;
160     socket_ = NULL;
161     windowed_handle_ = 0;
162   }
163 }
164 
WindowedReposition(const gfx::Rect & window_rect,const gfx::Rect & clip_rect)165 bool WebPluginDelegateImpl::WindowedReposition(
166     const gfx::Rect& window_rect,
167     const gfx::Rect& clip_rect) {
168   if (window_rect == window_rect_ && clip_rect == clip_rect_)
169     return false;
170 
171   window_rect_ = window_rect;
172   clip_rect_ = clip_rect;
173 
174   return true;
175 }
176 
WindowedSetWindow()177 void WebPluginDelegateImpl::WindowedSetWindow() {
178   if (!instance_.get())
179     return;
180 
181   if (!windowed_handle_) {
182     NOTREACHED();
183     return;
184   }
185 
186   // See https://bugzilla.mozilla.org/show_bug.cgi?id=108347
187   // If we call NPP_SetWindow with a <= 0 width or height, problems arise in
188   // Flash (and possibly other plugins).
189   // TODO(piman): the Mozilla code suggests that for the Java plugin, we should
190   // still call NPP_SetWindow in that case. We need to verify that.
191   if (window_rect_.width() <= 0 || window_rect_.height() <= 0) {
192     return;
193   }
194 
195   instance()->set_window_handle(windowed_handle_);
196 
197   DCHECK(!instance()->windowless());
198 
199   window_.clipRect.top = clip_rect_.y();
200   window_.clipRect.left = clip_rect_.x();
201   window_.clipRect.bottom = clip_rect_.y() + clip_rect_.height();
202   window_.clipRect.right = clip_rect_.x() + clip_rect_.width();
203   window_.height = window_rect_.height();
204   window_.width = window_rect_.width();
205   window_.x = window_rect_.x();
206   window_.y = window_rect_.y();
207   window_.type = NPWindowTypeWindow;
208 
209   // Reset this flag before entering the instance in case of side-effects.
210   windowed_did_set_window_ = true;
211 
212   NPError err = instance()->NPP_SetWindow(&window_);
213   DCHECK(err == NPERR_NO_ERROR);
214 }
215 
WindowlessUpdateGeometry(const gfx::Rect & window_rect,const gfx::Rect & clip_rect)216 void WebPluginDelegateImpl::WindowlessUpdateGeometry(
217     const gfx::Rect& window_rect,
218     const gfx::Rect& clip_rect) {
219   // Only resend to the instance if the geometry has changed.
220   if (window_rect == window_rect_ && clip_rect == clip_rect_)
221     return;
222 
223   clip_rect_ = clip_rect;
224   window_rect_ = window_rect;
225   WindowlessSetWindow();
226 }
227 
EnsurePixmapAtLeastSize(int width,int height)228 void WebPluginDelegateImpl::EnsurePixmapAtLeastSize(int width, int height) {
229   if (pixmap_) {
230     gint cur_width, cur_height;
231     gdk_pixmap_get_size(pixmap_, &cur_width, &cur_height);
232     if (cur_width >= width && cur_height >= height)
233       return;  // We are already the appropriate size.
234 
235     // Otherwise, we need to recreate ourselves.
236     g_object_unref(pixmap_);
237     pixmap_ = NULL;
238   }
239 
240   // |sys_visual| is owned by gdk; we shouldn't free it.
241   GdkVisual* sys_visual = gdk_visual_get_system();
242   pixmap_ = gdk_pixmap_new(NULL,  // use width/height/depth params
243                            std::max(1, width), std::max(1, height),
244                            sys_visual->depth);
245   // TODO(erg): Replace this with GdkVisual when we move to GTK3.
246   GdkColormap* colormap = gdk_colormap_new(gdk_visual_get_system(),
247                                            FALSE);
248   gdk_drawable_set_colormap(pixmap_, colormap);
249   // The GdkDrawable now owns the GdkColormap.
250   g_object_unref(colormap);
251 }
252 
253 #ifdef DEBUG_RECTANGLES
254 namespace {
255 
256 // Draw a rectangle on a Cairo context.
257 // Useful for debugging various rectangles involved in drawing plugins.
DrawDebugRectangle(cairo_t * cairo,const gfx::Rect & rect,float r,float g,float b)258 void DrawDebugRectangle(cairo_t* cairo,
259                         const gfx::Rect& rect,
260                         float r, float g, float b) {
261   cairo_set_source_rgba(cairo, r, g, b, 0.5);
262   cairo_rectangle(cairo, rect.x(), rect.y(),
263                   rect.width(), rect.height());
264   cairo_stroke(cairo);
265 }
266 
267 }  // namespace
268 #endif
269 
WindowlessPaint(cairo_t * context,const gfx::Rect & damage_rect)270 void WebPluginDelegateImpl::WindowlessPaint(cairo_t* context,
271                                             const gfx::Rect& damage_rect) {
272   // Compare to:
273   // http://mxr.mozilla.org/firefox/source/layout/generic/nsObjectFrame.cpp:
274   // nsPluginInstanceOwner::Renderer::NativeDraw().
275 
276   DCHECK(context);
277 
278   // TODO(darin): we should avoid calling NPP_SetWindow here since it may
279   // cause page layout to be invalidated.
280 
281   // The actual dirty region is just the intersection of the plugin window and
282   // the clip window with the damage region. However, the plugin wants to draw
283   // relative to the containing window's origin, so our pixmap must be from the
284   // window's origin down to the bottom-right edge of the dirty region.
285   //
286   // Typical case:
287   // X-----------------------------------+-----------------------------+
288   // |                                   |                             |
289   // |    pixmap     +-------------------+                             |
290   // |               |   damage          |                window       |
291   // |               |                   |                             |
292   // |           +---+-------------------+-------------+               |
293   // |           |   |                   |   clip      |               |
294   // |       +---+---+-------------------+----------+  |               |
295   // |       |   |   |                   |          |  |               |
296   // |       |   |   | draw              |          |  |               |
297   // |       |   |   |                   |          |  |               |
298   // +-------+---+---+-------------------+----------+--+               |
299   // |       |       |                   |          |                  |
300   // |       |       +-------------------+          |                  |
301   // |       |                                      |                  |
302   // |       |        plugin                        |                  |
303   // |       +--------------------------------------+                  |
304   // |                                                                 |
305   // |                                                                 |
306   // +-----------------------------------------------------------------+
307   // X = origin
308   //
309   // NPAPI doesn't properly define which coordinates each of
310   // - window.clipRect, window.x and window.y in the SetWindow call
311   // - x and y in GraphicsExpose HandleEvent call
312   // are relative to, nor does it define what the pixmap is relative to.
313   //
314   // Any sane values for them just don't work with the flash plugin. Firefox
315   // has some interesting behavior. Experiments showed that:
316   // - window.clipRect is always in the same space as window.x and window.y
317   // - in the first SetWindow call, or when scrolling, window.x and window.y are
318   // the coordinates of the plugin relative to the window.
319   // - whenever only a part of the plugin is drawn, Firefox issues a SetWindow
320   // call before each GraphicsExpose event, that sets the drawing origin to
321   // (0, 0) as if the plugin was scrolled to be partially out of the view. The
322   // GraphicsExpose event has coordinates relative to the "window" (assuming
323   // that virtual scroll). The pixmap is also relative to the window. It always
324   // sets the clip rect to the draw rect.
325   //
326   // Attempts to deviate from that makes Flash render at the wrong place in the
327   // pixmap, or render the wrong pixels.
328   //
329   // Flash plugin:
330   // X-----------------------------------------------------------------+
331   // |                                                                 |
332   // |               +-------------------+        "real" window        |
333   // |               |   damage          |                             |
334   // |               |                   |                             |
335   // |           +---+-------------------+-------------+               |
336   // |           |   |                   | "real" clip |               |
337   // |       +---+---O===================#==========#==#===============#
338   // |       |   |   H draw              |          |  |               H
339   // |       |   |   H = pixmap          |          |  |               H
340   // |       |   |   H = "apparent" clip |          |  |               H
341   // |       +   +---#-------------------+----------+--+               H
342   // |       |       H                   |          |                  H
343   // |       |       H-------------------+          |                  H
344   // |       |       H                              |                  H
345   // |       |       H  plugin                      |                  H
346   // |       +-------#------------------------------+                  H
347   // |               H                                                 H
348   // |               H                  "apparent" window              H
349   // +---------------#=================================================#
350   // X = "real" origin
351   // O = "apparent" origin
352   // "real" means as seen by Chrome
353   // "apparent" means as seen by the plugin.
354 
355   gfx::Rect draw_rect = gfx::IntersectRects(window_rect_, damage_rect);
356 
357   // clip_rect_ is relative to the plugin
358   gfx::Rect clip_rect_window = clip_rect_;
359   clip_rect_window.Offset(window_rect_.x(), window_rect_.y());
360   draw_rect.Intersect(clip_rect_window);
361 
362   // These offsets represent by how much the view is shifted to accomodate
363   // Flash (the coordinates of X relative to O in the diagram above).
364   int offset_x = 0;
365   int offset_y = 0;
366   if (quirks_ & PLUGIN_QUIRK_WINDOWLESS_OFFSET_WINDOW_TO_DRAW) {
367     offset_x = -draw_rect.x();
368     offset_y = -draw_rect.y();
369     window_.clipRect.top = 0;
370     window_.clipRect.left = 0;
371     window_.clipRect.bottom = draw_rect.height();
372     window_.clipRect.right = draw_rect.width();
373     window_.height = window_rect_.height();
374     window_.width = window_rect_.width();
375     window_.x = window_rect_.x() - draw_rect.x();
376     window_.y = window_rect_.y() - draw_rect.y();
377     window_.type = NPWindowTypeDrawable;
378     DCHECK(window_.ws_info);
379     NPError err = instance()->NPP_SetWindow(&window_);
380     DCHECK_EQ(err, NPERR_NO_ERROR);
381   }
382 
383   gfx::Rect pixmap_draw_rect = draw_rect;
384   pixmap_draw_rect.Offset(offset_x, offset_y);
385 
386   gfx::Rect pixmap_rect(0, 0,
387                         pixmap_draw_rect.right(),
388                         pixmap_draw_rect.bottom());
389 
390   // Construct the paint message, targeting the pixmap.
391   NPEvent np_event = {0};
392   XGraphicsExposeEvent& event = np_event.xgraphicsexpose;
393   event.type = GraphicsExpose;
394   event.x = pixmap_draw_rect.x();
395   event.y = pixmap_draw_rect.y();
396   event.width = pixmap_draw_rect.width();
397   event.height = pixmap_draw_rect.height();
398   event.display = GDK_DISPLAY();
399 
400   if (windowless_shm_pixmap_ != None) {
401     Pixmap pixmap = None;
402     GC xgc = NULL;
403     Display* display = event.display;
404     gfx::Rect plugin_draw_rect = draw_rect;
405 
406     // Make plugin_draw_rect relative to the plugin window.
407     plugin_draw_rect.Offset(-window_rect_.x(), -window_rect_.y());
408 
409     // In case the drawing area does not start with the plugin window origin,
410     // we can not let the plugin directly draw over the shared memory pixmap.
411     if (plugin_draw_rect.x() != pixmap_draw_rect.x() ||
412         plugin_draw_rect.y() != pixmap_draw_rect.y()) {
413       pixmap = XCreatePixmap(display, windowless_shm_pixmap_,
414                              std::max(1, pixmap_rect.width()),
415                              std::max(1, pixmap_rect.height()),
416                              DefaultDepth(display, DefaultScreen(display)));
417       xgc = XCreateGC(display, windowless_shm_pixmap_, 0, NULL);
418       // Copy the current image into the pixmap, so the plugin can draw over it.
419       XCopyArea(display, windowless_shm_pixmap_, pixmap, xgc,
420                 plugin_draw_rect.x(), plugin_draw_rect.y(),
421                 pixmap_draw_rect.width(), pixmap_draw_rect.height(),
422                 pixmap_draw_rect.x(), pixmap_draw_rect.y());
423 
424       event.drawable = pixmap;
425     } else {
426       event.drawable = windowless_shm_pixmap_;
427     }
428 
429     // Tell the plugin to paint into the pixmap.
430     base::StatsRate plugin_paint("Plugin.Paint");
431     base::StatsScope<base::StatsRate> scope(plugin_paint);
432     instance()->NPP_HandleEvent(&np_event);
433 
434     if (pixmap != None) {
435       // Copy the rendered image pixmap back into the shm pixmap
436       // and thus the drawing buffer.
437       XCopyArea(display, pixmap, windowless_shm_pixmap_, xgc,
438                 pixmap_draw_rect.x(), pixmap_draw_rect.y(),
439                 pixmap_draw_rect.width(), pixmap_draw_rect.height(),
440                 plugin_draw_rect.x(), plugin_draw_rect.y());
441       XSync(display, FALSE);
442       if (xgc)
443         XFreeGC(display, xgc);
444       XFreePixmap(display, pixmap);
445     } else {
446       XSync(display, FALSE);
447     }
448   } else {
449     EnsurePixmapAtLeastSize(pixmap_rect.width(), pixmap_rect.height());
450 
451     // Copy the current image into the pixmap, so the plugin can draw over
452     // this background.
453     cairo_t* cairo = gdk_cairo_create(pixmap_);
454     BlitContextToContext(cairo, pixmap_draw_rect, context, draw_rect.origin());
455     cairo_destroy(cairo);
456 
457     event.drawable = GDK_PIXMAP_XID(pixmap_);
458 
459     // Tell the plugin to paint into the pixmap.
460     base::StatsRate plugin_paint("Plugin.Paint");
461     base::StatsScope<base::StatsRate> scope(plugin_paint);
462     instance()->NPP_HandleEvent(&np_event);
463 
464     cairo_save(context);
465     // Now copy the rendered image pixmap back into the drawing buffer.
466     gdk_cairo_set_source_pixmap(context, pixmap_, -offset_x, -offset_y);
467     cairo_rectangle(context, draw_rect.x(), draw_rect.y(),
468                     draw_rect.width(), draw_rect.height());
469     cairo_clip(context);
470     cairo_paint(context);
471 
472 #ifdef DEBUG_RECTANGLES
473     // Draw some debugging rectangles.
474     // Pixmap rect = blue.
475     DrawDebugRectangle(context, pixmap_rect, 0, 0, 1);
476     // Drawing rect = red.
477     DrawDebugRectangle(context, draw_rect, 1, 0, 0);
478 #endif
479     cairo_restore(context);
480   }
481 }
482 
WindowlessSetWindow()483 void WebPluginDelegateImpl::WindowlessSetWindow() {
484   if (!instance())
485     return;
486 
487   if (window_rect_.IsEmpty())  // wait for geometry to be set.
488     return;
489 
490   DCHECK(instance()->windowless());
491   // Mozilla docs say that this window param is not used for windowless
492   // plugins; rather, the window is passed during the GraphicsExpose event.
493   DCHECK_EQ(window_.window, static_cast<void*>(NULL));
494 
495   window_.clipRect.top = clip_rect_.y() + window_rect_.y();
496   window_.clipRect.left = clip_rect_.x() + window_rect_.x();
497   window_.clipRect.bottom =
498       clip_rect_.y() + clip_rect_.height() + window_rect_.y();
499   window_.clipRect.right =
500       clip_rect_.x() + clip_rect_.width() + window_rect_.x();
501   window_.height = window_rect_.height();
502   window_.width = window_rect_.width();
503   window_.x = window_rect_.x();
504   window_.y = window_rect_.y();
505   window_.type = NPWindowTypeDrawable;
506 
507   if (!window_.ws_info)
508     window_.ws_info = new NPSetWindowCallbackStruct;
509   NPSetWindowCallbackStruct* extra =
510       static_cast<NPSetWindowCallbackStruct*>(window_.ws_info);
511   extra->display = GDK_DISPLAY();
512   int screen = DefaultScreen(GDK_DISPLAY());
513   extra->visual = DefaultVisual(GDK_DISPLAY(), screen);
514   extra->depth = DefaultDepth(GDK_DISPLAY(), screen);
515   extra->colormap = DefaultColormap(GDK_DISPLAY(), screen);
516 
517   NPError err = instance()->NPP_SetWindow(&window_);
518   DCHECK(err == NPERR_NO_ERROR);
519   if (quirks_ & PLUGIN_QUIRK_WINDOWLESS_INVALIDATE_AFTER_SET_WINDOW) {
520     // After a NPP_SetWindow, Flash cancels its timer that generates the
521     // invalidates until it gets a paint event, but doesn't explicitly call
522     // NPP_InvalidateRect.
523     plugin_->InvalidateRect(clip_rect_);
524   }
525 }
526 
PlatformSetPluginHasFocus(bool focused)527 bool WebPluginDelegateImpl::PlatformSetPluginHasFocus(bool focused) {
528   DCHECK(instance()->windowless());
529 
530   NPEvent np_event = {0};
531   XFocusChangeEvent& event = np_event.xfocus;
532   event.type = focused ? FocusIn : FocusOut;
533   event.display = GDK_DISPLAY();
534   // Same values as Firefox. .serial and .window stay 0.
535   event.mode = -1;
536   event.detail = NotifyDetailNone;
537   instance()->NPP_HandleEvent(&np_event);
538   return true;
539 }
540 
541 // Converts a WebInputEvent::Modifiers bitfield into a
542 // corresponding X modifier state.
GetXModifierState(int modifiers)543 static int GetXModifierState(int modifiers) {
544   int x_state = 0;
545   if (modifiers & WebInputEvent::ControlKey)
546     x_state |= ControlMask;
547   if (modifiers & WebInputEvent::ShiftKey)
548     x_state |= ShiftMask;
549   if (modifiers & WebInputEvent::AltKey)
550     x_state |= Mod1Mask;
551   if (modifiers & WebInputEvent::MetaKey)
552     x_state |= Mod2Mask;
553   if (modifiers & WebInputEvent::LeftButtonDown)
554     x_state |= Button1Mask;
555   if (modifiers & WebInputEvent::MiddleButtonDown)
556     x_state |= Button2Mask;
557   if (modifiers & WebInputEvent::RightButtonDown)
558     x_state |= Button3Mask;
559   // TODO(piman@google.com): There are other modifiers, e.g. Num Lock, that
560   // should be set (and Firefox does), but we didn't keep the information in
561   // the WebKit event.
562   return x_state;
563 }
564 
NPEventFromWebMouseEvent(const WebMouseEvent & event,Time timestamp,NPEvent * np_event)565 static bool NPEventFromWebMouseEvent(const WebMouseEvent& event,
566                                      Time timestamp,
567                                      NPEvent* np_event) {
568   np_event->xany.display = GDK_DISPLAY();
569   // NOTE: Firefox keeps xany.serial and xany.window as 0.
570 
571   int modifier_state = GetXModifierState(event.modifiers);
572 
573   Window root = GDK_ROOT_WINDOW();
574   switch (event.type) {
575     case WebInputEvent::MouseMove: {
576       np_event->type = MotionNotify;
577       XMotionEvent& motion_event = np_event->xmotion;
578       motion_event.root = root;
579       motion_event.time = timestamp;
580       motion_event.x = event.x;
581       motion_event.y = event.y;
582       motion_event.x_root = event.globalX;
583       motion_event.y_root = event.globalY;
584       motion_event.state = modifier_state;
585       motion_event.is_hint = NotifyNormal;
586       motion_event.same_screen = True;
587       break;
588     }
589     case WebInputEvent::MouseLeave:
590     case WebInputEvent::MouseEnter: {
591       if (event.type == WebInputEvent::MouseEnter) {
592         np_event->type = EnterNotify;
593       } else {
594         np_event->type = LeaveNotify;
595       }
596       XCrossingEvent& crossing_event = np_event->xcrossing;
597       crossing_event.root = root;
598       crossing_event.time = timestamp;
599       crossing_event.x = event.x;
600       crossing_event.y = event.y;
601       crossing_event.x_root = event.globalX;
602       crossing_event.y_root = event.globalY;
603       crossing_event.mode = -1;  // This is what Firefox sets it to.
604       crossing_event.detail = NotifyDetailNone;
605       crossing_event.same_screen = True;
606       // TODO(piman@google.com): set this to the correct value. Firefox does. I
607       // don't know where to get the information though, we get focus
608       // notifications, but no unfocus.
609       crossing_event.focus = 0;
610       crossing_event.state = modifier_state;
611       break;
612     }
613     case WebInputEvent::MouseUp:
614     case WebInputEvent::MouseDown: {
615       if (event.type == WebInputEvent::MouseDown) {
616         np_event->type = ButtonPress;
617       } else {
618         np_event->type = ButtonRelease;
619       }
620       XButtonEvent& button_event = np_event->xbutton;
621       button_event.root = root;
622       button_event.time = timestamp;
623       button_event.x = event.x;
624       button_event.y = event.y;
625       button_event.x_root = event.globalX;
626       button_event.y_root = event.globalY;
627       button_event.state = modifier_state;
628       switch (event.button) {
629         case WebMouseEvent::ButtonLeft:
630           button_event.button = Button1;
631           break;
632         case WebMouseEvent::ButtonMiddle:
633           button_event.button = Button2;
634           break;
635         case WebMouseEvent::ButtonRight:
636           button_event.button = Button3;
637           break;
638         default:
639           NOTREACHED();
640       }
641       button_event.same_screen = True;
642       break;
643     }
644     default:
645       NOTREACHED();
646       return false;
647   }
648   return true;
649 }
650 
NPEventFromWebKeyboardEvent(const WebKeyboardEvent & event,Time timestamp,NPEvent * np_event)651 static bool NPEventFromWebKeyboardEvent(const WebKeyboardEvent& event,
652                                         Time timestamp,
653                                         NPEvent* np_event) {
654   np_event->xany.display = GDK_DISPLAY();
655   // NOTE: Firefox keeps xany.serial and xany.window as 0.
656 
657   switch (event.type) {
658     case WebKeyboardEvent::KeyDown:
659       np_event->type = KeyPress;
660       break;
661     case WebKeyboardEvent::KeyUp:
662       np_event->type = KeyRelease;
663       break;
664     default:
665       NOTREACHED();
666       return false;
667   }
668   XKeyEvent& key_event = np_event->xkey;
669   key_event.send_event = False;
670   key_event.display = GDK_DISPLAY();
671   // NOTE: Firefox keeps xany.serial and xany.window as 0.
672   // TODO(piman@google.com): is this right for multiple screens ?
673   key_event.root = DefaultRootWindow(key_event.display);
674   key_event.time = timestamp;
675   // NOTE: We don't have the correct information for x/y/x_root/y_root. Firefox
676   // doesn't have it either, so we pass the same values.
677   key_event.x = 0;
678   key_event.y = 0;
679   key_event.x_root = -1;
680   key_event.y_root = -1;
681   key_event.state = GetXModifierState(event.modifiers);
682   key_event.keycode = event.nativeKeyCode;
683   key_event.same_screen = True;
684   return true;
685 }
686 
NPEventFromWebInputEvent(const WebInputEvent & event,Time timestamp,NPEvent * np_event)687 static bool NPEventFromWebInputEvent(const WebInputEvent& event,
688                                      Time timestamp,
689                                      NPEvent* np_event) {
690   switch (event.type) {
691     case WebInputEvent::MouseMove:
692     case WebInputEvent::MouseLeave:
693     case WebInputEvent::MouseEnter:
694     case WebInputEvent::MouseDown:
695     case WebInputEvent::MouseUp:
696       if (event.size < sizeof(WebMouseEvent)) {
697         NOTREACHED();
698         return false;
699       }
700       return NPEventFromWebMouseEvent(
701           *static_cast<const WebMouseEvent*>(&event), timestamp, np_event);
702     case WebInputEvent::KeyDown:
703     case WebInputEvent::KeyUp:
704       if (event.size < sizeof(WebKeyboardEvent)) {
705         NOTREACHED();
706         return false;
707       }
708       return NPEventFromWebKeyboardEvent(
709           *static_cast<const WebKeyboardEvent*>(&event), timestamp, np_event);
710     default:
711       return false;
712   }
713 }
714 
PlatformHandleInputEvent(const WebInputEvent & event,WebCursor::CursorInfo * cursor_info)715 bool WebPluginDelegateImpl::PlatformHandleInputEvent(
716     const WebInputEvent& event, WebCursor::CursorInfo* cursor_info) {
717 
718   if (first_event_time_ < 0.0)
719     first_event_time_ = event.timeStampSeconds;
720   Time timestamp = static_cast<Time>(
721       (event.timeStampSeconds - first_event_time_) * 1.0e3);
722   NPEvent np_event = {0};
723   if (!NPEventFromWebInputEvent(event, timestamp, &np_event)) {
724     return false;
725   }
726   // See comment about PLUGIN_QUIRK_WINDOWLESS_NO_RIGHT_CLICK in constructor.
727   if (windowless_ &&
728       (quirks_ & PLUGIN_QUIRK_WINDOWLESS_NO_RIGHT_CLICK) &&
729       (np_event.type == ButtonPress || np_event.type == ButtonRelease) &&
730       (np_event.xbutton.button == Button3)) {
731     return false;
732   }
733 
734   bool ret = instance()->NPP_HandleEvent(&np_event) != 0;
735 
736   // Flash always returns false, even when the event is handled.
737   ret = true;
738 
739 #if 0
740   if (event->event == WM_MOUSEMOVE) {
741     // Snag a reference to the current cursor ASAP in case the plugin modified
742     // it. There is a nasty race condition here with the multiprocess browser
743     // as someone might be setting the cursor in the main process as well.
744     *cursor = current_windowless_cursor_;
745   }
746 #endif
747 
748   return ret;
749 }
750 
751 }  // namespace content
752