diff options
author | alekseys@chromium.org <alekseys@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-11-16 22:33:03 +0000 |
---|---|---|
committer | alekseys@chromium.org <alekseys@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-11-16 22:33:03 +0000 |
commit | e2356245e357455e99aade6a0800032b115c1b53 (patch) | |
tree | dcd58e1a53d918716650531111141d266d6aea9b /chrome/browser/cocoa | |
parent | 880a6d5e6933a40338c06fcc5d9fd7ae5e654a4a (diff) | |
download | chromium_src-e2356245e357455e99aade6a0800032b115c1b53.zip chromium_src-e2356245e357455e99aade6a0800032b115c1b53.tar.gz chromium_src-e2356245e357455e99aade6a0800032b115c1b53.tar.bz2 |
Handle resize corner layout entirely in the BrowserView (views) or BrowserWindow* (Mac)
and extend RenderViewHost with a concept of reserved contents rect, a place to show extra stuff,
such as Sidebar's mini tab UI.
sidebar UI implementation warranted this change (mini tabs UI and resize corner area for sidebar
contents).
TabContentsDelegate::GetRootWindowResizerRect() is no more, reserved contents area is now cached
in RenderWidgetHostView and updated upon view resize.
Views: BrowserView is responsible for the actual layout and reserved contents area update.
Mac: TabContentsController now manages all TabContents views in the main browser window to solve
two problems, first to prevent contents flickering during tab change not only for the page,
but for the sidebar and devtools contents too and, second, to monitor contents view frame changes
and update reserved contents area accordingly.
BUG=51084
TEST=All tests should pass, this is a refactoring CL.
Review URL: http://codereview.chromium.org/3547008
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@66332 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/cocoa')
-rw-r--r-- | chrome/browser/cocoa/browser_window_cocoa.h | 1 | ||||
-rw-r--r-- | chrome/browser/cocoa/browser_window_cocoa.mm | 5 | ||||
-rw-r--r-- | chrome/browser/cocoa/browser_window_controller.h | 6 | ||||
-rw-r--r-- | chrome/browser/cocoa/browser_window_controller.mm | 71 | ||||
-rw-r--r-- | chrome/browser/cocoa/dev_tools_controller.h | 10 | ||||
-rw-r--r-- | chrome/browser/cocoa/dev_tools_controller.mm | 43 | ||||
-rw-r--r-- | chrome/browser/cocoa/sidebar_controller.h | 11 | ||||
-rw-r--r-- | chrome/browser/cocoa/sidebar_controller.mm | 33 | ||||
-rw-r--r-- | chrome/browser/cocoa/tab_contents_controller.h | 44 | ||||
-rw-r--r-- | chrome/browser/cocoa/tab_contents_controller.mm | 152 | ||||
-rw-r--r-- | chrome/browser/cocoa/tab_strip_controller.h | 14 | ||||
-rw-r--r-- | chrome/browser/cocoa/tab_strip_controller.mm | 15 |
12 files changed, 312 insertions, 93 deletions
diff --git a/chrome/browser/cocoa/browser_window_cocoa.h b/chrome/browser/cocoa/browser_window_cocoa.h index 5722fff..486ec11 100644 --- a/chrome/browser/cocoa/browser_window_cocoa.h +++ b/chrome/browser/cocoa/browser_window_cocoa.h @@ -66,7 +66,6 @@ class BrowserWindowCocoa : public BrowserWindow, virtual bool IsBookmarkBarVisible() const; virtual bool IsBookmarkBarAnimating() const; virtual bool IsToolbarVisible() const; - virtual gfx::Rect GetRootWindowResizerRect() const; virtual void ConfirmAddSearchProvider(const TemplateURL* template_url, Profile* profile); virtual void ToggleBookmarkBar(); diff --git a/chrome/browser/cocoa/browser_window_cocoa.mm b/chrome/browser/cocoa/browser_window_cocoa.mm index 8695325..5ce7156 100644 --- a/chrome/browser/cocoa/browser_window_cocoa.mm +++ b/chrome/browser/cocoa/browser_window_cocoa.mm @@ -216,11 +216,6 @@ bool BrowserWindowCocoa::IsFullscreenBubbleVisible() const { return false; } -gfx::Rect BrowserWindowCocoa::GetRootWindowResizerRect() const { - NSRect tabRect = [controller_ selectedTabGrowBoxRect]; - return gfx::Rect(NSRectToCGRect(tabRect)); -} - void BrowserWindowCocoa::ConfirmAddSearchProvider( const TemplateURL* template_url, Profile* profile) { diff --git a/chrome/browser/cocoa/browser_window_controller.h b/chrome/browser/cocoa/browser_window_controller.h index 39bd9c4..ce55ec6 100644 --- a/chrome/browser/cocoa/browser_window_controller.h +++ b/chrome/browser/cocoa/browser_window_controller.h @@ -18,6 +18,7 @@ #import "chrome/browser/cocoa/bookmarks/bookmark_bar_controller.h" #import "chrome/browser/cocoa/bookmarks/bookmark_bubble_controller.h" #import "chrome/browser/cocoa/browser_command_executor.h" +#import "chrome/browser/cocoa/tab_contents_controller.h" #import "chrome/browser/cocoa/tab_strip_controller.h" #import "chrome/browser/cocoa/tab_window_controller.h" #import "chrome/browser/cocoa/themed_window.h" @@ -52,6 +53,7 @@ class TabContents; BookmarkBarControllerDelegate, BrowserCommandExecutor, ViewResizer, + TabContentsControllerDelegate, TabStripControllerDelegate> { @private // The ordering of these members is important as it determines the order in @@ -170,10 +172,6 @@ class TabContents; // Sets whether or not the current page in the frontmost tab is bookmarked. - (void)setStarredState:(BOOL)isStarred; -// Return the rect, in WebKit coordinates (flipped), of the window's grow box -// in the coordinate system of the content area of the currently selected tab. -- (NSRect)selectedTabGrowBoxRect; - // Called to tell the selected tab to update its loading state. // |force| is set if the update is due to changing tabs, as opposed to // the page-load finishing. See comment in reload_button.h. diff --git a/chrome/browser/cocoa/browser_window_controller.mm b/chrome/browser/cocoa/browser_window_controller.mm index da6f070..b074603 100644 --- a/chrome/browser/cocoa/browser_window_controller.mm +++ b/chrome/browser/cocoa/browser_window_controller.mm @@ -240,14 +240,15 @@ // Create a sub-controller for the docked devTools and add its view to the // hierarchy. This must happen before the sidebar controller is // instantiated. - devToolsController_.reset([[DevToolsController alloc] init]); + devToolsController_.reset( + [[DevToolsController alloc] initWithDelegate:self]); [[devToolsController_ view] setFrame:[[self tabContentArea] bounds]]; [[self tabContentArea] addSubview:[devToolsController_ view]]; // Create a sub-controller for the docked sidebar and add its view to the // hierarchy. This must happen before the previewable contents controller // is instantiated. - sidebarController_.reset([[SidebarController alloc] init]); + sidebarController_.reset([[SidebarController alloc] initWithDelegate:self]); [[sidebarController_ view] setFrame:[[devToolsController_ view] bounds]]; [[devToolsController_ view] addSubview:[sidebarController_ view]]; @@ -444,10 +445,12 @@ - (void)updateDevToolsForContents:(TabContents*)contents { [devToolsController_ updateDevToolsForTabContents:contents]; + [devToolsController_ ensureContentsVisible]; } - (void)updateSidebarForContents:(TabContents*)contents { [sidebarController_ updateSidebarForTabContents:contents]; + [sidebarController_ ensureContentsVisible]; } // Called when the user wants to close a window or from the shutdown process. @@ -1045,24 +1048,6 @@ [toolbarController_ setStarredState:isStarred]; } -// Return the rect, in WebKit coordinates (flipped), of the window's grow box -// in the coordinate system of the content area of the currently selected tab. -// |windowGrowBox| needs to be in the window's coordinate system. -- (NSRect)selectedTabGrowBoxRect { - NSWindow* window = [self window]; - if (![window respondsToSelector:@selector(_growBoxRect)]) - return NSZeroRect; - - // Before we return a rect, we need to convert it from window coordinates - // to tab content area coordinates and flip the coordinate system. - NSRect growBoxRect = - [[self tabContentArea] convertRect:[window _growBoxRect] fromView:nil]; - growBoxRect.origin.y = - [[self tabContentArea] frame].size.height - growBoxRect.size.height - - growBoxRect.origin.y; - return growBoxRect; -} - // Accept tabs from a BrowserWindowController with the same Profile. - (BOOL)canReceiveFrom:(TabWindowController*)source { if (![source isKindOfClass:[BrowserWindowController class]]) { @@ -1352,6 +1337,44 @@ return [self supportsWindowFeature:Browser::FEATURE_TABSTRIP]; } +// TabContentsControllerDelegate protocol. +- (void)tabContentsViewFrameWillChange:(TabContentsController*)source + frameRect:(NSRect)frameRect { + TabContents* contents = [source tabContents]; + RenderWidgetHostView* render_widget_host_view = contents ? + contents->GetRenderWidgetHostView() : NULL; + if (!render_widget_host_view) + return; + + gfx::Rect reserved_rect; + + NSWindow* window = [self window]; + if ([window respondsToSelector:@selector(_growBoxRect)]) { + NSView* view = [source view]; + NSRect windowGrowBoxRect = [window _growBoxRect]; + NSRect viewRect = [[view superview] convertRect:frameRect toView:nil]; + NSRect growBoxRect = NSIntersectionRect(windowGrowBoxRect, viewRect); + if (!NSIsEmptyRect(growBoxRect)) { + // Before we return a rect, we need to convert it from window coordinates + // to content area coordinates and flip the coordinate system. + // Superview is used here because, first, it's a frame rect, so it is + // specified in the parent's coordinates and, second, view is not + // positioned yet. + growBoxRect = [[view superview] convertRect:growBoxRect fromView:nil]; + growBoxRect.origin.y = + NSHeight(frameRect) - NSHeight(growBoxRect); + growBoxRect = + NSOffsetRect(growBoxRect, -frameRect.origin.x, -frameRect.origin.y); + + reserved_rect = + gfx::Rect(growBoxRect.origin.x, growBoxRect.origin.y, + growBoxRect.size.width, growBoxRect.size.height); + } + } + + render_widget_host_view->set_reserved_contents_rect(reserved_rect); +} + // TabStripControllerDelegate protocol. - (void)onSelectTabWithContents:(TabContents*)contents { // Update various elements that are interested in knowing the current @@ -1360,8 +1383,8 @@ // Update all the UI bits. windowShim_->UpdateTitleBar(); - [self updateSidebarForContents:contents]; - [self updateDevToolsForContents:contents]; + [sidebarController_ updateSidebarForTabContents:contents]; + [devToolsController_ updateDevToolsForTabContents:contents]; // Update the bookmark bar. // Must do it after sidebar and devtools update, otherwise bookmark bar might @@ -1371,6 +1394,10 @@ [self updateBookmarkBarVisibilityWithAnimation:NO]; [infoBarContainerController_ changeTabContents:contents]; + + // Update devTools and sidebar contents after size for all views is set. + [sidebarController_ ensureContentsVisible]; + [devToolsController_ ensureContentsVisible]; } - (void)onReplaceTabWithContents:(TabContents*)contents { diff --git a/chrome/browser/cocoa/dev_tools_controller.h b/chrome/browser/cocoa/dev_tools_controller.h index 774d799..05aaca4 100644 --- a/chrome/browser/cocoa/dev_tools_controller.h +++ b/chrome/browser/cocoa/dev_tools_controller.h @@ -9,6 +9,7 @@ #import <Foundation/Foundation.h> #include "base/scoped_nsobject.h" +#import "chrome/browser/cocoa/tab_contents_controller.h" @class NSSplitView; @class NSView; @@ -22,9 +23,12 @@ class TabContents; @private // A view hosting docked devTools contents. scoped_nsobject<NSSplitView> splitView_; + + // Manages currently displayed devTools contents. + scoped_nsobject<TabContentsController> contentsController_; } -- (id)init; +- (id)initWithDelegate:(id<TabContentsControllerDelegate>)delegate; // This controller's view. - (NSView*)view; @@ -38,6 +42,10 @@ class TabContents; // the actual resize). - (void)updateDevToolsForTabContents:(TabContents*)contents; +// Call when the devTools view is properly sized and the render widget host view +// should be put into the view hierarchy. +- (void)ensureContentsVisible; + @end #endif // CHROME_BROWSER_COCOA_DEV_TOOLS_CONTROLLER_H_ diff --git a/chrome/browser/cocoa/dev_tools_controller.mm b/chrome/browser/cocoa/dev_tools_controller.mm index c9cf4e1..cba5822 100644 --- a/chrome/browser/cocoa/dev_tools_controller.mm +++ b/chrome/browser/cocoa/dev_tools_controller.mm @@ -36,16 +36,26 @@ const int kMinWebHeight = 50; @implementation DevToolsController -- (id)init { +- (id)initWithDelegate:(id<TabContentsControllerDelegate>)delegate { if ((self = [super init])) { splitView_.reset([[NSSplitView alloc] initWithFrame:NSZeroRect]); [splitView_ setDividerStyle:NSSplitViewDividerStyleThin]; [splitView_ setVertical:NO]; [splitView_ setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable]; + [splitView_ setDelegate:self]; + + contentsController_.reset( + [[TabContentsController alloc] initWithContents:NULL + delegate:delegate]); } return self; } +- (void)dealloc { + [splitView_ setDelegate:nil]; + [super dealloc]; +} + - (NSView*)view { return splitView_.get(); } @@ -62,7 +72,13 @@ const int kMinWebHeight = 50; [self showDevToolsContents:devToolsContents]; } +- (void)ensureContentsVisible { + [contentsController_ ensureContentsVisible]; +} + - (void)showDevToolsContents:(TabContents*)devToolsContents { + [contentsController_ ensureContentsSizeDoesNotChange]; + NSArray* subviews = [splitView_ subviews]; if (devToolsContents) { DCHECK_GE([subviews count], 1u); @@ -70,8 +86,8 @@ const int kMinWebHeight = 50; // |devToolsView| is a TabContentsViewCocoa object, whose ViewID was // set to VIEW_ID_TAB_CONTAINER initially, so we need to change it to // VIEW_ID_DEV_TOOLS_DOCKED here. - NSView* devToolsView = devToolsContents->GetNativeView(); - view_id_util::SetID(devToolsView, VIEW_ID_DEV_TOOLS_DOCKED); + view_id_util::SetID( + devToolsContents->GetNativeView(), VIEW_ID_DEV_TOOLS_DOCKED); CGFloat splitOffset = 0; if ([subviews count] == 1) { @@ -82,13 +98,11 @@ const int kMinWebHeight = 50; // Initial load, set to default value. splitOffset = kDefaultContentsSplitOffset; } - [splitView_ addSubview:devToolsView]; + [splitView_ addSubview:[contentsController_ view]]; } else { DCHECK_EQ([subviews count], 2u); // If devtools are already visible, keep the current size. - splitOffset = NSHeight([devToolsView frame]); - [splitView_ replaceSubview:[subviews objectAtIndex:1] - with:devToolsView]; + splitOffset = NSHeight([[subviews objectAtIndex:1] frame]); } // Make sure |splitOffset| isn't too large or too small. @@ -107,8 +121,11 @@ const int kMinWebHeight = 50; g_browser_process->local_state()->SetInteger( prefs::kDevToolsSplitLocation, splitOffset); [oldDevToolsContentsView removeFromSuperview]; + [splitView_ adjustSubviews]; } } + + [contentsController_ changeTabContents:devToolsContents]; } - (void)resizeDevToolsToNewHeight:(CGFloat)height { @@ -132,6 +149,16 @@ const int kMinWebHeight = 50; [splitView_ adjustSubviews]; } - +// NSSplitViewDelegate protocol. +- (BOOL)splitView:(NSSplitView *)splitView + shouldAdjustSizeOfSubview:(NSView *)subview { + // Return NO for the devTools view to indicate that it should not be resized + // automatically. It preserves the height set by the user and also keeps + // view height the same while changing tabs when one of the tabs shows infobar + // and others are not. + if ([[splitView_ subviews] indexOfObject:subview] == 1) + return NO; + return YES; +} @end diff --git a/chrome/browser/cocoa/sidebar_controller.h b/chrome/browser/cocoa/sidebar_controller.h index f3c4a30..00c272a 100644 --- a/chrome/browser/cocoa/sidebar_controller.h +++ b/chrome/browser/cocoa/sidebar_controller.h @@ -9,6 +9,7 @@ #import <Foundation/Foundation.h> #include "base/scoped_nsobject.h" +#import "chrome/browser/cocoa/tab_contents_controller.h" @class NSSplitView; @class NSView; @@ -23,11 +24,11 @@ class TabContents; // A view hosting sidebar contents. scoped_nsobject<NSSplitView> splitView_; - // Currently displayed sidebar contents. - TabContents* sidebarContents_; // weak. + // Manages currently displayed sidebar contents. + scoped_nsobject<TabContentsController> contentsController_; } -- (id)init; +- (id)initWithDelegate:(id<TabContentsControllerDelegate>)delegate; // This controller's view. - (NSSplitView*)view; @@ -41,6 +42,10 @@ class TabContents; // the actual resize). - (void)updateSidebarForTabContents:(TabContents*)contents; +// Call when the sidebar view is properly sized and the render widget host view +// should be put into the view hierarchy. +- (void)ensureContentsVisible; + @end #endif // CHROME_BROWSER_COCOA_SIDEBAR_CONTROLLER_H_ diff --git a/chrome/browser/cocoa/sidebar_controller.mm b/chrome/browser/cocoa/sidebar_controller.mm index b78570e..77ec323 100644 --- a/chrome/browser/cocoa/sidebar_controller.mm +++ b/chrome/browser/cocoa/sidebar_controller.mm @@ -36,14 +36,17 @@ const int kMinWebWidth = 50; @implementation SidebarController -- (id)init { +- (id)initWithDelegate:(id<TabContentsControllerDelegate>)delegate { if ((self = [super init])) { splitView_.reset([[NSSplitView alloc] initWithFrame:NSZeroRect]); [splitView_ setDividerStyle:NSSplitViewDividerStyleThin]; [splitView_ setVertical:YES]; [splitView_ setAutoresizingMask:NSViewWidthSizable|NSViewHeightSizable]; [splitView_ setDelegate:self]; - sidebarContents_ = NULL; + + contentsController_.reset( + [[TabContentsController alloc] initWithContents:NULL + delegate:delegate]); } return self; } @@ -73,11 +76,10 @@ const int kMinWebWidth = 50; if (activeSidebar) sidebarContents = activeSidebar->sidebar_contents(); } - if (sidebarContents_ == sidebarContents) - return; - TabContents* oldSidebarContents = sidebarContents_; - sidebarContents_ = sidebarContents; + TabContents* oldSidebarContents = [contentsController_ tabContents]; + if (oldSidebarContents == sidebarContents) + return; // Adjust sidebar view. [self showSidebarContents:sidebarContents]; @@ -87,16 +89,22 @@ const int kMinWebWidth = 50; oldSidebarContents, sidebarContents); } +- (void)ensureContentsVisible { + [contentsController_ ensureContentsVisible]; +} + - (void)showSidebarContents:(TabContents*)sidebarContents { + [contentsController_ ensureContentsSizeDoesNotChange]; + NSArray* subviews = [splitView_ subviews]; if (sidebarContents) { DCHECK_GE([subviews count], 1u); - // |sidebarView| is a TabContentsViewCocoa object, whose ViewID was + // Native view is a TabContentsViewCocoa object, whose ViewID was // set to VIEW_ID_TAB_CONTAINER initially, so change it to // VIEW_ID_SIDE_BAR_CONTAINER here. - NSView* sidebarView = sidebarContents->GetNativeView(); - view_id_util::SetID(sidebarView, VIEW_ID_SIDE_BAR_CONTAINER); + view_id_util::SetID( + sidebarContents->GetNativeView(), VIEW_ID_SIDE_BAR_CONTAINER); CGFloat sidebarWidth = 0; if ([subviews count] == 1) { @@ -108,12 +116,10 @@ const int kMinWebWidth = 50; sidebarWidth = NSWidth([splitView_ frame]) * kDefaultSidebarWidthRatio; } - [splitView_ addSubview:sidebarView]; + [splitView_ addSubview:[contentsController_ view]]; } else { DCHECK_EQ([subviews count], 2u); sidebarWidth = NSWidth([[subviews objectAtIndex:1] frame]); - [splitView_ replaceSubview:[subviews objectAtIndex:1] - with:sidebarView]; } // Make sure |sidebarWidth| isn't too large or too small. @@ -132,8 +138,11 @@ const int kMinWebWidth = 50; g_browser_process->local_state()->SetInteger( prefs::kExtensionSidebarWidth, sidebarWidth); [oldSidebarContentsView removeFromSuperview]; + [splitView_ adjustSubviews]; } } + + [contentsController_ changeTabContents:sidebarContents]; } - (void)resizeSidebarToNewWidth:(CGFloat)width { diff --git a/chrome/browser/cocoa/tab_contents_controller.h b/chrome/browser/cocoa/tab_contents_controller.h index cf5405a..46a1ca5 100644 --- a/chrome/browser/cocoa/tab_contents_controller.h +++ b/chrome/browser/cocoa/tab_contents_controller.h @@ -8,9 +8,23 @@ #include <Cocoa/Cocoa.h> +#include "base/scoped_ptr.h" + class TabContents; +class TabContentsNotificationBridge; +@class TabContentsController; + +// The interface for the tab contents view controller's delegate. + +@protocol TabContentsControllerDelegate + +// Tells the delegate when the tab contents view's frame is about to change. +- (void)tabContentsViewFrameWillChange:(TabContentsController*)source + frameRect:(NSRect)frameRect; + +@end -// A class that controls the web contents of a tab. It manages displaying the +// A class that controls the TabContents view. It manages displaying the // native view for a given TabContents. // Note that just creating the class does not display the view. We defer // inserting it until the box is the correct size to avoid multiple resize @@ -20,11 +34,27 @@ class TabContents; @interface TabContentsController : NSViewController { @private TabContents* contents_; // weak + // Delegate to be notified about size changes. + id<TabContentsControllerDelegate> delegate_; // weak + scoped_ptr<TabContentsNotificationBridge> tabContentsBridge_; } @property(readonly, nonatomic) TabContents* tabContents; -// Create the contents of a tab represented by |contents|. -- (id)initWithContents:(TabContents*)contents; +- (id)initWithContents:(TabContents*)contents + delegate:(id<TabContentsControllerDelegate>)delegate; + +// Call when the tab contents is about to be replaced with the currently +// selected tab contents to do not trigger unnecessary content relayout. +- (void)ensureContentsSizeDoesNotChange; + +// Call when the tab view is properly sized and the render widget host view +// should be put into the view hierarchy. +- (void)ensureContentsVisible; + +// Call to change the underlying tab contents object. View is not changed, +// call |-ensureContentsVisible| to display the |newContents|'s render widget +// host view. +- (void)changeTabContents:(TabContents*)newContents; // Called when the tab contents is the currently selected tab and is about to be // removed from the view hierarchy. @@ -35,14 +65,6 @@ class TabContents; // enabled. - (void)willBecomeSelectedTab; -// Call when the tab contents is about to be replaced with the currently -// selected tab contents to do not trigger unnecessary content relayout. -- (void)ensureContentsSizeDoesNotChange; - -// Call when the tab view is properly sized and the render widget host view -// should be put into the view hierarchy. -- (void)ensureContentsVisible; - // Called when the tab contents is updated in some non-descript way (the // notification from the model isn't specific). |updatedContents| could reflect // an entirely new tab contents object. diff --git a/chrome/browser/cocoa/tab_contents_controller.mm b/chrome/browser/cocoa/tab_contents_controller.mm index 1ff7721..4a5cd43 100644 --- a/chrome/browser/cocoa/tab_contents_controller.mm +++ b/chrome/browser/cocoa/tab_contents_controller.mm @@ -8,15 +8,105 @@ #include "base/scoped_nsobject.h" #include "chrome/browser/renderer_host/render_view_host.h" #include "chrome/browser/renderer_host/render_widget_host_view.h" +#include "chrome/browser/tab_contents/navigation_controller.h" #include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/common/notification_details.h" +#include "chrome/common/notification_observer.h" +#include "chrome/common/notification_registrar.h" +#include "chrome/common/notification_source.h" +#include "chrome/common/notification_type.h" + + +@interface TabContentsController(Private) +// Forwards frame update to |delegate_| (ResizeNotificationView calls it). +- (void)tabContentsViewFrameWillChange:(NSRect)frameRect; +// Notification from TabContents (forwarded by TabContentsNotificationBridge). +- (void)tabContentsRenderViewHostChanged:(RenderViewHost*)oldHost + newHost:(RenderViewHost*)newHost; +@end + + +// A supporting C++ bridge object to register for TabContents notifications. + +class TabContentsNotificationBridge : public NotificationObserver { + public: + explicit TabContentsNotificationBridge(TabContentsController* controller); + + // Overriden from NotificationObserver. + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + // Register for |contents|'s notifications, remove all prior registrations. + void ChangeTabContents(TabContents* contents); + private: + NotificationRegistrar registrar_; + TabContentsController* controller_; // weak, owns us +}; + +TabContentsNotificationBridge::TabContentsNotificationBridge( + TabContentsController* controller) + : controller_(controller) { +} + +void TabContentsNotificationBridge::Observe( + NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + if (type == NotificationType::RENDER_VIEW_HOST_CHANGED) { + RenderViewHostSwitchedDetails* switched_details = + Details<RenderViewHostSwitchedDetails>(details).ptr(); + [controller_ tabContentsRenderViewHostChanged:switched_details->old_host + newHost:switched_details->new_host]; + } else { + NOTREACHED(); + } +} + +void TabContentsNotificationBridge::ChangeTabContents(TabContents* contents) { + registrar_.RemoveAll(); + if (contents) { + registrar_.Add(this, + NotificationType::RENDER_VIEW_HOST_CHANGED, + Source<NavigationController>(&contents->controller())); + } +} + + +// A custom view that notifies |controller| that view's frame is changing. + +@interface ResizeNotificationView : NSView { + TabContentsController* controller_; +} +- (id)initWithController:(TabContentsController*)controller; +@end + +@implementation ResizeNotificationView + +- (id)initWithController:(TabContentsController*)controller { + if ((self = [super initWithFrame:NSZeroRect])) { + controller_ = controller; + } + return self; +} + +- (void)setFrame:(NSRect)frameRect { + [controller_ tabContentsViewFrameWillChange:frameRect]; + [super setFrame:frameRect]; +} + +@end @implementation TabContentsController @synthesize tabContents = contents_; -- (id)initWithContents:(TabContents*)contents { +- (id)initWithContents:(TabContents*)contents + delegate:(id<TabContentsControllerDelegate>)delegate { if ((self = [super initWithNibName:nil bundle:nil])) { contents_ = contents; + delegate_ = delegate; + tabContentsBridge_.reset(new TabContentsNotificationBridge(self)); + tabContentsBridge_->ChangeTabContents(contents); } return self; } @@ -28,47 +118,76 @@ } - (void)loadView { - scoped_nsobject<NSView> view([[NSView alloc] initWithFrame:NSZeroRect]); + scoped_nsobject<ResizeNotificationView> view( + [[ResizeNotificationView alloc] initWithController:self]); [view setAutoresizingMask:NSViewHeightSizable|NSViewWidthSizable]; [self setView:view]; } - (void)ensureContentsSizeDoesNotChange { - NSView* contentsContainer = [self view]; - NSArray* subviews = [contentsContainer subviews]; - if ([subviews count] > 0) - [contents_->GetNativeView() setAutoresizingMask:NSViewNotSizable]; + if (contents_) { + NSView* contentsContainer = [self view]; + NSArray* subviews = [contentsContainer subviews]; + if ([subviews count] > 0) + [contents_->GetNativeView() setAutoresizingMask:NSViewNotSizable]; + } } // Call when the tab view is properly sized and the render widget host view // should be put into the view hierarchy. - (void)ensureContentsVisible { + if (!contents_) + return; NSView* contentsContainer = [self view]; NSArray* subviews = [contentsContainer subviews]; NSView* contentsNativeView = contents_->GetNativeView(); - [contentsNativeView setFrame:[contentsContainer frame]]; + + NSRect contentsNativeViewFrame = [contentsContainer frame]; + contentsNativeViewFrame.origin = NSZeroPoint; + + [delegate_ tabContentsViewFrameWillChange:self + frameRect:contentsNativeViewFrame]; + + // Native view is resized to the actual size before it becomes visible + // to avoid flickering. + [contentsNativeView setFrame:contentsNativeViewFrame]; if ([subviews count] == 0) { [contentsContainer addSubview:contentsNativeView]; } else if ([subviews objectAtIndex:0] != contentsNativeView) { [contentsContainer replaceSubview:[subviews objectAtIndex:0] with:contentsNativeView]; } + // Restore autoresizing properties possibly stripped by + // ensureContentsSizeDoesNotChange call. [contentsNativeView setAutoresizingMask:NSViewWidthSizable| NSViewHeightSizable]; } -// Returns YES if the tab represented by this controller is the front-most. -- (BOOL)isCurrentTab { - // We're the current tab if we're in the view hierarchy, otherwise some other - // tab is. - return [[self view] superview] ? YES : NO; +- (void)changeTabContents:(TabContents*)newContents { + contents_ = newContents; + tabContentsBridge_->ChangeTabContents(contents_); +} + +- (void)tabContentsViewFrameWillChange:(NSRect)frameRect { + [delegate_ tabContentsViewFrameWillChange:self frameRect:frameRect]; +} + +- (void)tabContentsRenderViewHostChanged:(RenderViewHost*)oldHost + newHost:(RenderViewHost*)newHost { + if (oldHost && newHost && oldHost->view() && newHost->view()) { + newHost->view()->set_reserved_contents_rect( + oldHost->view()->reserved_contents_rect()); + } else { + [delegate_ tabContentsViewFrameWillChange:self + frameRect:[[self view] frame]]; + } } - (void)willBecomeUnselectedTab { // The RWHV is ripped out of the view hierarchy on tab switches, so it never // formally resigns first responder status. Handle this by explicitly sending // a Blur() message to the renderer, but only if the RWHV currently has focus. - RenderViewHost* rvh = contents_->render_view_host(); + RenderViewHost* rvh = [self tabContents]->render_view_host(); if (rvh && rvh->view() && rvh->view()->HasFocus()) rvh->Blur(); } @@ -83,9 +202,10 @@ // Calling setContentView: here removes any first responder status // the view may have, so avoid changing the view hierarchy unless // the view is different. - if (contents_ != updatedContents) { - contents_ = updatedContents; - [self ensureContentsVisible]; + if ([self tabContents] != updatedContents) { + [self changeTabContents:updatedContents]; + if ([self tabContents]) + [self ensureContentsVisible]; } } diff --git a/chrome/browser/cocoa/tab_strip_controller.h b/chrome/browser/cocoa/tab_strip_controller.h index 85de319..b063d9a 100644 --- a/chrome/browser/cocoa/tab_strip_controller.h +++ b/chrome/browser/cocoa/tab_strip_controller.h @@ -10,6 +10,7 @@ #include "base/scoped_nsobject.h" #include "base/scoped_ptr.h" +#import "chrome/browser/cocoa/tab_contents_controller.h" #import "chrome/browser/cocoa/tab_controller_target.h" #import "chrome/browser/cocoa/url_drop_target.h" #import "third_party/GTM/AppKit/GTMWindowSheetController.h" @@ -26,12 +27,10 @@ class TabStripModel; class TabContents; class ToolbarModel; -// The interface for the tab strip controller's delegate. Currently, the -// delegate is the BWC and is responsible for subviews layout and forwarding -// these events to InfoBarContainerController. -// Delegating TabStripModelObserverBridge's events (in lieu of subscrining -// BWC and InfoBarContainerController to TabStripModelObserverBridge events) -// is necessary to guarantee a proper order of subviews layout updates, +// The interface for the tab strip controller's delegate. +// Delegating TabStripModelObserverBridge's events (in lieu of directly +// subscribing to TabStripModelObserverBridge events, as TabStripController +// does) is necessary to guarantee a proper order of subviews layout updates, // otherwise it might trigger unnesessary content relayout, UI flickering etc. @protocol TabStripControllerDelegate @@ -59,7 +58,8 @@ class ToolbarModel; @interface TabStripController : NSObject<TabControllerTarget, URLDropTargetController, - GTMWindowSheetControllerDelegate> { + GTMWindowSheetControllerDelegate, + TabContentsControllerDelegate> { @protected // YES if tabs are to be laid out vertically instead of horizontally. BOOL verticalLayout_; diff --git a/chrome/browser/cocoa/tab_strip_controller.mm b/chrome/browser/cocoa/tab_strip_controller.mm index 896dadf..3ccdf3f 100644 --- a/chrome/browser/cocoa/tab_strip_controller.mm +++ b/chrome/browser/cocoa/tab_strip_controller.mm @@ -469,7 +469,7 @@ private: // New content is in place, delegate should adjust itself accordingly. [delegate_ onSelectTabWithContents:[controller tabContents]]; - // It also resores content autoresizing properties. + // It also restores content autoresizing properties. [controller ensureContentsVisible]; // Make sure the new tabs's sheets are visible (necessary when a background @@ -973,7 +973,7 @@ private: // Make a new tab. Load the contents of this tab from the nib and associate // the new controller with |contents| so it can be looked up later. scoped_nsobject<TabContentsController> contentsController( - [[TabContentsController alloc] initWithContents:contents]); + [[TabContentsController alloc] initWithContents:contents delegate:self]); [tabContentsArray_ insertObject:contentsController atIndex:index]; // Make a new tab and add it to the strip. Keep track of its controller. @@ -1080,7 +1080,8 @@ private: // into the array, replacing |oldContents|. A TabSelectedAt notification will // follow, at which point we will install the new view. scoped_nsobject<TabContentsController> newController( - [[TabContentsController alloc] initWithContents:newContents]); + [[TabContentsController alloc] initWithContents:newContents + delegate:self]); // Bye bye, |oldController|. [tabContentsArray_ replaceObjectAtIndex:index withObject:newController]; @@ -1787,6 +1788,14 @@ private: sheetController_.reset(); } +// TabContentsControllerDelegate protocol. +- (void)tabContentsViewFrameWillChange:(TabContentsController*)source + frameRect:(NSRect)frameRect { + id<TabContentsControllerDelegate> controller = + [[switchView_ window] windowController]; + [controller tabContentsViewFrameWillChange:source frameRect:frameRect]; +} + - (TabContentsController*)activeTabContentsController { int modelIndex = tabStripModel_->selected_index(); if (modelIndex < 0) |