OLD | NEW |
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 // HACK: we need this #define in place before npapi.h is included for | 5 // HACK: we need this #define in place before npapi.h is included for |
6 // plugins to work. However, all sorts of headers include npapi.h, so | 6 // plugins to work. However, all sorts of headers include npapi.h, so |
7 // the only way to be certain the define is in place is to put it | 7 // the only way to be certain the define is in place is to put it |
8 // here. You might ask, "Why not set it in npapi.h directly, or in | 8 // here. You might ask, "Why not set it in npapi.h directly, or in |
9 // this directory's SConscript, then?" but it turns out this define | 9 // this directory's SConscript, then?" but it turns out this define |
10 // makes npapi.h include Xlib.h, which in turn defines a ton of symbols | 10 // makes npapi.h include Xlib.h, which in turn defines a ton of symbols |
(...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
164 const gfx::Rect& clip_rect) { | 164 const gfx::Rect& clip_rect) { |
165 if (windowless_) { | 165 if (windowless_) { |
166 WindowlessUpdateGeometry(window_rect, clip_rect); | 166 WindowlessUpdateGeometry(window_rect, clip_rect); |
167 } else { | 167 } else { |
168 WindowedUpdateGeometry(window_rect, clip_rect); | 168 WindowedUpdateGeometry(window_rect, clip_rect); |
169 } | 169 } |
170 } | 170 } |
171 | 171 |
172 void WebPluginDelegateImpl::Paint(cairo_surface_t* context, | 172 void WebPluginDelegateImpl::Paint(cairo_surface_t* context, |
173 const gfx::Rect& rect) { | 173 const gfx::Rect& rect) { |
174 if (windowless_) { | 174 if (windowless_) |
175 WindowlessPaint(context, rect); | 175 WindowlessPaint(context, rect); |
176 } | |
177 } | 176 } |
178 | 177 |
179 void WebPluginDelegateImpl::Print(cairo_surface_t* context) { | 178 void WebPluginDelegateImpl::Print(cairo_surface_t* context) { |
180 NOTIMPLEMENTED(); | 179 NOTIMPLEMENTED(); |
181 } | 180 } |
182 | 181 |
183 NPObject* WebPluginDelegateImpl::GetPluginScriptableObject() { | 182 NPObject* WebPluginDelegateImpl::GetPluginScriptableObject() { |
184 return instance_->GetPluginScriptableObject(); | 183 return instance_->GetPluginScriptableObject(); |
185 } | 184 } |
186 | 185 |
(...skipping 266 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
453 | 452 |
454 // |sys_visual| is owned by gdk; we shouldn't free it. | 453 // |sys_visual| is owned by gdk; we shouldn't free it. |
455 GdkVisual* sys_visual = gdk_visual_get_system(); | 454 GdkVisual* sys_visual = gdk_visual_get_system(); |
456 pixmap_ = gdk_pixmap_new(NULL, // use width/height/depth params | 455 pixmap_ = gdk_pixmap_new(NULL, // use width/height/depth params |
457 width, height, sys_visual->depth); | 456 width, height, sys_visual->depth); |
458 GdkColormap* colormap = gdk_colormap_new(gdk_visual_get_system(), | 457 GdkColormap* colormap = gdk_colormap_new(gdk_visual_get_system(), |
459 FALSE); | 458 FALSE); |
460 gdk_drawable_set_colormap(GDK_DRAWABLE(pixmap_), colormap); | 459 gdk_drawable_set_colormap(GDK_DRAWABLE(pixmap_), colormap); |
461 } | 460 } |
462 | 461 |
| 462 #ifdef DEBUG_RECTANGLES |
| 463 namespace { |
| 464 |
| 465 // Draw a rectangle on a Cairo surface. |
| 466 // Useful for debugging various rectangles involved in drawing plugins. |
| 467 void DrawDebugRectangle(cairo_surface_t* surface, |
| 468 const gfx::Rect& rect, |
| 469 float r, float g, float b) { |
| 470 cairo_t* cairo = cairo_create(surface); |
| 471 cairo_set_source_rgba(cairo, r, g, b, 0.5); |
| 472 cairo_rectangle(cairo, rect.x(), rect.y(), |
| 473 rect.width(), rect.height()); |
| 474 cairo_stroke(cairo); |
| 475 cairo_destroy(cairo); |
| 476 } |
| 477 |
| 478 } // namespace |
| 479 #endif |
| 480 |
463 void WebPluginDelegateImpl::WindowlessPaint(cairo_surface_t* context, | 481 void WebPluginDelegateImpl::WindowlessPaint(cairo_surface_t* context, |
464 const gfx::Rect& damage_rect) { | 482 const gfx::Rect& damage_rect) { |
465 // Compare to: | 483 // Compare to: |
466 // http://mxr.mozilla.org/firefox/source/layout/generic/nsObjectFrame.cpp: | 484 // http://mxr.mozilla.org/firefox/source/layout/generic/nsObjectFrame.cpp: |
467 // nsPluginInstanceOwner::Renderer::NativeDraw(). | 485 // nsPluginInstanceOwner::Renderer::NativeDraw(). |
468 | 486 |
469 DCHECK(context); | 487 DCHECK(context); |
470 | 488 |
471 // We need to pass the DC to the plugin via NPP_SetWindow in the | 489 // We need to pass the DC to the plugin via NPP_SetWindow in the |
472 // first paint to ensure that it initiates rect invalidations. | 490 // first paint to ensure that it initiates rect invalidations. |
473 // TODO(evanm): for now, it appears we always need to do this. | 491 // TODO(evanm): for now, it appears we always need to do this. |
474 if (true) | 492 if (true) |
475 windowless_needs_set_window_ = true; | 493 windowless_needs_set_window_ = true; |
476 | 494 |
477 // TODO(darin): we should avoid calling NPP_SetWindow here since it may | 495 // TODO(darin): we should avoid calling NPP_SetWindow here since it may |
478 // cause page layout to be invalidated. | 496 // cause page layout to be invalidated. |
479 | 497 |
480 // We really don't need to continually call SetWindow. | 498 // We really don't need to continually call SetWindow. |
481 // m_needsSetWindow flags when the geometry has changed. | 499 // m_needsSetWindow flags when the geometry has changed. |
482 if (windowless_needs_set_window_) | 500 if (windowless_needs_set_window_) |
483 WindowlessSetWindow(false); | 501 WindowlessSetWindow(false); |
484 | 502 |
485 EnsurePixmapAtLeastSize(damage_rect.width(), damage_rect.height()); | 503 // The actual dirty region is just the intersection of the plugin |
| 504 // window with the damage region. However, the plugin wants to draw |
| 505 // relative to the containing window's origin, so our pixmap must be |
| 506 // from the window's origin down to the bottom-right edge of the |
| 507 // dirty region. |
| 508 // |
| 509 // +-----------------------------+-----------------------------+ |
| 510 // | | | |
| 511 // | pixmap +-------------+ | |
| 512 // | | damage | window | |
| 513 // | | | | |
| 514 // | +-------+-------------+----------+ | |
| 515 // | | | draw | | | |
| 516 // +-------+-------+-------------+ | | |
| 517 // | | | | |
| 518 // | | plugin | | |
| 519 // | +--------------------------------+ | |
| 520 // | | |
| 521 // | | |
| 522 // +-----------------------------------------------------------+ |
| 523 // |
| 524 // TOOD(evanm): on Windows, we instead just translate the origin of |
| 525 // the DC that we hand to the plugin. Does such a thing exist on X? |
| 526 // TODO(evanm): make use of the clip rect as well. |
| 527 |
| 528 gfx::Rect plugin_rect(window_.x, window_.y, window_.width, window_.height); |
| 529 gfx::Rect draw_rect = plugin_rect.Intersect(damage_rect); |
| 530 |
| 531 gfx::Rect pixmap_rect(0, 0, |
| 532 draw_rect.x() + draw_rect.width(), |
| 533 draw_rect.y() + draw_rect.height()); |
| 534 |
| 535 EnsurePixmapAtLeastSize(pixmap_rect.width(), pixmap_rect.height()); |
486 | 536 |
487 // Copy the current image into the pixmap, so the plugin can draw over | 537 // Copy the current image into the pixmap, so the plugin can draw over |
488 // this background. | 538 // this background. |
489 cairo_t* cairo = gdk_cairo_create(pixmap_); | 539 cairo_t* cairo = gdk_cairo_create(pixmap_); |
490 cairo_set_source_surface(cairo, context, 0, 0); | 540 cairo_set_source_surface(cairo, context, 0, 0); |
| 541 cairo_rectangle(cairo, draw_rect.x(), draw_rect.y(), |
| 542 draw_rect.width(), draw_rect.height()); |
| 543 cairo_clip(cairo); |
491 cairo_paint(cairo); | 544 cairo_paint(cairo); |
492 cairo_destroy(cairo); | 545 cairo_destroy(cairo); |
493 | 546 |
494 // Construct the paint message, targeting the pixmap. | 547 // Construct the paint message, targeting the pixmap. |
495 XGraphicsExposeEvent event = {0}; | 548 XGraphicsExposeEvent event = {0}; |
496 event.type = GraphicsExpose; | 549 event.type = GraphicsExpose; |
497 event.display = GDK_DISPLAY(); | 550 event.display = GDK_DISPLAY(); |
498 event.drawable = GDK_PIXMAP_XID(pixmap_); | 551 event.drawable = GDK_PIXMAP_XID(pixmap_); |
499 event.x = damage_rect.x(); | 552 event.x = draw_rect.x(); |
500 event.y = damage_rect.y(); | 553 event.y = draw_rect.y(); |
501 event.width = damage_rect.width(); | 554 event.width = draw_rect.width(); |
502 event.height = damage_rect.height(); | 555 event.height = draw_rect.height(); |
503 | 556 |
504 // Tell the plugin to paint into the pixmap. | 557 // Tell the plugin to paint into the pixmap. |
505 static StatsRate plugin_paint("Plugin.Paint"); | 558 static StatsRate plugin_paint("Plugin.Paint"); |
506 StatsScope<StatsRate> scope(plugin_paint); | 559 StatsScope<StatsRate> scope(plugin_paint); |
507 NPError err = instance()->NPP_HandleEvent(reinterpret_cast<XEvent*>(&event)); | 560 NPError err = instance()->NPP_HandleEvent(reinterpret_cast<XEvent*>(&event)); |
508 DCHECK_EQ(err, NPERR_NO_ERROR); | 561 DCHECK_EQ(err, NPERR_NO_ERROR); |
509 | 562 |
510 // Now copy the rendered image pixmap back into the drawing buffer. | 563 // Now copy the rendered image pixmap back into the drawing buffer. |
511 cairo = cairo_create(context); | 564 cairo = cairo_create(context); |
512 gdk_cairo_set_source_pixmap(cairo, pixmap_, 0, 0); | 565 gdk_cairo_set_source_pixmap(cairo, pixmap_, 0, 0); |
| 566 cairo_rectangle(cairo, draw_rect.x(), draw_rect.y(), |
| 567 draw_rect.width(), draw_rect.height()); |
| 568 cairo_clip(cairo); |
513 cairo_paint(cairo); | 569 cairo_paint(cairo); |
514 cairo_destroy(cairo); | 570 cairo_destroy(cairo); |
| 571 |
| 572 #ifdef DEBUG_RECTANGLES |
| 573 // Draw some debugging rectangles. |
| 574 // Pixmap rect = blue. |
| 575 DrawDebugRectangle(context, pixmap_rect, 0, 0, 1); |
| 576 // Drawing rect = red. |
| 577 DrawDebugRectangle(context, draw_rect, 1, 0, 0); |
| 578 #endif |
515 } | 579 } |
516 | 580 |
517 void WebPluginDelegateImpl::WindowlessSetWindow(bool force_set_window) { | 581 void WebPluginDelegateImpl::WindowlessSetWindow(bool force_set_window) { |
518 if (!instance()) | 582 if (!instance()) |
519 return; | 583 return; |
520 | 584 |
521 if (window_rect_.IsEmpty()) // wait for geometry to be set. | 585 if (window_rect_.IsEmpty()) // wait for geometry to be set. |
522 return; | 586 return; |
523 | 587 |
524 DCHECK(instance()->windowless()); | 588 DCHECK(instance()->windowless()); |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
561 /* NPEvent focus_event; | 625 /* NPEvent focus_event; |
562 focus_event.event = WM_SETFOCUS; | 626 focus_event.event = WM_SETFOCUS; |
563 focus_event.wParam = 0; | 627 focus_event.wParam = 0; |
564 focus_event.lParam = 0; | 628 focus_event.lParam = 0; |
565 | 629 |
566 instance()->NPP_HandleEvent(&focus_event);*/ | 630 instance()->NPP_HandleEvent(&focus_event);*/ |
567 } | 631 } |
568 | 632 |
569 bool WebPluginDelegateImpl::HandleEvent(NPEvent* event, | 633 bool WebPluginDelegateImpl::HandleEvent(NPEvent* event, |
570 WebCursor* cursor) { | 634 WebCursor* cursor) { |
571 NOTIMPLEMENTED(); | |
572 #if 0 | |
573 DCHECK(windowless_) << "events should only be received in windowless mode"; | |
574 DCHECK(cursor != NULL); | |
575 | |
576 // To ensure that the plugin receives keyboard events we set focus to the | |
577 // dummy window. | |
578 // TODO(iyengar) We need a framework in the renderer to identify which | |
579 // windowless plugin is under the mouse and to handle this. This would | |
580 // also require some changes in RenderWidgetHost to detect this in the | |
581 // WM_MOUSEACTIVATE handler and inform the renderer accordingly. | |
582 HWND prev_focus_window = NULL; | |
583 if (event->event == WM_RBUTTONDOWN) { | |
584 prev_focus_window = ::SetFocus(dummy_window_for_activation_); | |
585 } | |
586 | |
587 if (ShouldTrackEventForModalLoops(event)) { | |
588 // A windowless plugin can enter a modal loop in a NPP_HandleEvent call. | |
589 // For e.g. Flash puts up a context menu when we right click on the | |
590 // windowless plugin area. We detect this by setting up a message filter | |
591 // hook pror to calling NPP_HandleEvent on the plugin and unhook on | |
592 // return from NPP_HandleEvent. If the plugin does enter a modal loop | |
593 // in that context we unhook on receiving the first notification in | |
594 // the message filter hook. | |
595 handle_event_message_filter_hook_ = | |
596 SetWindowsHookEx(WH_MSGFILTER, HandleEventMessageFilterHook, NULL, | |
597 GetCurrentThreadId()); | |
598 } | |
599 | |
600 bool old_task_reentrancy_state = | |
601 MessageLoop::current()->NestableTasksAllowed(); | |
602 | |
603 current_plugin_instance_ = this; | |
604 | |
605 handle_event_depth_++; | |
606 | |
607 bool pop_user_gesture = false; | |
608 | |
609 if (IsUserGestureMessage(event->event)) { | |
610 pop_user_gesture = true; | |
611 instance()->PushPopupsEnabledState(true); | |
612 } | |
613 | |
614 bool ret = instance()->NPP_HandleEvent(event) != 0; | 635 bool ret = instance()->NPP_HandleEvent(event) != 0; |
615 | 636 |
| 637 #if 0 |
616 if (event->event == WM_MOUSEMOVE) { | 638 if (event->event == WM_MOUSEMOVE) { |
617 // Snag a reference to the current cursor ASAP in case the plugin modified | 639 // Snag a reference to the current cursor ASAP in case the plugin modified |
618 // it. There is a nasty race condition here with the multiprocess browser | 640 // it. There is a nasty race condition here with the multiprocess browser |
619 // as someone might be setting the cursor in the main process as well. | 641 // as someone might be setting the cursor in the main process as well. |
620 *cursor = current_windowless_cursor_; | 642 *cursor = current_windowless_cursor_; |
621 } | 643 } |
622 | 644 #endif |
623 if (pop_user_gesture) { | |
624 instance()->PopPopupsEnabledState(); | |
625 } | |
626 | |
627 handle_event_depth_--; | |
628 | |
629 current_plugin_instance_ = NULL; | |
630 | |
631 MessageLoop::current()->SetNestableTasksAllowed(old_task_reentrancy_state); | |
632 | |
633 if (handle_event_message_filter_hook_) { | |
634 UnhookWindowsHookEx(handle_event_message_filter_hook_); | |
635 handle_event_message_filter_hook_ = NULL; | |
636 } | |
637 | |
638 // We could have multiple NPP_HandleEvent calls nested together in case | |
639 // the plugin enters a modal loop. Reset the pump messages event when | |
640 // the outermost NPP_HandleEvent call unwinds. | |
641 if (handle_event_depth_ == 0) { | |
642 ResetEvent(handle_event_pump_messages_event_); | |
643 } | |
644 | |
645 if (event->event == WM_RBUTTONUP && ::IsWindow(prev_focus_window)) { | |
646 ::SetFocus(prev_focus_window); | |
647 } | |
648 | 645 |
649 return ret; | 646 return ret; |
650 #endif | |
651 return 0; | |
652 } | 647 } |
653 | 648 |
654 WebPluginResourceClient* WebPluginDelegateImpl::CreateResourceClient( | 649 WebPluginResourceClient* WebPluginDelegateImpl::CreateResourceClient( |
655 int resource_id, const std::string &url, bool notify_needed, | 650 int resource_id, const std::string &url, bool notify_needed, |
656 intptr_t notify_data, intptr_t existing_stream) { | 651 intptr_t notify_data, intptr_t existing_stream) { |
657 // Stream already exists. This typically happens for range requests | 652 // Stream already exists. This typically happens for range requests |
658 // initiated via NPN_RequestRead. | 653 // initiated via NPN_RequestRead. |
659 if (existing_stream) { | 654 if (existing_stream) { |
660 NPAPI::PluginStream* plugin_stream = | 655 NPAPI::PluginStream* plugin_stream = |
661 reinterpret_cast<NPAPI::PluginStream*>(existing_stream); | 656 reinterpret_cast<NPAPI::PluginStream*>(existing_stream); |
(...skipping 13 matching lines...) Expand all Loading... |
675 return stream; | 670 return stream; |
676 } | 671 } |
677 | 672 |
678 void WebPluginDelegateImpl::URLRequestRouted(const std::string&url, | 673 void WebPluginDelegateImpl::URLRequestRouted(const std::string&url, |
679 bool notify_needed, | 674 bool notify_needed, |
680 intptr_t notify_data) { | 675 intptr_t notify_data) { |
681 if (notify_needed) { | 676 if (notify_needed) { |
682 instance()->SetURLLoadData(GURL(url.c_str()), notify_data); | 677 instance()->SetURLLoadData(GURL(url.c_str()), notify_data); |
683 } | 678 } |
684 } | 679 } |
OLD | NEW |