Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(4360)

Unified Diff: chrome/browser/ui/cocoa/tab_contents/tab_contents_controller.mm

Issue 2654413002: Stretching NativeViewHost, and misc tab capture fixes.
Patch Set: Gettin' it all working on ui/cocoa and MacViews too. Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/ui/cocoa/tab_contents/tab_contents_controller.mm
diff --git a/chrome/browser/ui/cocoa/tab_contents/tab_contents_controller.mm b/chrome/browser/ui/cocoa/tab_contents/tab_contents_controller.mm
index b85b75815391ca3da848969b4c4149a6116e30b2..85c0dd2242d2754a05cd1d1e35b462f83a8d43b6 100644
--- a/chrome/browser/ui/cocoa/tab_contents/tab_contents_controller.mm
+++ b/chrome/browser/ui/cocoa/tab_contents/tab_contents_controller.mm
@@ -23,7 +23,10 @@
#include "content/public/browser/web_contents_observer.h"
#include "skia/ext/skia_utils_mac.h"
#include "ui/base/cocoa/animation_utils.h"
-#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/geometry/rect_conversions.h"
+#include "ui/gfx/geometry/rect_f.h"
+#include "ui/gfx/geometry/size.h"
+#include "ui/gfx/geometry/size_f.h"
using content::WebContents;
using content::WebContentsObserver;
@@ -70,9 +73,17 @@ void DidToggleFullscreenModeForTab(bool entered_fullscreen,
@interface TabContentsController (TabContentsContainerViewDelegate)
- (BOOL)contentsInFullscreenCaptureMode;
-// Computes and returns the frame to use for the contents view using the size of
-// |container| as the target size.
-- (NSRect)frameForContentsViewIn:(NSView*)container;
+
+// Computes and returns the frame to use for the scaling view within the
+// TabContentsContainerView.
+- (NSRect)frameForScalingViewIn:(NSView*)container;
+
+// Computes and returns the bounds to use for the scaling view within the
+// TabContentsContainerView. In the normal case, the bounds size will be
+// equivalent to frame size. In 'AutoEmbedFullscreen mode', the bounds size is
+// set to the video capture resolution, which is almost always different from
+// the frame size. See 'AutoEmbedFullscreen mode' in header file comments.
+- (NSRect)boundsForScalingViewIn:(NSView*)container;
// Returns YES if the content view should be resized.
- (BOOL)shouldResizeContentView;
@@ -85,11 +96,17 @@ - (BOOL)isPopup;
// An NSView with special-case handling for when the contents view does not
// expand to fill the entire tab contents area. See 'AutoEmbedFullscreen mode'
// in header file comments.
+//
+// This view creates and manages its own subview, a "scaling" view, which acts
+// as a container for the contents view. It allows the rendering size of the
+// contents view to be something different than its normal on-screen display
+// size.
@interface TabContentsContainerView : NSView {
@private
TabContentsController* delegate_; // weak
}
+- (void)ensureContentsVisible:(NSView*)contentsView;
- (void)updateBackgroundColor;
@end
@@ -98,11 +115,15 @@ @implementation TabContentsContainerView
- (id)initWithDelegate:(TabContentsController*)delegate {
if ((self = [super initWithFrame:NSZeroRect])) {
delegate_ = delegate;
+
ScopedCAActionDisabler disabler;
base::scoped_nsobject<CALayer> layer([[CALayer alloc] init]);
[self setLayer:layer];
[self setWantsLayer:YES];
[self updateBackgroundColor];
+
+ // Create and add the scaling view.
+ [self addSubview:[[NSView alloc] initWithFrame:NSZeroRect]];
}
return self;
}
@@ -113,23 +134,48 @@ - (void)delegateDestroyed {
delegate_ = nil;
}
-// Override auto-resizing logic to query the delegate for the exact frame to
-// use for the contents view.
-// TODO(spqchan): The popup check is a temporary solution to fix the regression
-// issue described in crbug.com/604288. This method doesn't really affect
-// fullscreen if the content is inside a normal browser window, but would
-// cause a flash fullscreen widget to blow up if it's inside a popup.
+// Returns the scaling view, which is the child of TabContentsContainerView.
+- (NSView*)scalingView {
+ return [[self subviews] objectAtIndex:0];
+}
+
+// Return the contents view, which is the child of the scaling view; or nil if
+// there isn't one attached.
+- (NSView*)contentsView {
+ NSArray* const scalingViewSubviews = [[self scalingView] subviews];
+ if ([scalingViewSubviews count] == 0)
+ return nil; // No contents view attached.
+ return [scalingViewSubviews objectAtIndex:0];
+}
+
+// Override auto-resizing logic to query the delegate for the exact frame and
+// bounds to use for the contents view, and then perform manual layout of the
+// scaling view (child view) and contents view (grandchild view).
- (void)resizeSubviewsWithOldSize:(NSSize)oldBoundsSize {
- NSView* const contentsView =
- [[self subviews] count] > 0 ? [[self subviews] objectAtIndex:0] : nil;
- if (!contentsView || [contentsView autoresizingMask] == NSViewNotSizable ||
- !delegate_ ||
+ ScopedCAActionDisabler disabler;
+
+ NSView* const contentsView = [self contentsView];
+ if (!contentsView)
+ return; // No contents view attached yet.
+
+ // TODO(spqchan): The popup check is a temporary solution to fix the
+ // regression described in crbug.com/604288. This method doesn't really affect
+ // fullscreen if the content is inside a normal browser window, but would
+ // cause a flash fullscreen widget to blow up if it's inside a popup.
+ if (!delegate_ ||
(![delegate_ shouldResizeContentView] && [delegate_ isPopup])) {
return;
}
- ScopedCAActionDisabler disabler;
- [contentsView setFrame:[delegate_ frameForContentsViewIn:self]];
+ // Set the position and size of the scaling view within this container view.
+ NSView* const scalingView = [self scalingView];
+ [scalingView setFrame:[delegate_ frameForScalingViewIn:self]];
+ const NSRect scalingViewBounds = [delegate_ boundsForScalingViewIn:self];
+ [scalingView setBounds:scalingViewBounds];
+
+ // Update the position and size of the contents view, if it has changed.
+ if (!NSEqualRects(scalingViewBounds, [contentsView frame]))
+ [contentsView setFrame:scalingViewBounds];
}
// Update the background layer's color whenever the view needs to repaint.
@@ -138,6 +184,23 @@ - (void)setNeedsDisplayInRect:(NSRect)rect {
[self updateBackgroundColor];
}
+- (void)ensureContentsVisible:(NSView*)contentsView {
+ DCHECK(contentsView);
+
+ // TabContentsContainerView performs manual layout.
+ [contentsView setAutoresizingMask:NSViewNotSizable];
+
+ NSView* const scalingView = [self scalingView];
+ if ([[scalingView subviews] count] == 0) {
+ [scalingView addSubview:contentsView];
+ } else if ([[scalingView subviews] objectAtIndex:0] != contentsView) {
+ [scalingView replaceSubview:[[scalingView subviews] objectAtIndex:0]
+ with:contentsView];
+ }
+
+ [self resizeSubviewsWithOldSize:[self bounds].size];
+}
+
- (void)updateBackgroundColor {
// This view is sometimes flashed into visibility (e.g, when closing
// windows or opening new tabs), so ensure that the flash be the theme
@@ -169,8 +232,8 @@ - (ViewID)viewID {
}
- (BOOL)acceptsFirstResponder {
- return [[self subviews] count] > 0 &&
- [[[self subviews] objectAtIndex:0] acceptsFirstResponder];
+ NSView* const contentsView = [self contentsView];
+ return contentsView && [contentsView acceptsFirstResponder];
}
// When receiving a click-to-focus in the solid color area surrounding the
@@ -179,7 +242,7 @@ - (BOOL)acceptsFirstResponder {
- (BOOL)becomeFirstResponder {
if (![self acceptsFirstResponder])
return NO;
- return [[self window] makeFirstResponder:[[self subviews] objectAtIndex:0]];
+ return [[self window] makeFirstResponder:[self contentsView]];
}
- (BOOL)canBecomeKeyView {
@@ -221,8 +284,7 @@ - (void)ensureContentsVisibleInSuperview:(NSView*)superview {
return;
ScopedCAActionDisabler disabler;
- NSView* contentsContainer = [self view];
- NSArray* subviews = [contentsContainer subviews];
+
NSView* contentsNativeView;
content::RenderWidgetHostView* const fullscreenView =
isEmbeddingFullscreenWidget_ ?
@@ -234,22 +296,11 @@ - (void)ensureContentsVisibleInSuperview:(NSView*)superview {
contentsNativeView = contents_->GetNativeView();
}
- if ([self shouldResizeContentView])
- [contentsNativeView setFrame:[self frameForContentsViewIn:superview]];
-
- if ([subviews count] == 0) {
- [contentsContainer addSubview:contentsNativeView];
- } else if ([subviews objectAtIndex:0] != contentsNativeView) {
- [contentsContainer replaceSubview:[subviews objectAtIndex:0]
- with:contentsNativeView];
- }
-
- [contentsNativeView setAutoresizingMask:NSViewNotSizable];
+ TabContentsContainerView* const contentsContainer =
+ static_cast<TabContentsContainerView*>([self view]);
+ [contentsContainer ensureContentsVisible:contentsNativeView];
[contentsContainer setFrame:[superview bounds]];
[superview addSubview:contentsContainer];
- [contentsNativeView setAutoresizingMask:NSViewWidthSizable|
- NSViewHeightSizable];
-
[contentsContainer setNeedsDisplay:YES];
// Push the background color down to the RenderWidgetHostView, so that if
@@ -272,8 +323,8 @@ - (void)updateFullscreenWidgetFrame {
content::RenderWidgetHostView* const fullscreenView =
contents_->GetFullscreenRenderWidgetHostView();
if (fullscreenView) {
- [fullscreenView->GetNativeView()
- setFrame:[self frameForContentsViewIn:[self view]]];
+ [static_cast<TabContentsContainerView*>([self view])
+ ensureContentsVisible:fullscreenView->GetNativeView()];
}
}
@@ -350,41 +401,49 @@ - (BOOL)contentsInFullscreenCaptureMode {
return YES;
}
-- (NSRect)frameForContentsViewIn:(NSView*)container {
- gfx::Rect rect([container bounds]);
+- (NSRect)frameForScalingViewIn:(NSView*)container {
+ NSRect bounds = [container bounds];
+ bounds.origin = NSZeroPoint;
// In most cases, the contents view is simply sized to fill the container
// view's bounds. Only WebContentses that are in fullscreen mode and being
// screen-captured will engage the special layout/sizing behavior.
if (![self contentsInFullscreenCaptureMode])
- return NSRectFromCGRect(rect.ToCGRect());
-
- // Size the contents view to the capture video resolution and center it. If
- // the container view is not large enough to fit it at the preferred size,
- // scale down to fit (preserving aspect ratio).
- content::WebContents* const wc = fullscreenObserver_->web_contents();
- const gfx::Size captureSize = wc->GetPreferredSize();
- if (captureSize.width() <= rect.width() &&
- captureSize.height() <= rect.height()) {
- // No scaling, just centering.
- rect.ClampToCenteredSize(captureSize);
+ return bounds;
+
+ // For 'AutoEmbedFullscreen mode', scale the view to fit within the container,
+ // and center it.
+ gfx::RectF rect(bounds);
+ gfx::Size captureSize =
+ fullscreenObserver_->web_contents()->GetPreferredSize();
+ DCHECK(!captureSize.IsEmpty());
+ const float x = captureSize.width() * rect.height();
+ const float y = captureSize.height() * rect.width();
+ if (y < x) {
+ rect.ClampToCenteredSize(gfx::SizeF(rect.width(), y / captureSize.width()));
} else {
- // Scale down, preserving aspect ratio, and center.
- // TODO(miu): This is basically media::ComputeLetterboxRegion(), and it
- // looks like others have written this code elsewhere. Let's consolidate
- // into a shared function ui/gfx/geometry or around there.
- const int64_t x = static_cast<int64_t>(captureSize.width()) * rect.height();
- const int64_t y = static_cast<int64_t>(captureSize.height()) * rect.width();
- if (y < x) {
- rect.ClampToCenteredSize(gfx::Size(
- rect.width(), static_cast<int>(y / captureSize.width())));
- } else {
- rect.ClampToCenteredSize(gfx::Size(
- static_cast<int>(x / captureSize.height()), rect.height()));
- }
+ rect.ClampToCenteredSize(
+ gfx::SizeF(x / captureSize.height(), rect.height()));
}
+ // Ensure the bounds align with pixel boundaries.
+ return gfx::ToEnclosedRect(rect).ToCGRect();
+}
- return NSRectFromCGRect(rect.ToCGRect());
+- (NSRect)boundsForScalingViewIn:(NSView*)container {
+ NSRect bounds;
+ bounds.origin = NSZeroPoint;
+
+ // When in 'AutoEmbedFullscreen mode' mode, the bounds size is set to the
+ // video capture resolution. See header file for more details.
+ if ([self contentsInFullscreenCaptureMode]) {
+ content::WebContents* const wc = fullscreenObserver_->web_contents();
+ gfx::Size captureSize = wc->GetPreferredSize();
+ DCHECK(!captureSize.IsEmpty());
+ bounds.size = captureSize.ToCGSize();
+ } else {
+ bounds.size = [container bounds].size;
+ }
+ return bounds;
}
- (BOOL)shouldResizeContentView {

Powered by Google App Engine
This is Rietveld 408576698