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 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 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 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 95 void WebPluginDelegateImpl::PlatformDestroyInstance() { 96 // Nothing to do here. 97 } 98 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 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 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 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 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 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 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. 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 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 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 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. 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 (at) 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 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 (at) 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 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 (at) 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 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 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