diff options
Diffstat (limited to 'chrome/browser/cocoa/tab_contents_controller.mm')
-rw-r--r-- | chrome/browser/cocoa/tab_contents_controller.mm | 152 |
1 files changed, 136 insertions, 16 deletions
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]; } } |