Index: chrome/browser/cocoa/tab_contents_controller.mm |
=================================================================== |
--- chrome/browser/cocoa/tab_contents_controller.mm (revision 66034) |
+++ chrome/browser/cocoa/tab_contents_controller.mm (working copy) |
@@ -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]; |
} |
} |