diff options
author | stuartmorgan@chromium.org <stuartmorgan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-05 16:09:11 +0000 |
---|---|---|
committer | stuartmorgan@chromium.org <stuartmorgan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-03-05 16:09:11 +0000 |
commit | 33f39d5985daafddf49ad68be2e1c4c03baad06c (patch) | |
tree | 132400ebc1fa5caed7a882f9ef65103b891103c6 | |
parent | 0fb510a055aebb59c6c228f9a8202c6f6d9a39d6 (diff) | |
download | chromium_src-33f39d5985daafddf49ad68be2e1c4c03baad06c.zip chromium_src-33f39d5985daafddf49ad68be2e1c4c03baad06c.tar.gz chromium_src-33f39d5985daafddf49ad68be2e1c4c03baad06c.tar.bz2 |
Mac Plugin context handling cleanup
Fixes a number of things about how the shared buffer context is handled for plugins on the Mac:
- Explicitly send paint events to QuickDraw plugins. This was being done before only because of a missing break in a switch statement, but we do actually want to send them since that's the only way those plugins know when we have invalidated part of their content (e.g., on first paint, or because the window focus changed). Also moves the window-scraping to after the paint event, instead of before, so we get the latest bits (and moves that code to a new method for clarity).
- Remove context save/restore from HandleInputEvent, since CoreGraphics plugins are not allowed to paint outside of handling a paint event.
- Add safety checks to context save/restore in WindowlessPaint and WebPluginProxy's paint, to correctly handle resizes that happen during paint handling (using a new member variable that is independent of event model).
Also renames cg_context_ for clarity.
BUG=none
TEST=QuickDraw plugins (e.g., QuickTime) should no longer have a flicker of painting artifacts when they are first drawn.
Review URL: http://codereview.chromium.org/668113
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@40739 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/plugin/webplugin_proxy.cc | 10 | ||||
-rw-r--r-- | webkit/glue/plugins/webplugin_delegate_impl.h | 8 | ||||
-rw-r--r-- | webkit/glue/plugins/webplugin_delegate_impl_mac.mm | 186 |
3 files changed, 107 insertions, 97 deletions
diff --git a/chrome/plugin/webplugin_proxy.cc b/chrome/plugin/webplugin_proxy.cc index 469d92f..95f6545 100644 --- a/chrome/plugin/webplugin_proxy.cc +++ b/chrome/plugin/webplugin_proxy.cc @@ -395,6 +395,13 @@ void WebPluginProxy::Paint(const gfx::Rect& rect) { windowless_canvas_->restore(); #elif defined(OS_MACOSX) CGContextSaveGState(windowless_context_); + // It is possible for windowless_context_ to change during plugin painting + // (since the plugin can make a synchronous call during paint event handling), + // in which case we don't want to try to restore it later. Not an owning ref + // since owning the ref without owning the shared backing memory doesn't make + // sense, so this should only be used for pointer comparisons. + CGContextRef saved_context_weak = windowless_context_.get(); + if (!background_context_.get()) { CGContextSetFillColorWithColor(windowless_context_, CGColorGetConstantColor(kCGColorBlack)); @@ -411,7 +418,8 @@ void WebPluginProxy::Paint(const gfx::Rect& rect) { } CGContextClipToRect(windowless_context_, rect.ToCGRect()); delegate_->Paint(windowless_context_, rect); - CGContextRestoreGState(windowless_context_); + if (windowless_context_.get() == saved_context_weak) + CGContextRestoreGState(windowless_context_); #endif } diff --git a/webkit/glue/plugins/webplugin_delegate_impl.h b/webkit/glue/plugins/webplugin_delegate_impl.h index 25f9fde..520da4c 100644 --- a/webkit/glue/plugins/webplugin_delegate_impl.h +++ b/webkit/glue/plugins/webplugin_delegate_impl.h @@ -292,8 +292,9 @@ 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 cg_context_; + NP_CGContext np_cg_context_; #endif #ifndef NP_NO_QUICKDRAW NP_Port qd_port_; @@ -353,6 +354,11 @@ class WebPluginDelegateImpl : public webkit_glue::WebPluginDelegate { #ifndef NP_NO_CARBON // Moves our dummy window to match the current screen location of the plugin. void UpdateDummyWindowBounds(const gfx::Point& plugin_origin); + +#ifndef NP_NO_QUICKDRAW + // Scrapes the contents of our dummy window into the given context. + void ScrapeDummyWindowIntoContext(CGContextRef context); +#endif // Adjusts the idle event rate for a Carbon plugin based on its current // visibility. diff --git a/webkit/glue/plugins/webplugin_delegate_impl_mac.mm b/webkit/glue/plugins/webplugin_delegate_impl_mac.mm index ae22f61..fc2bf31 100644 --- a/webkit/glue/plugins/webplugin_delegate_impl_mac.mm +++ b/webkit/glue/plugins/webplugin_delegate_impl_mac.mm @@ -178,6 +178,7 @@ WebPluginDelegateImpl::WebPluginDelegateImpl( plugin_(NULL), instance_(instance), parent_(containing_view), + buffer_context_(NULL), quirks_(0), have_focus_(false), focus_notifier_(NULL), @@ -188,7 +189,7 @@ WebPluginDelegateImpl::WebPluginDelegateImpl( handle_event_depth_(0) { memset(&window_, 0, sizeof(window_)); #ifndef NP_NO_CARBON - memset(&cg_context_, 0, sizeof(cg_context_)); + memset(&np_cg_context_, 0, sizeof(np_cg_context_)); #endif #ifndef NP_NO_QUICKDRAW memset(&qd_port_, 0, sizeof(qd_port_)); @@ -206,9 +207,9 @@ WebPluginDelegateImpl::~WebPluginDelegateImpl() { DestroyInstance(); #ifndef NP_NO_CARBON - if (cg_context_.window) { + if (np_cg_context_.window) { CarbonPluginWindowTracker::SharedInstance()->DestroyDummyWindowForDelegate( - this, reinterpret_cast<WindowRef>(cg_context_.window)); + this, reinterpret_cast<WindowRef>(np_cg_context_.window)); } #endif } @@ -231,12 +232,12 @@ void WebPluginDelegateImpl::PlatformInitialize() { // a non-NULL WindowRef to which it can refer. CarbonPluginWindowTracker* window_tracker = CarbonPluginWindowTracker::SharedInstance(); - cg_context_.window = window_tracker->CreateDummyWindowForDelegate(this); - cg_context_.context = NULL; + np_cg_context_.window = window_tracker->CreateDummyWindowForDelegate(this); + np_cg_context_.context = NULL; UpdateDummyWindowBounds(gfx::Point(0, 0)); #ifndef NP_NO_QUICKDRAW qd_port_.port = - GetWindowPort(reinterpret_cast<WindowRef>(cg_context_.window)); + GetWindowPort(reinterpret_cast<WindowRef>(np_cg_context_.window)); #endif } #endif @@ -251,7 +252,7 @@ void WebPluginDelegateImpl::PlatformInitialize() { case NPDrawingModelCoreGraphics: #ifndef NP_NO_CARBON if (instance()->event_model() == NPEventModelCarbon) - window_.window = &cg_context_; + window_.window = &np_cg_context_; #endif window_.type = NPWindowTypeDrawable; break; @@ -288,22 +289,36 @@ void WebPluginDelegateImpl::PlatformDestroyInstance() { } void WebPluginDelegateImpl::UpdateContext(CGContextRef context) { + buffer_context_ = context; #ifndef NP_NO_CARBON - // Flash on the Mac apparently caches the context from the struct it receives - // in NPP_SetWindow, and continues to use it even when the contents of the - // struct have changed, so we need to call NPP_SetWindow again if the context - // changes. + // Under the Carbon event model, CoreGraphics plugins can cache the context + // they are given in NPP_SetWindow (and at least Flash does), and continue to + // use it even when the contents of the struct have changed, so we need to + // call NPP_SetWindow again if the context changes. + // In the Cocoa event model plugins are only given a context during paint + // events, so we don't have to tell the plugin anything here. if (instance()->event_model() == NPEventModelCarbon && - context != cg_context_.context) { - cg_context_.context = context; + instance()->drawing_model() == NPDrawingModelCoreGraphics && + context != np_cg_context_.context) { + np_cg_context_.context = context; WindowlessSetWindow(true); } #endif } void WebPluginDelegateImpl::Paint(CGContextRef context, const gfx::Rect& rect) { - DCHECK(windowless_); WindowlessPaint(context, rect); + +#ifndef NP_NO_QUICKDRAW + // Paint events are our cue to scrape the dummy window into the real context + // if we are dealing with a QuickDraw plugin. + // Note that we use buffer_context_ rather than the Paint parameter + // because the buffer might have changed during the NPP_HandleEvent call + // in WindowlessPaint. + if (instance()->drawing_model() == NPDrawingModelQuickDraw) { + ScrapeDummyWindowIntoContext(buffer_context_); + } +#endif } void WebPluginDelegateImpl::Print(CGContextRef context) { @@ -366,14 +381,10 @@ void WebPluginDelegateImpl::WindowlessUpdateGeometry( void WebPluginDelegateImpl::WindowlessPaint(gfx::NativeDrawingContext context, const gfx::Rect& damage_rect) { -#ifndef NP_NO_CARBON - if (instance()->event_model() == NPEventModelCarbon) { - // If we somehow get a paint before we've set up the plugin window, bail. - if (!cg_context_.context) - return; - DCHECK(cg_context_.context == context); - } -#endif + // If we somehow get a paint before we've set up the plugin buffer, bail. + if (!buffer_context_) + return; + DCHECK(buffer_context_ == context); static StatsRate plugin_paint("Plugin.Paint"); StatsScope<StatsRate> scope(plugin_paint); @@ -386,56 +397,37 @@ void WebPluginDelegateImpl::WindowlessPaint(gfx::NativeDrawingContext context, ScopedActiveDelegate active_delegate(this); - switch (instance()->drawing_model()) { -#ifndef NP_NO_QUICKDRAW - case NPDrawingModelQuickDraw: { - // Plugins using the QuickDraw drawing model do not restrict their - // drawing to update events the way that CoreGraphics-based plugins - // do. When we are asked to paint, we therefore just copy from the - // plugin's hidden window into our shared memory bitmap context. - CGRect window_bounds = CGRectMake(0, 0, - window_rect_.width(), - window_rect_.height()); - CGWindowID window_id = HIWindowGetCGWindowID( - reinterpret_cast<WindowRef>(cg_context_.window)); - CGContextSaveGState(context); - CGContextTranslateCTM(context, 0, window_rect_.height()); - CGContextScaleCTM(context, 1.0, -1.0); - CGContextCopyWindowCaptureContentsToRect(context, window_bounds, - _CGSDefaultConnection(), - window_id, 0); - CGContextRestoreGState(context); - } -#endif - case NPDrawingModelCoreGraphics: { - CGContextSaveGState(context); - switch (instance()->event_model()) { + CGContextSaveGState(context); + + switch (instance()->event_model()) { #ifndef NP_NO_CARBON - case NPEventModelCarbon: { - NPEvent paint_event = { 0 }; - paint_event.what = updateEvt; - paint_event.message = reinterpret_cast<uint32>(cg_context_.window); - paint_event.when = TickCount(); - instance()->NPP_HandleEvent(&paint_event); - break; - } + case NPEventModelCarbon: { + NPEvent paint_event = { 0 }; + paint_event.what = updateEvt; + paint_event.message = reinterpret_cast<uint32>(np_cg_context_.window); + paint_event.when = TickCount(); + instance()->NPP_HandleEvent(&paint_event); + break; + } #endif - case NPEventModelCocoa: { - NPCocoaEvent paint_event; - memset(&paint_event, 0, sizeof(NPCocoaEvent)); - paint_event.type = NPCocoaEventDrawRect; - paint_event.data.draw.context = context; - paint_event.data.draw.x = paint_rect.x(); - paint_event.data.draw.y = paint_rect.y(); - paint_event.data.draw.width = paint_rect.width(); - paint_event.data.draw.height = paint_rect.height(); - instance()->NPP_HandleEvent(&paint_event); - break; - } - } - CGContextRestoreGState(context); + case NPEventModelCocoa: { + NPCocoaEvent paint_event; + memset(&paint_event, 0, sizeof(NPCocoaEvent)); + paint_event.type = NPCocoaEventDrawRect; + paint_event.data.draw.context = context; + paint_event.data.draw.x = paint_rect.x(); + paint_event.data.draw.y = paint_rect.y(); + paint_event.data.draw.width = paint_rect.width(); + paint_event.data.draw.height = paint_rect.height(); + instance()->NPP_HandleEvent(&paint_event); + break; } } + + // The backing buffer can change during the call to NPP_HandleEvent, in which + // case the old context is (or is about to be) invalid. + if (context == buffer_context_) + CGContextRestoreGState(context); } void WebPluginDelegateImpl::WindowlessSetWindow(bool force_set_window) { @@ -536,7 +528,8 @@ void WebPluginDelegateImpl::SetWindowHasFocus(bool has_focus) { focus_event.what = activateEvt; if (has_focus) focus_event.modifiers |= activeFlag; - focus_event.message = reinterpret_cast<unsigned long>(cg_context_.window); + focus_event.message = + reinterpret_cast<unsigned long>(np_cg_context_.window); focus_event.when = TickCount(); instance()->NPP_HandleEvent(&focus_event); break; @@ -548,6 +541,7 @@ void WebPluginDelegateImpl::SetWindowHasFocus(bool has_focus) { focus_event.type = NPCocoaEventWindowFocusChanged; focus_event.data.focus.hasFocus = has_focus; instance()->NPP_HandleEvent(&focus_event); + break; } } } @@ -629,7 +623,7 @@ void WebPluginDelegateImpl::PluginScreenLocationChanged() { #ifndef NP_NO_CARBON void WebPluginDelegateImpl::UpdateDummyWindowBounds( const gfx::Point& plugin_origin) { - WindowRef window = reinterpret_cast<WindowRef>(cg_context_.window); + WindowRef window = reinterpret_cast<WindowRef>(np_cg_context_.window); Rect current_bounds; GetWindowBounds(window, kWindowContentRgn, ¤t_bounds); @@ -652,6 +646,26 @@ void WebPluginDelegateImpl::UpdateDummyWindowBounds( SetWindowBounds(window, kWindowContentRgn, &new_bounds); } +#ifndef NP_NO_QUICKDRAW +void WebPluginDelegateImpl::ScrapeDummyWindowIntoContext(CGContextRef context) { + if (!context) + return; + + CGRect window_bounds = CGRectMake(0, 0, + window_rect_.width(), + window_rect_.height()); + CGWindowID window_id = HIWindowGetCGWindowID( + reinterpret_cast<WindowRef>(np_cg_context_.window)); + CGContextSaveGState(context); + CGContextTranslateCTM(context, 0, window_rect_.height()); + CGContextScaleCTM(context, 1.0, -1.0); + CGContextCopyWindowCaptureContentsToRect(context, window_bounds, + _CGSDefaultConnection(), + window_id, 0); + CGContextRestoreGState(context); +} +#endif // !NP_NO_QUICKDRAW + void WebPluginDelegateImpl::UpdateIdleEventRate() { bool plugin_visible = container_is_visible_ && !clip_rect_.IsEmpty(); CarbonIdleEventSource::SharedInstance()->RegisterDelegate(this, @@ -931,10 +945,12 @@ bool WebPluginDelegateImpl::PlatformHandleInputEvent( const WebInputEvent& event, WebCursorInfo* cursor_info) { DCHECK(cursor_info != NULL); + // If we somehow 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 && - !cg_context_.context) { - // If we somehow get an event before we've set up the plugin window, bail. + !np_cg_context_.context) { return false; } #endif @@ -974,29 +990,16 @@ bool WebPluginDelegateImpl::PlatformHandleInputEvent( return true; // The recurring OnNull will send null events. } - switch (instance()->drawing_model()) { #ifndef NP_NO_QUICKDRAW - case NPDrawingModelQuickDraw: - SetPort(qd_port_.port); - break; -#endif - case NPDrawingModelCoreGraphics: - CGContextSaveGState(cg_context_.context); - break; + if (instance()->drawing_model() == NPDrawingModelQuickDraw) { + SetPort(qd_port_.port); } +#endif } #endif ScopedActiveDelegate active_delegate(this); -#ifndef NP_NO_CARBON - // cgcontext_.context can change during event handling (because of a geometry - // change triggered by the event); we need to know if that happens so we - // don't keep trying to use the context. It is not an owning ref, so shouldn't - // be used for anything but pointer comparison. - CGContextRef old_context_weak = cg_context_.context; -#endif - // Create the plugin event structure, and send it to the plugin. bool ret = false; switch (instance()->event_model()) { @@ -1031,13 +1034,6 @@ bool WebPluginDelegateImpl::PlatformHandleInputEvent( ret = true; } -#ifndef NP_NO_CARBON - if (instance() && instance()->event_model() == NPEventModelCarbon && - instance()->drawing_model() == NPDrawingModelCoreGraphics && - cg_context_.context == old_context_weak) - CGContextRestoreGState(cg_context_.context); -#endif - return ret; } |