summaryrefslogtreecommitdiffstats
path: root/chrome/browser/cocoa/tab_contents_controller.mm
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/cocoa/tab_contents_controller.mm')
-rw-r--r--chrome/browser/cocoa/tab_contents_controller.mm152
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];
}
}