diff options
author | stuartmorgan@chromium.org <stuartmorgan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-04-16 15:02:37 +0000 |
---|---|---|
committer | stuartmorgan@chromium.org <stuartmorgan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-04-16 15:02:37 +0000 |
commit | 7ba3818362289db0081fb869caf53084fca52eaa (patch) | |
tree | 1c3e32713cc537b798d3108810df7f4d5efab9ec | |
parent | 23ce3973a2bead57dde8c741d1c2e90e15d1cf43 (diff) | |
download | chromium_src-7ba3818362289db0081fb869caf53084fca52eaa.zip chromium_src-7ba3818362289db0081fb869caf53084fca52eaa.tar.gz chromium_src-7ba3818362289db0081fb869caf53084fca52eaa.tar.bz2 |
Reorganize the Mac plugin host code
Reorders the Mac WebPluginDelegateImpl code for better findability. The only changes that aren't just moving existing code unchanged are:
- The external drag tracking has been consolidated into a helper class.
- The Windows-specific comment in Print has been replaced with a NOTIMPLEMENTED().
BUG=none
TEST=none; no behavioral change
Review URL: http://codereview.chromium.org/1633014
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@44773 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | webkit/glue/plugins/webplugin_delegate_impl.h | 40 | ||||
-rw-r--r-- | webkit/glue/plugins/webplugin_delegate_impl_mac.mm | 668 |
2 files changed, 372 insertions, 336 deletions
diff --git a/webkit/glue/plugins/webplugin_delegate_impl.h b/webkit/glue/plugins/webplugin_delegate_impl.h index 7b5f970..f57755e 100644 --- a/webkit/glue/plugins/webplugin_delegate_impl.h +++ b/webkit/glue/plugins/webplugin_delegate_impl.h @@ -39,10 +39,10 @@ class WebMouseEvent; } #if defined(OS_MACOSX) +class ExternalDragTracker; #ifndef NP_NO_QUICKDRAW class QuickDrawDrawingManager; #endif -class CoreAnimationRedrawTimerSource; #ifdef __OBJC__ @class CALayer; @class CARenderer; @@ -314,21 +314,6 @@ class WebPluginDelegateImpl : public webkit_glue::WebPluginDelegate { gfx::PluginWindowHandle parent_; NPWindow window_; -#if defined(OS_MACOSX) - CGContextRef buffer_context_; // Weak ref. -#ifndef NP_NO_CARBON - NP_CGContext np_cg_context_; -#endif -#ifndef NP_NO_QUICKDRAW - scoped_ptr<QuickDrawDrawingManager> qd_manager_; - NP_Port qd_port_; - base::TimeTicks fast_path_enable_tick_; -#endif - CALayer* layer_; // Used for CA drawing mode. Weak, retained by plug-in. - AcceleratedSurface* surface_; - CARenderer* renderer_; // Renders layer_ to surface_. - scoped_ptr<base::RepeatingTimer<WebPluginDelegateImpl> > redraw_timer_; -#endif gfx::Rect window_rect_; gfx::Rect clip_rect_; int quirks_; @@ -413,16 +398,28 @@ class WebPluginDelegateImpl : public webkit_glue::WebPluginDelegate { void UpdateIdleEventRate(); #endif // !NP_NO_CARBON + CGContextRef buffer_context_; // Weak ref. + +#ifndef NP_NO_CARBON + NP_CGContext np_cg_context_; +#endif +#ifndef NP_NO_QUICKDRAW + NP_Port qd_port_; + scoped_ptr<QuickDrawDrawingManager> qd_manager_; + base::TimeTicks fast_path_enable_tick_; +#endif + + CALayer* layer_; // Used for CA drawing mode. Weak, retained by plug-in. + AcceleratedSurface* surface_; + CARenderer* renderer_; // Renders layer_ to surface_. + scoped_ptr<base::RepeatingTimer<WebPluginDelegateImpl> > redraw_timer_; + // The upper-left corner of the web content area in screen coordinates, // relative to an upper-left (0,0). gfx::Point content_area_origin_; // True if the plugin thinks it has keyboard focus bool have_focus_; - // If non-zero, we are in the middle of a drag that started outside the - // plugin, and this corresponds to the WebInputEvent modifier flags for any - // buttons that were down when the mouse entered and are still down now. - int external_drag_buttons_; // A function to call when we want to accept keyboard focus void (*focus_notifier_)(WebPluginDelegateImpl* notifier); @@ -430,7 +427,10 @@ class WebPluginDelegateImpl : public webkit_glue::WebPluginDelegate { bool initial_window_focus_; bool container_is_visible_; bool have_called_set_window_; + gfx::Rect cached_clip_rect_; + + scoped_ptr<ExternalDragTracker> external_drag_tracker_; #endif // OS_MACOSX // Called by the message filter hook when the plugin enters a modal loop. diff --git a/webkit/glue/plugins/webplugin_delegate_impl_mac.mm b/webkit/glue/plugins/webplugin_delegate_impl_mac.mm index 9227242..09ee22b 100644 --- a/webkit/glue/plugins/webplugin_delegate_impl_mac.mm +++ b/webkit/glue/plugins/webplugin_delegate_impl_mac.mm @@ -166,7 +166,87 @@ class CarbonIdleEventSource { } // namespace +// Helper to build and maintain a model of a drag entering the plugin but not +// starting there. See explanation in PlatformHandleInputEvent. +class ExternalDragTracker { + public: + ExternalDragTracker() : pressed_buttons_(0) {} + + // Returns true if an external drag is in progress. + bool IsDragInProgress() { return pressed_buttons_ != 0; }; + + // Returns true if the given event appears to be related to an external drag. + bool EventIsRelatedToDrag(const WebInputEvent& event); + + // Updates the tracking of whether an external drag is in progress--and if + // so what buttons it involves--based on the given event. + void UpdateDragStateFromEvent(const WebInputEvent& event); + + private: + // Returns the mask for just the button state in a WebInputEvent's modifiers. + static int WebEventButtonModifierMask(); + + // The WebInputEvent modifier flags for any buttons that were down when an + // external drag entered the plugin, and which and are still down now. + int pressed_buttons_; + + DISALLOW_COPY_AND_ASSIGN(ExternalDragTracker); +}; + +void ExternalDragTracker::UpdateDragStateFromEvent(const WebInputEvent& event) { + switch (event.type) { + case WebInputEvent::MouseEnter: + pressed_buttons_ = event.modifiers & WebEventButtonModifierMask(); + break; + case WebInputEvent::MouseUp: { + const WebMouseEvent* mouse_event = + static_cast<const WebMouseEvent*>(&event); + if (mouse_event->button == WebMouseEvent::ButtonLeft) + pressed_buttons_ &= ~WebInputEvent::LeftButtonDown; + if (mouse_event->button == WebMouseEvent::ButtonMiddle) + pressed_buttons_ &= ~WebInputEvent::MiddleButtonDown; + if (mouse_event->button == WebMouseEvent::ButtonRight) + pressed_buttons_ &= ~WebInputEvent::RightButtonDown; + break; + } + default: + break; + } +} + +bool ExternalDragTracker::EventIsRelatedToDrag(const WebInputEvent& event) { + const WebMouseEvent* mouse_event = static_cast<const WebMouseEvent*>(&event); + switch (event.type) { + case WebInputEvent::MouseUp: + // We only care about release of buttons that were part of the drag. + return ((mouse_event->button == WebMouseEvent::ButtonLeft && + (pressed_buttons_ & WebInputEvent::LeftButtonDown)) || + (mouse_event->button == WebMouseEvent::ButtonMiddle && + (pressed_buttons_ & WebInputEvent::MiddleButtonDown)) || + (mouse_event->button == WebMouseEvent::ButtonRight && + (pressed_buttons_ & WebInputEvent::RightButtonDown))); + case WebInputEvent::MouseEnter: + return (event.modifiers & WebEventButtonModifierMask()) != 0; + case WebInputEvent::MouseLeave: + case WebInputEvent::MouseMove: { + int event_buttons = (event.modifiers & WebEventButtonModifierMask()); + return (pressed_buttons_ && + pressed_buttons_ == event_buttons); + } + default: + return false; + } + return false; +} + +int ExternalDragTracker::WebEventButtonModifierMask() { + return WebInputEvent::LeftButtonDown | + WebInputEvent::RightButtonDown | + WebInputEvent::MiddleButtonDown; +} + #pragma mark - +#pragma mark Core WebPluginDelegate implementation WebPluginDelegateImpl::WebPluginDelegateImpl( gfx::PluginWindowHandle containing_view, @@ -177,18 +257,18 @@ WebPluginDelegateImpl::WebPluginDelegateImpl( plugin_(NULL), instance_(instance), parent_(containing_view), + quirks_(0), buffer_context_(NULL), layer_(nil), surface_(NULL), renderer_(nil), - quirks_(0), have_focus_(false), - external_drag_buttons_(0), focus_notifier_(NULL), containing_window_has_focus_(false), initial_window_focus_(false), container_is_visible_(false), have_called_set_window_(false), + external_drag_tracker_(new ExternalDragTracker()), handle_event_depth_(0) { memset(&window_, 0, sizeof(window_)); #ifndef NP_NO_CARBON @@ -395,36 +475,183 @@ void WebPluginDelegateImpl::Paint(CGContextRef context, const gfx::Rect& rect) { } void WebPluginDelegateImpl::Print(CGContextRef context) { - // Disabling the call to NPP_Print as it causes a crash in - // flash in some cases. In any case this does not work as expected - // as the EMF meta file dc passed in needs to be created with the - // the plugin window dc as its sibling dc and the window rect - // in .01 mm units. -} - -void WebPluginDelegateImpl::InstallMissingPlugin() { NOTIMPLEMENTED(); } -bool WebPluginDelegateImpl::WindowedCreatePlugin() { - NOTREACHED(); - return false; +void WebPluginDelegateImpl::SetFocus() { + if (focus_notifier_) + focus_notifier_(this); + else + FocusChanged(true); } -void WebPluginDelegateImpl::WindowedDestroyWindow() { - NOTREACHED(); -} +bool WebPluginDelegateImpl::PlatformHandleInputEvent( + const WebInputEvent& event, WebCursorInfo* cursor_info) { + DCHECK(cursor_info != NULL); -bool WebPluginDelegateImpl::WindowedReposition(const gfx::Rect& window_rect, - const gfx::Rect& clip_rect) { - NOTREACHED(); - return false; + // If we get an event before we've set up the plugin, bail. + if (!have_called_set_window_) + return false; +#ifndef NP_NO_CARBON + if (instance()->event_model() == NPEventModelCarbon && + !np_cg_context_.context) { + return false; + } +#endif + + if (WebInputEvent::isMouseEventType(event.type) || + event.type == WebInputEvent::MouseWheel) { + // Ideally we would compute the content origin from the web event using the + // code below as a safety net for missed content area location changes. + // Because of <http://crbug.com/9996>, however, only globalX/Y are right if + // the page has been zoomed, so for now the coordinates we get aren't + // trustworthy enough to use for corrections. +#if PLUGIN_SCALING_FIXED + // Check our plugin location before we send the event to the plugin, just + // in case we somehow missed a plugin frame change. + const WebMouseEvent* mouse_event = + static_cast<const WebMouseEvent*>(&event); + gfx::Point content_origin( + mouse_event->globalX - mouse_event->x - window_rect_.x(), + mouse_event->globalY - mouse_event->y - window_rect_.y()); + if (content_origin.x() != content_area_origin_.x() || + content_origin.y() != content_area_origin_.y()) { + DLOG(WARNING) << "Stale plugin content area location: " + << content_area_origin_ << " instead of " + << content_origin; + SetContentAreaOrigin(content_origin); + } +#endif + + current_windowless_cursor_.GetCursorInfo(cursor_info); + } + +#ifndef NP_NO_CARBON + if (instance()->event_model() == NPEventModelCarbon) { +#ifndef NP_NO_QUICKDRAW + if (instance()->drawing_model() == NPDrawingModelQuickDraw) { + if (quirks_ & PLUGIN_QUIRK_ALLOW_FASTER_QUICKDRAW_PATH) { + // Mouse event handling doesn't work correctly in the fast path mode, + // so any time we get a mouse event turn the fast path off, but set a + // time to switch it on again (we don't rely just on MouseLeave because + // we don't want poor performance in the case of clicking the play + // button and then leaving the mouse there). + // This isn't perfect (specifically, click-and-hold doesn't seem to work + // if the fast path is on), but the slight regression is worthwhile + // for the improved framerates. + if (WebInputEvent::isMouseEventType(event.type)) { + if (event.type == WebInputEvent::MouseLeave) { + SetQuickDrawFastPathEnabled(true); + } else { + SetQuickDrawFastPathEnabled(false); + } + // Make sure the plugin wasn't destroyed during the switch. + if (!instance()) + return false; + } + } + + qd_manager_->MakePortCurrent(); + } +#endif + + if (event.type == WebInputEvent::MouseMove) { + return true; // The recurring FireIdleEvent will send null events. + } + } +#endif + + // if we do not currently have focus and this is a mouseDown, trigger a + // notification that we are taking the keyboard focus. We can't just key + // off of incoming calls to SetFocus, since WebKit may already think we + // have it if we were the most recently focused element on our parent tab. + if (event.type == WebInputEvent::MouseDown && !have_focus_) { + SetFocus(); + // Make sure that the plugin is still there after handling the focus event. + if (!instance()) + return false; + } + + ScopedActiveDelegate active_delegate(this); + + // Create the plugin event structure. + NPEventModel event_model = instance()->event_model(); + scoped_ptr<PluginWebEventConverter> event_converter( + PluginWebEventConverterFactory::CreateConverterForModel(event_model)); + if (!(event_converter.get() && event_converter->InitWithEvent(event))) { + // Silently consume any keyboard event types that we don't handle, so that + // they don't fall through to the page. + if (WebInputEvent::isKeyboardEventType(event.type)) + return true; + return false; + } + void* plugin_event = event_converter->plugin_event(); + + if (instance()->event_model() == NPEventModelCocoa) { + // We recieve events related to drags starting outside the plugin, but the + // NPAPI Cocoa event model spec says plugins shouldn't receive them, so + // filter them out. + // If we add a page capture mode at the WebKit layer (like the plugin + // capture mode that handles drags starting inside) this can be removed. + bool drag_related = external_drag_tracker_->EventIsRelatedToDrag(event); + external_drag_tracker_->UpdateDragStateFromEvent(event); + if (drag_related) { + if (event.type == WebInputEvent::MouseUp && + !external_drag_tracker_->IsDragInProgress()) { + // When an external drag ends, we need to synthesize a MouseEntered. + NPCocoaEvent enter_event = *(static_cast<NPCocoaEvent*>(plugin_event)); + enter_event.type = NPCocoaEventMouseEntered; + NPAPI::ScopedCurrentPluginEvent event_scope(instance(), &enter_event); + instance()->NPP_HandleEvent(&enter_event); + } + return false; + } + } + +#ifndef PLUGIN_SCALING_FIXED + // Because of <http://crbug.com/9996>, the non-global coordinates we get for + // zoomed pages are wrong. As a temporary hack around that bug, override the + // coordinates we are given with ones computed based on our knowledge of where + // the plugin is on screen. We only need to do this for Cocoa, since Carbon + // only uses the global coordinates. + if (instance()->event_model() == NPEventModelCocoa && + (WebInputEvent::isMouseEventType(event.type) || + event.type == WebInputEvent::MouseWheel)) { + const WebMouseEvent* mouse_event = + static_cast<const WebMouseEvent*>(&event); + NPCocoaEvent* cocoa_event = static_cast<NPCocoaEvent*>(plugin_event); + cocoa_event->data.mouse.pluginX = + mouse_event->globalX - content_area_origin_.x() - window_rect_.x(); + cocoa_event->data.mouse.pluginY = + mouse_event->globalY - content_area_origin_.y() - window_rect_.y(); + } +#endif + + // Send the plugin the event. + scoped_ptr<NPAPI::ScopedCurrentPluginEvent> event_scope(NULL); + if (instance()->event_model() == NPEventModelCocoa) { + event_scope.reset(new NPAPI::ScopedCurrentPluginEvent( + instance(), static_cast<NPCocoaEvent*>(plugin_event))); + } + bool handled = instance()->NPP_HandleEvent(plugin_event) != 0; + + if (WebInputEvent::isMouseEventType(event.type)) { + // Plugins are not good about giving accurate information about whether or + // not they handled events, and other browsers on the Mac generally ignore + // the return value. We may need to expand this to other input types, but + // we'll need to be careful about things like Command-keys. + handled = true; + } + + return handled; } -void WebPluginDelegateImpl::WindowedSetWindow() { - NOTREACHED(); +void WebPluginDelegateImpl::InstallMissingPlugin() { + NOTIMPLEMENTED(); } +#pragma mark - + void WebPluginDelegateImpl::WindowlessUpdateGeometry( const gfx::Rect& window_rect, const gfx::Rect& clip_rect) { @@ -458,21 +685,6 @@ void WebPluginDelegateImpl::WindowlessUpdateGeometry( WindowlessSetWindow(); } -void WebPluginDelegateImpl::DrawLayerInSurface() { - surface_->MakeCurrent(); - - surface_->Clear(window_rect_); - - [renderer_ beginFrameAtTime:CACurrentMediaTime() timeStamp:NULL]; - CGRect layerRect = [layer_ bounds]; - [renderer_ addUpdateRect:layerRect]; - [renderer_ render]; - [renderer_ endFrame]; - - surface_->SwapBuffers(); - plugin_->AcceleratedFrameBuffersDidSwap(windowed_handle()); -} - void WebPluginDelegateImpl::WindowlessPaint(gfx::NativeDrawingContext context, const gfx::Rect& damage_rect) { // If we get a paint event before we are completely set up (e.g., a nested @@ -564,6 +776,30 @@ void WebPluginDelegateImpl::WindowlessSetWindow() { DCHECK(err == NPERR_NO_ERROR); } +#pragma mark - + +bool WebPluginDelegateImpl::WindowedCreatePlugin() { + NOTREACHED(); + return false; +} + +void WebPluginDelegateImpl::WindowedDestroyWindow() { + NOTREACHED(); +} + +bool WebPluginDelegateImpl::WindowedReposition(const gfx::Rect& window_rect, + const gfx::Rect& clip_rect) { + NOTREACHED(); + return false; +} + +void WebPluginDelegateImpl::WindowedSetWindow() { + NOTREACHED(); +} + +#pragma mark - +#pragma mark Mac Extensions + WebPluginDelegateImpl* WebPluginDelegateImpl::GetActiveDelegate() { return g_active_delegate; } @@ -607,13 +843,6 @@ void WebPluginDelegateImpl::FocusChanged(bool has_focus) { } } -void WebPluginDelegateImpl::SetFocus() { - if (focus_notifier_) - focus_notifier_(this); - else - FocusChanged(true); -} - void WebPluginDelegateImpl::SetWindowHasFocus(bool has_focus) { // If we get a window focus event before calling SetWindow, just remember the // states (WindowlessSetWindow will then send it on the first call). @@ -696,28 +925,6 @@ void WebPluginDelegateImpl::SetContainerVisibility(bool is_visible) { instance()->webplugin()->InvalidateRect(gfx::Rect()); } -// Update the size of the IOSurface to match the current size of the plug-in, -// then tell the browser host view so it can adjust its bookkeeping and CALayer -// appropriately. -void WebPluginDelegateImpl::UpdateAcceleratedSurface() { - // Will only have a window handle when using the CoreAnimation drawing model. - if (!windowed_handle() || - instance()->drawing_model() != NPDrawingModelCoreAnimation) - return; - - [layer_ setFrame:CGRectMake(0, 0, - window_rect_.width(), window_rect_.height())]; - [renderer_ setBounds:[layer_ bounds]]; - - uint64 io_surface_id = surface_->SetSurfaceSize(window_rect_.size()); - if (io_surface_id) { - plugin_->SetAcceleratedSurface(windowed_handle(), - window_rect_.width(), - window_rect_.height(), - io_surface_id); - } -} - void WebPluginDelegateImpl::WindowFrameChanged(gfx::Rect window_frame, gfx::Rect view_frame) { instance()->set_window_frame(window_frame); @@ -736,6 +943,9 @@ void WebPluginDelegateImpl::SetNSCursor(NSCursor* cursor) { current_windowless_cursor_.InitFromNSCursor(cursor); } +#pragma mark - +#pragma mark Internal Tracking + void WebPluginDelegateImpl::SetPluginRect(const gfx::Rect& rect) { bool plugin_size_changed = rect.width() != window_rect_.width() || rect.height() != window_rect_.height(); @@ -779,6 +989,49 @@ void WebPluginDelegateImpl::PluginVisibilityChanged() { } } +#pragma mark - +#pragma mark Core Animation Support + +void WebPluginDelegateImpl::DrawLayerInSurface() { + surface_->MakeCurrent(); + + surface_->Clear(window_rect_); + + [renderer_ beginFrameAtTime:CACurrentMediaTime() timeStamp:NULL]; + CGRect layerRect = [layer_ bounds]; + [renderer_ addUpdateRect:layerRect]; + [renderer_ render]; + [renderer_ endFrame]; + + surface_->SwapBuffers(); + plugin_->AcceleratedFrameBuffersDidSwap(windowed_handle()); +} + +// Update the size of the IOSurface to match the current size of the plug-in, +// then tell the browser host view so it can adjust its bookkeeping and CALayer +// appropriately. +void WebPluginDelegateImpl::UpdateAcceleratedSurface() { + // Will only have a window handle when using the CoreAnimation drawing model. + if (!windowed_handle() || + instance()->drawing_model() != NPDrawingModelCoreAnimation) + return; + + [layer_ setFrame:CGRectMake(0, 0, + window_rect_.width(), window_rect_.height())]; + [renderer_ setBounds:[layer_ bounds]]; + + uint64 io_surface_id = surface_->SetSurfaceSize(window_rect_.size()); + if (io_surface_id) { + plugin_->SetAcceleratedSurface(windowed_handle(), + window_rect_.width(), + window_rect_.height(), + io_surface_id); + } +} + +#pragma mark - +#pragma mark Carbon Event support + #ifndef NP_NO_CARBON void WebPluginDelegateImpl::UpdateDummyWindowBounds( const gfx::Point& plugin_origin) { @@ -805,260 +1058,12 @@ void WebPluginDelegateImpl::UpdateDummyWindowBounds( SetWindowBounds(window, kWindowContentRgn, &new_bounds); } -#ifndef NP_NO_QUICKDRAW -void WebPluginDelegateImpl::SetQuickDrawFastPathEnabled(bool enabled) { - if (!enabled) { - // Wait a couple of seconds, then turn the fast path back on. If we're - // turning it off for event handling, that ensures that the common case of - // move-mouse-then-click works (as well as making it likely that a second - // click attempt will work if the first one fails). If we're turning it - // off to force a new baseline image, this leaves plenty of time for the - // plugin to draw. - fast_path_enable_tick_ = base::TimeTicks::Now() + - base::TimeDelta::FromSeconds(2); - } - - if (enabled == qd_manager_->IsFastPathEnabled()) - return; - if (enabled && clip_rect_.IsEmpty()) { - // Don't switch to the fast path while the plugin is completely clipped; - // we can only switch when the window has an up-to-date image for us to - // scrape. We'll automatically switch after we become visible again. - return; - } - - qd_manager_->SetFastPathEnabled(enabled); - qd_port_.port = qd_manager_->port(); - WindowlessSetWindow(); - // Send a paint event so that the new buffer gets updated immediately. - WindowlessPaint(buffer_context_, clip_rect_); -} -#endif // !NP_NO_QUICKDRAW - void WebPluginDelegateImpl::UpdateIdleEventRate() { bool plugin_visible = container_is_visible_ && !clip_rect_.IsEmpty(); CarbonIdleEventSource::SharedInstance()->RegisterDelegate(this, plugin_visible); } -#endif // !NP_NO_CARBON - -// Returns the mask for just the button state in a WebInputEvent's modifiers. -static int WebEventButtonModifierMask() { - return WebInputEvent::LeftButtonDown | - WebInputEvent::RightButtonDown | - WebInputEvent::MiddleButtonDown; -} - -// Returns a new drag button state from applying |event| to the previous state. -static int UpdatedDragStateFromEvent(int drag_buttons, - const WebInputEvent& event) { - switch (event.type) { - case WebInputEvent::MouseEnter: - return event.modifiers & WebEventButtonModifierMask(); - case WebInputEvent::MouseUp: { - const WebMouseEvent* mouse_event = - static_cast<const WebMouseEvent*>(&event); - int new_buttons = drag_buttons; - if (mouse_event->button == WebMouseEvent::ButtonLeft) - new_buttons &= ~WebInputEvent::LeftButtonDown; - if (mouse_event->button == WebMouseEvent::ButtonMiddle) - new_buttons &= ~WebInputEvent::MiddleButtonDown; - if (mouse_event->button == WebMouseEvent::ButtonRight) - new_buttons &= ~WebInputEvent::RightButtonDown; - return new_buttons; - } - default: - return drag_buttons; - } -} - -// Returns true if this is an event that looks like part of a drag with the -// given button state. -static bool EventIsRelatedToDrag(const WebInputEvent& event, int drag_buttons) { - const WebMouseEvent* mouse_event = static_cast<const WebMouseEvent*>(&event); - switch (event.type) { - case WebInputEvent::MouseUp: - // We only care about release of buttons that were part of the drag. - return ((mouse_event->button == WebMouseEvent::ButtonLeft && - (drag_buttons & WebInputEvent::LeftButtonDown)) || - (mouse_event->button == WebMouseEvent::ButtonMiddle && - (drag_buttons & WebInputEvent::MiddleButtonDown)) || - (mouse_event->button == WebMouseEvent::ButtonRight && - (drag_buttons & WebInputEvent::RightButtonDown))); - case WebInputEvent::MouseEnter: - return (event.modifiers & WebEventButtonModifierMask()) != 0; - case WebInputEvent::MouseLeave: - case WebInputEvent::MouseMove: - return (drag_buttons && - drag_buttons == (event.modifiers & WebEventButtonModifierMask())); - default: - return false; - } - return false; -} - -bool WebPluginDelegateImpl::PlatformHandleInputEvent( - const WebInputEvent& event, WebCursorInfo* cursor_info) { - DCHECK(cursor_info != NULL); - - // If we get an event before we've set up the plugin, bail. - if (!have_called_set_window_) - return false; -#ifndef NP_NO_CARBON - if (instance()->event_model() == NPEventModelCarbon && - !np_cg_context_.context) { - return false; - } -#endif - - if (WebInputEvent::isMouseEventType(event.type) || - event.type == WebInputEvent::MouseWheel) { - // Ideally we would compute the content origin from the web event using the - // code below as a safety net for missed content area location changes. - // Because of <http://crbug.com/9996>, however, only globalX/Y are right if - // the page has been zoomed, so for now the coordinates we get aren't - // trustworthy enough to use for corrections. -#if PLUGIN_SCALING_FIXED - // Check our plugin location before we send the event to the plugin, just - // in case we somehow missed a plugin frame change. - const WebMouseEvent* mouse_event = - static_cast<const WebMouseEvent*>(&event); - gfx::Point content_origin( - mouse_event->globalX - mouse_event->x - window_rect_.x(), - mouse_event->globalY - mouse_event->y - window_rect_.y()); - if (content_origin.x() != content_area_origin_.x() || - content_origin.y() != content_area_origin_.y()) { - DLOG(WARNING) << "Stale plugin content area location: " - << content_area_origin_ << " instead of " - << content_origin; - SetContentAreaOrigin(content_origin); - } -#endif - - current_windowless_cursor_.GetCursorInfo(cursor_info); - } - -#ifndef NP_NO_CARBON - if (instance()->event_model() == NPEventModelCarbon) { -#ifndef NP_NO_QUICKDRAW - if (instance()->drawing_model() == NPDrawingModelQuickDraw) { - if (quirks_ & PLUGIN_QUIRK_ALLOW_FASTER_QUICKDRAW_PATH) { - // Mouse event handling doesn't work correctly in the fast path mode, - // so any time we get a mouse event turn the fast path off, but set a - // time to switch it on again (we don't rely just on MouseLeave because - // we don't want poor performance in the case of clicking the play - // button and then leaving the mouse there). - // This isn't perfect (specifically, click-and-hold doesn't seem to work - // if the fast path is on), but the slight regression is worthwhile - // for the improved framerates. - if (WebInputEvent::isMouseEventType(event.type)) { - if (event.type == WebInputEvent::MouseLeave) { - SetQuickDrawFastPathEnabled(true); - } else { - SetQuickDrawFastPathEnabled(false); - } - // Make sure the plugin wasn't destroyed during the switch. - if (!instance()) - return false; - } - } - - qd_manager_->MakePortCurrent(); - } -#endif - - if (event.type == WebInputEvent::MouseMove) { - return true; // The recurring FireIdleEvent will send null events. - } - } -#endif - - // if we do not currently have focus and this is a mouseDown, trigger a - // notification that we are taking the keyboard focus. We can't just key - // off of incoming calls to SetFocus, since WebKit may already think we - // have it if we were the most recently focused element on our parent tab. - if (event.type == WebInputEvent::MouseDown && !have_focus_) { - SetFocus(); - // Make sure that the plugin is still there after handling the focus event. - if (!instance()) - return false; - } - - ScopedActiveDelegate active_delegate(this); - - // Create the plugin event structure. - NPEventModel event_model = instance()->event_model(); - scoped_ptr<PluginWebEventConverter> event_converter( - PluginWebEventConverterFactory::CreateConverterForModel(event_model)); - if (!(event_converter.get() && event_converter->InitWithEvent(event))) { - // Silently consume any keyboard event types that we don't handle, so that - // they don't fall through to the page. - if (WebInputEvent::isKeyboardEventType(event.type)) - return true; - return false; - } - void* plugin_event = event_converter->plugin_event(); - - if (instance()->event_model() == NPEventModelCocoa) { - // We recieve events related to drags starting outside the plugin, but the - // NPAPI Cocoa event model spec says plugins shouldn't receive them, so - // filter them out. - // If we add a page capture mode at the WebKit layer (like the plugin - // capture mode that handles drags starting inside) this can be removed. - bool drag_related = EventIsRelatedToDrag(event, external_drag_buttons_); - external_drag_buttons_ = UpdatedDragStateFromEvent(external_drag_buttons_, - event); - if (drag_related) { - if (event.type == WebInputEvent::MouseUp && !external_drag_buttons_) { - // When an external drag ends, we need to synthesize a MouseEntered. - NPCocoaEvent enter_event = *(static_cast<NPCocoaEvent*>(plugin_event)); - enter_event.type = NPCocoaEventMouseEntered; - NPAPI::ScopedCurrentPluginEvent event_scope(instance(), &enter_event); - instance()->NPP_HandleEvent(&enter_event); - } - return false; - } - } - -#ifndef PLUGIN_SCALING_FIXED - // Because of <http://crbug.com/9996>, the non-global coordinates we get for - // zoomed pages are wrong. As a temporary hack around that bug, override the - // coordinates we are given with ones computed based on our knowledge of where - // the plugin is on screen. We only need to do this for Cocoa, since Carbon - // only uses the global coordinates. - if (instance()->event_model() == NPEventModelCocoa && - (WebInputEvent::isMouseEventType(event.type) || - event.type == WebInputEvent::MouseWheel)) { - const WebMouseEvent* mouse_event = - static_cast<const WebMouseEvent*>(&event); - NPCocoaEvent* cocoa_event = static_cast<NPCocoaEvent*>(plugin_event); - cocoa_event->data.mouse.pluginX = - mouse_event->globalX - content_area_origin_.x() - window_rect_.x(); - cocoa_event->data.mouse.pluginY = - mouse_event->globalY - content_area_origin_.y() - window_rect_.y(); - } -#endif - - // Send the plugin the event. - scoped_ptr<NPAPI::ScopedCurrentPluginEvent> event_scope(NULL); - if (instance()->event_model() == NPEventModelCocoa) { - event_scope.reset(new NPAPI::ScopedCurrentPluginEvent( - instance(), static_cast<NPCocoaEvent*>(plugin_event))); - } - bool handled = instance()->NPP_HandleEvent(plugin_event) != 0; - - if (WebInputEvent::isMouseEventType(event.type)) { - // Plugins are not good about giving accurate information about whether or - // not they handled events, and other browsers on the Mac generally ignore - // the return value. We may need to expand this to other input types, but - // we'll need to be careful about things like Command-keys. - handled = true; - } - - return handled; -} -#ifndef NP_NO_CARBON void WebPluginDelegateImpl::FireIdleEvent() { // Avoid a race condition between IO and UI threads during plugin shutdown if (!instance()) @@ -1099,10 +1104,41 @@ void WebPluginDelegateImpl::FireIdleEvent() { #ifndef NP_NO_QUICKDRAW // Quickdraw-based plugins can draw at any time, so tell the renderer to // repaint. - // TODO: only do this if the contents of the offscreen window/buffer have - // changed, so as not to spam the renderer with an unchanging image. if (instance() && instance()->drawing_model() == NPDrawingModelQuickDraw) instance()->webplugin()->Invalidate(); #endif } #endif // !NP_NO_CARBON + +#pragma mark - +#pragma mark QuickDraw Support + +#ifndef NP_NO_QUICKDRAW +void WebPluginDelegateImpl::SetQuickDrawFastPathEnabled(bool enabled) { + if (!enabled) { + // Wait a couple of seconds, then turn the fast path back on. If we're + // turning it off for event handling, that ensures that the common case of + // move-mouse-then-click works (as well as making it likely that a second + // click attempt will work if the first one fails). If we're turning it + // off to force a new baseline image, this leaves plenty of time for the + // plugin to draw. + fast_path_enable_tick_ = base::TimeTicks::Now() + + base::TimeDelta::FromSeconds(2); + } + + if (enabled == qd_manager_->IsFastPathEnabled()) + return; + if (enabled && clip_rect_.IsEmpty()) { + // Don't switch to the fast path while the plugin is completely clipped; + // we can only switch when the window has an up-to-date image for us to + // scrape. We'll automatically switch after we become visible again. + return; + } + + qd_manager_->SetFastPathEnabled(enabled); + qd_port_.port = qd_manager_->port(); + WindowlessSetWindow(); + // Send a paint event so that the new buffer gets updated immediately. + WindowlessPaint(buffer_context_, clip_rect_); +} +#endif // !NP_NO_QUICKDRAW |