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