Index: chrome/browser/renderer_host/render_widget_host_view_mac.mm |
diff --git a/chrome/browser/renderer_host/render_widget_host_view_mac.mm b/chrome/browser/renderer_host/render_widget_host_view_mac.mm |
index 647eba676c6e772100cb3936adf101a5cc3d581a..e4ad82cd310ceb7b702df2044033bc33e8aa63d1 100644 |
--- a/chrome/browser/renderer_host/render_widget_host_view_mac.mm |
+++ b/chrome/browser/renderer_host/render_widget_host_view_mac.mm |
@@ -11,6 +11,7 @@ |
#include "base/command_line.h" |
#include "base/histogram.h" |
#include "base/logging.h" |
+#import "base/scoped_nsautorelease_pool.h" |
#import "base/scoped_nsobject.h" |
#include "base/string_util.h" |
#include "base/sys_string_conversions.h" |
@@ -127,51 +128,152 @@ void DisablePasswordInput() { |
} // namespace |
-// AcceleratedPluginLayer ------------------------------------------------------ |
+// AcceleratedPluginView ------------------------------------------------------ |
-// This subclass of CAOpenGLLayer hosts the output of accelerated plugins on |
+// This subclass of NSView hosts the output of accelerated plugins on |
// the page. |
-@interface AcceleratedPluginLayer : CAOpenGLLayer { |
+@interface AcceleratedPluginView : NSView { |
+ scoped_nsobject<NSOpenGLPixelFormat> glPixelFormat_; |
+ CGLPixelFormatObj cglPixelFormat_; // weak, backed by |glPixelFormat_|. |
+ scoped_nsobject<NSOpenGLContext> glContext_; |
+ CGLContextObj cglContext_; // weak, backed by |glContext_|. |
+ |
+ CVDisplayLinkRef displayLink_; // Owned by us. |
+ |
RenderWidgetHostViewMac* renderWidgetHostView_; // weak |
+ gfx::PluginWindowHandle pluginHandle_; // weak |
+ |
+ // True if the backing IO surface was updated since we last painted. |
+ BOOL surfaceWasSwapped_; |
} |
-- (id)initWithRenderWidgetHostViewMac:(RenderWidgetHostViewMac*)r; |
+- (id)initWithRenderWidgetHostViewMac:(RenderWidgetHostViewMac*)r |
+ pluginHandle:(gfx::PluginWindowHandle)pluginHandle; |
+- (void)drawView; |
+ |
+// This _must_ be atomic, since it's accessed from several threads. |
+@property BOOL surfaceWasSwapped; |
@end |
-@implementation AcceleratedPluginLayer |
-- (id)initWithRenderWidgetHostViewMac:(RenderWidgetHostViewMac*)r { |
- self = [super init]; |
- if (self != nil) { |
+@implementation AcceleratedPluginView : NSView |
+@synthesize surfaceWasSwapped = surfaceWasSwapped_; |
+ |
+- (CVReturn)getFrameForTime:(const CVTimeStamp*)outputTime { |
+ // There is no autorelease pool when this method is called because it will be |
+ // called from a background thread. |
+ base::ScopedNSAutoreleasePool pool; |
+ |
+ if (![self surfaceWasSwapped]) |
+ return kCVReturnSuccess; |
+ |
+ [self drawView]; |
+ [self setSurfaceWasSwapped:NO]; |
+ return kCVReturnSuccess; |
+} |
+ |
+// This is the renderer output callback function |
+static CVReturn MyDisplayLinkCallback( |
stuartmorgan
2010/08/11 16:40:44
Can we give this a better name please?
Ken Russell (switch to Gerrit)
2010/08/14 02:12:11
Will change to "DrawOneAcceleratedPluginCallback".
|
+ CVDisplayLinkRef displayLink, |
+ const CVTimeStamp* now, |
+ const CVTimeStamp* outputTime, |
+ CVOptionFlags flagsIn, |
+ CVOptionFlags* flagsOut, |
+ void* displayLinkContext) { |
+ CVReturn result = |
+ [(AcceleratedPluginView*)displayLinkContext getFrameForTime:outputTime]; |
+ return result; |
+} |
+ |
+- (id)initWithRenderWidgetHostViewMac:(RenderWidgetHostViewMac*)r |
+ pluginHandle:(gfx::PluginWindowHandle)pluginHandle { |
+ if ((self = [super initWithFrame:NSZeroRect])) { |
renderWidgetHostView_ = r; |
+ pluginHandle_ = pluginHandle; |
+ |
+ [self setAutoresizingMask:NSViewMaxXMargin|NSViewMinYMargin]; |
+ |
+ NSOpenGLPixelFormatAttribute attributes[] = |
+ { NSOpenGLPFAAccelerated, NSOpenGLPFADoubleBuffer, 0}; |
+ |
+ glPixelFormat_.reset([[NSOpenGLPixelFormat alloc] |
+ initWithAttributes:attributes]); |
+ glContext_.reset([[NSOpenGLContext alloc] initWithFormat:glPixelFormat_ |
+ shareContext:nil]); |
+ |
+ cglContext_ = (CGLContextObj)[glContext_ CGLContextObj]; |
+ cglPixelFormat_ = (CGLPixelFormatObj)[glPixelFormat_ CGLPixelFormatObj]; |
+ |
+ // Draw at beam vsync. |
+ GLint swapInterval = 1; |
+ [glContext_ setValues:&swapInterval forParameter:NSOpenGLCPSwapInterval]; |
+ |
+ |
+ // Set up a display link to do OpenGL rendering on a background thread. |
+ CVDisplayLinkCreateWithActiveCGDisplays(&displayLink_); |
+ CVDisplayLinkSetOutputCallback(displayLink_, &MyDisplayLinkCallback, self); |
+ CVDisplayLinkSetCurrentCGDisplayFromOpenGLContext( |
+ displayLink_, cglContext_, cglPixelFormat_); |
+ CVDisplayLinkStart(displayLink_); |
} |
return self; |
} |
-- (void)drawInCGLContext:(CGLContextObj)glContext |
- pixelFormat:(CGLPixelFormatObj)pixelFormat |
- forLayerTime:(CFTimeInterval)timeInterval |
- displayTime:(const CVTimeStamp *)timeStamp { |
- renderWidgetHostView_->DrawAcceleratedSurfaceInstances(glContext); |
- [super drawInCGLContext:glContext |
- pixelFormat:pixelFormat |
- forLayerTime:timeInterval |
- displayTime:timeStamp]; |
-} |
- |
-- (void)setFrame:(CGRect)rect { |
- // The frame we get when the superlayer resizes doesn't make sense, so ignore |
- // it and just match the superlayer's size. See the email thread referenced in |
- // ensureAcceleratedPluginLayer for an explanation of why the superlayer |
- // isn't trustworthy. |
- [CATransaction begin]; |
- [CATransaction setValue:[NSNumber numberWithInt:0] |
- forKey:kCATransactionAnimationDuration]; |
- if ([self superlayer]) |
- [super setFrame:[[self superlayer] bounds]]; |
- else |
- [super setFrame:rect]; |
- [CATransaction commit]; |
+- (void)drawView { |
+ // Called on a background thread. Synchronized via the CGL context lock. |
+ CGLLockContext(cglContext_); |
+ |
+ renderWidgetHostView_->DrawAcceleratedSurfaceInstance( |
+ cglContext_, pluginHandle_); |
+ |
+ CGLFlushDrawable(cglContext_); |
+ CGLUnlockContext(cglContext_); |
+} |
+ |
+- (void)drawRect:(NSRect)rect { |
+ [self drawView]; |
+} |
+ |
+- (void)globalFrameDidChange:(NSNotification*)notification { |
+ CGLLockContext(cglContext_); |
+ [glContext_ update]; |
+ |
+ // You would think that -update updates the viewport. You would be wrong. |
+ CGLSetCurrentContext(cglContext_); |
+ NSSize size = [self frame].size; |
+ glViewport(0, 0, size.width, size.height); |
+ |
+ CGLUnlockContext(cglContext_); |
+} |
+ |
+- (void)renewGState { |
+ // Synchronize with window server to avoid flashes or corrupt drawing. |
+ [[self window] disableScreenUpdatesUntilFlush]; |
+ [self globalFrameDidChange:nil]; |
+ [super renewGState]; |
+} |
+ |
+- (void)lockFocus { |
+ [super lockFocus]; |
+ |
+ // If we're using OpenGL, make sure it is connected and that the display link |
+ // is running. |
+ if ([glContext_ view] != self) { |
+ [glContext_ setView:self]; |
+ |
+ [[NSNotificationCenter defaultCenter] |
+ addObserver:self |
+ selector:@selector(globalFrameDidChange:) |
+ name:NSViewGlobalFrameDidChangeNotification |
+ object:self]; |
+ } |
+ [glContext_ makeCurrentContext]; |
+} |
+ |
+- (void)dealloc { |
+ CVDisplayLinkRelease(displayLink_); |
+ [[NSNotificationCenter defaultCenter] removeObserver:self]; |
+ [super dealloc]; |
} |
@end |
@@ -316,12 +418,27 @@ void RenderWidgetHostViewMac::MovePluginWindows( |
webkit_glue::WebPluginGeometry geom = *iter; |
// Ignore bogus moves which claim to move the plugin to (0, 0) |
// with width and height (0, 0) |
- if (geom.window_rect.x() != 0 || |
- geom.window_rect.y() != 0 || |
- geom.window_rect.width() != 0 || |
- geom.window_rect.height() != 0) { |
- plugin_container_manager_.MovePluginContainer(geom); |
+ if (geom.window_rect.x() == 0 && |
+ geom.window_rect.y() == 0 && |
+ geom.window_rect.IsEmpty()) { |
+ continue; |
+ } |
+ |
+ gfx::Rect rect = geom.window_rect; |
+ if (geom.visible) { |
+ rect.set_x(rect.x() + geom.clip_rect.x()); |
+ rect.set_y(rect.y() + geom.clip_rect.y()); |
+ rect.set_width(geom.clip_rect.width()); |
+ rect.set_height(geom.clip_rect.height()); |
} |
+ |
+ PluginViewMap::iterator it = plugin_views_.find(geom.window); |
+ CHECK(plugin_views_.end() != it); |
stuartmorgan
2010/08/11 16:40:44
CHECK? Really? Crashing end-users doesn't seem lik
Ken Russell (switch to Gerrit)
2010/08/14 02:12:11
Will change these to DCHECKs and add if tests for
|
+ NSRect new_rect([cocoa_view_ RectToNSRect:rect]); |
+ [it->second setFrame:new_rect]; |
+ [it->second setNeedsDisplay:YES]; |
+ |
+ plugin_container_manager_.MovePluginContainer(geom); |
} |
} |
} |
@@ -470,8 +587,12 @@ void RenderWidgetHostViewMac::Destroy() { |
if (!is_popup_menu_) { |
// Depth-first destroy all popups. Use ShutdownHost() to enforce |
// deepest-first ordering. |
- for (RenderWidgetHostViewCocoa* subview in [cocoa_view_ subviews]) { |
- [subview renderWidgetHostViewMac]->ShutdownHost(); |
+ for (NSView* subview in [cocoa_view_ subviews]) { |
+ if (![subview isKindOfClass:[RenderWidgetHostViewCocoa class]]) |
+ continue; // Skip plugin views. |
stuartmorgan
2010/08/11 16:40:44
s/plugin/accelerated/
Ken Russell (switch to Gerrit)
2010/08/14 02:12:11
Will fix.
|
+ |
+ [static_cast<RenderWidgetHostViewCocoa*>(subview) |
+ renderWidgetHostViewMac]->ShutdownHost(); |
} |
// We've been told to destroy. |
@@ -615,14 +736,28 @@ void RenderWidgetHostViewMac::KillSelf() { |
gfx::PluginWindowHandle |
RenderWidgetHostViewMac::AllocateFakePluginWindowHandle(bool opaque, |
bool root) { |
- // Make sure we have a layer for the plugin to draw into. |
- [cocoa_view_ ensureAcceleratedPluginLayer]; |
+ // Create an NSView to host the plugin's/compositor's pixels. |
+ gfx::PluginWindowHandle handle = |
+ plugin_container_manager_.AllocateFakePluginWindowHandle(opaque, root); |
+ |
+ scoped_nsobject<NSView> plugin_view( |
+ [[AcceleratedPluginView alloc] initWithRenderWidgetHostViewMac:this |
+ pluginHandle:handle]); |
+ [plugin_view setHidden:YES]; |
+ |
+ [cocoa_view_ addSubview:plugin_view]; |
+ plugin_views_[handle] = plugin_view; |
- return plugin_container_manager_.AllocateFakePluginWindowHandle(opaque, root); |
+ return handle; |
} |
void RenderWidgetHostViewMac::DestroyFakePluginWindowHandle( |
gfx::PluginWindowHandle window) { |
+ PluginViewMap::iterator it = plugin_views_.find(window); |
+ CHECK(plugin_views_.end() != it); |
+ [it->second removeFromSuperview]; |
+ plugin_views_.erase(it); |
+ |
plugin_container_manager_.DestroyFakePluginWindowHandle(window); |
} |
@@ -631,10 +766,26 @@ void RenderWidgetHostViewMac::AcceleratedSurfaceSetIOSurface( |
int32 width, |
int32 height, |
uint64 io_surface_identifier) { |
+ PluginViewMap::iterator it = plugin_views_.find(window); |
+ CHECK(plugin_views_.end() != it); |
+ |
plugin_container_manager_.SetSizeAndIOSurface(window, |
width, |
height, |
io_surface_identifier); |
+ |
+ if (plugin_container_manager_.IsRootContainer(window)) { |
+ // Fake up a WebPluginGeometry for the root window to set the |
+ // container's size; we will never get a notification from the |
+ // browser about the root window, only plugins. |
+ webkit_glue::WebPluginGeometry geom; |
+ gfx::Rect rect(0, 0, width, height); |
+ geom.window = window; |
+ geom.window_rect = rect; |
+ geom.clip_rect = rect; |
+ geom.visible = true; |
+ MovePluginWindows(std::vector<webkit_glue::WebPluginGeometry>(1, geom)); |
+ } |
} |
void RenderWidgetHostViewMac::AcceleratedSurfaceSetTransportDIB( |
@@ -642,6 +793,9 @@ void RenderWidgetHostViewMac::AcceleratedSurfaceSetTransportDIB( |
int32 width, |
int32 height, |
TransportDIB::Handle transport_dib) { |
+ PluginViewMap::iterator it = plugin_views_.find(window); |
+ CHECK(plugin_views_.end() != it); |
stuartmorgan
2010/08/11 16:40:44
Again, why CHECK?
Ken Russell (switch to Gerrit)
2010/08/14 02:12:11
Will change.
|
+ |
plugin_container_manager_.SetSizeAndTransportDIB(window, |
width, |
height, |
@@ -650,32 +804,38 @@ void RenderWidgetHostViewMac::AcceleratedSurfaceSetTransportDIB( |
void RenderWidgetHostViewMac::AcceleratedSurfaceBuffersSwapped( |
gfx::PluginWindowHandle window) { |
- [cocoa_view_ drawAcceleratedPluginLayer]; |
- if (GetRenderWidgetHost()->is_gpu_rendering_active()) { |
- // Additionally dirty the entire region of the view to make AppKit |
- // and Core Animation think that our CALayer needs to repaint |
- // itself. |
- [cocoa_view_ setNeedsDisplayInRect:[cocoa_view_ frame]]; |
- } |
+ PluginViewMap::iterator it = plugin_views_.find(window); |
+ CHECK(plugin_views_.end() != it); |
stuartmorgan
2010/08/11 16:40:44
And here, and below, and in DrawAcceleratedSurface
Ken Russell (switch to Gerrit)
2010/08/14 02:12:11
Will change.
|
+ CHECK([it->second isKindOfClass:[AcceleratedPluginView class]]); |
+ |
+ AcceleratedPluginView* view = static_cast<AcceleratedPluginView*>(it->second); |
+ [view setHidden:NO]; |
+ [view setSurfaceWasSwapped:YES]; |
} |
-void RenderWidgetHostViewMac::DrawAcceleratedSurfaceInstances( |
- CGLContextObj context) { |
+void RenderWidgetHostViewMac::DrawAcceleratedSurfaceInstance( |
+ CGLContextObj context, gfx::PluginWindowHandle plugin_handle) { |
+ // Called on the display link thread. |
+ PluginViewMap::iterator it = plugin_views_.find(plugin_handle); |
+ CHECK(plugin_views_.end() != it); |
+ |
CGLSetCurrentContext(context); |
- gfx::Rect rect = GetWindowRect(); |
+ // TODO(thakis): Pixel or view coordinates? |
+ NSSize size = [it->second frame].size; |
+ |
glMatrixMode(GL_PROJECTION); |
glLoadIdentity(); |
// Note that we place the origin at the upper left corner with +y |
// going down |
- glOrtho(0, rect.width(), rect.height(), 0, -1, 1); |
+ glOrtho(0, size.width, size.height, 0, -1, 1); |
glMatrixMode(GL_MODELVIEW); |
glLoadIdentity(); |
plugin_container_manager_.Draw( |
- context, GetRenderWidgetHost()->is_gpu_rendering_active()); |
+ context, plugin_handle, GetRenderWidgetHost()->is_gpu_rendering_active()); |
} |
-void RenderWidgetHostViewMac::AcceleratedSurfaceContextChanged() { |
+void RenderWidgetHostViewMac::ForceTextureReload() { |
plugin_container_manager_.ForceTextureReload(); |
} |
@@ -925,7 +1085,7 @@ void RenderWidgetHostViewMac::SetTextInputActive(bool active) { |
[self keyEvent:theEvent wasKeyEquivalent:NO]; |
} |
-- (void)keyEvent:(NSEvent *)theEvent wasKeyEquivalent:(BOOL)equiv { |
+- (void)keyEvent:(NSEvent*)theEvent wasKeyEquivalent:(BOOL)equiv { |
DCHECK([theEvent type] != NSKeyDown || |
!equiv == !([theEvent modifierFlags] & NSCommandKeyMask)); |
@@ -1127,8 +1287,12 @@ void RenderWidgetHostViewMac::SetTextInputActive(bool active) { |
// If this view can be the key view, it is not a popup. Therefore, if it has |
// any children, they are popups that need to be canceled. |
if (canBeKeyView_) { |
- for (RenderWidgetHostViewCocoa* subview in [self subviews]) { |
- subview->renderWidgetHostView_->KillSelf(); |
+ for (NSView* subview in [self subviews]) { |
+ if (![subview isKindOfClass:[RenderWidgetHostViewCocoa class]]) |
+ continue; // Skip plugin views. |
+ |
+ [static_cast<RenderWidgetHostViewCocoa*>(subview) |
+ renderWidgetHostViewMac]->KillSelf(); |
} |
} |
} |
@@ -1981,9 +2145,12 @@ extern NSString *NSTextInputReplacementRangeAttributeName; |
NSWindow* newWindow = [self window]; |
// Pointer comparison only, since we don't know if lastWindow_ is still |
// valid. |
- if (newWindow && (newWindow != lastWindow_)) { |
- lastWindow_ = newWindow; |
- renderWidgetHostView_->WindowFrameChanged(); |
+ if (newWindow) { |
+ if (newWindow != lastWindow_) { |
+ lastWindow_ = newWindow; |
+ renderWidgetHostView_->WindowFrameChanged(); |
+ } |
+ renderWidgetHostView_->ForceTextureReload(); |
} |
} |
@@ -2049,48 +2216,6 @@ extern NSString *NSTextInputReplacementRangeAttributeName; |
} |
} |
-- (void)ensureAcceleratedPluginLayer { |
- if (acceleratedPluginLayer_.get()) |
- return; |
- |
- AcceleratedPluginLayer* pluginLayer = [[AcceleratedPluginLayer alloc] |
- initWithRenderWidgetHostViewMac:renderWidgetHostView_.get()]; |
- acceleratedPluginLayer_.reset(pluginLayer); |
- // Make our layer resize to fit the superlayer |
- pluginLayer.autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable; |
- // Make the view layer-backed so that there will be a layer to hang the |
- // |layer| off of. This is not the "right" way to host a sublayer in a view, |
- // but the right way would require making the whole view's drawing system |
- // layer-based (using setLayer:). We don't want to do that (at least not |
- // yet) so instead we override setLayer: and re-bind our plugin layer each |
- // time the view's layer changes. For discussion see: |
- // http://lists.apple.com/archives/Cocoa-dev/2009/Feb/msg01132.html |
- [self setWantsLayer:YES]; |
- [self attachPluginLayer]; |
-} |
- |
-- (void)attachPluginLayer { |
- CALayer* pluginLayer = acceleratedPluginLayer_.get(); |
- if (!pluginLayer) |
- return; |
- |
- CALayer* rootLayer = [self layer]; |
- DCHECK(rootLayer != nil); |
- [pluginLayer setFrame:NSRectToCGRect([self bounds])]; |
- [rootLayer addSublayer:pluginLayer]; |
- renderWidgetHostView_->AcceleratedSurfaceContextChanged(); |
-} |
- |
-- (void)setLayer:(CALayer *)newLayer { |
- CALayer* pluginLayer = acceleratedPluginLayer_.get(); |
- if (!newLayer && [pluginLayer superlayer]) |
- [pluginLayer removeFromSuperlayer]; |
- |
- [super setLayer:newLayer]; |
- if ([self layer]) |
- [self attachPluginLayer]; |
-} |
- |
- (void)drawAcceleratedPluginLayer { |
[acceleratedPluginLayer_.get() setNeedsDisplay]; |
} |