| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #import "chrome/browser/ui/cocoa/tab_contents_controller.h" | |
| 6 | |
| 7 #include "base/scoped_nsobject.h" | |
| 8 #include "chrome/browser/renderer_host/render_view_host.h" | |
| 9 #include "chrome/browser/renderer_host/render_widget_host_view.h" | |
| 10 #include "chrome/browser/tab_contents/navigation_controller.h" | |
| 11 #include "chrome/browser/tab_contents/tab_contents.h" | |
| 12 #include "chrome/common/notification_details.h" | |
| 13 #include "chrome/common/notification_observer.h" | |
| 14 #include "chrome/common/notification_registrar.h" | |
| 15 #include "chrome/common/notification_source.h" | |
| 16 #include "chrome/common/notification_type.h" | |
| 17 | |
| 18 | |
| 19 @interface TabContentsController(Private) | |
| 20 // Forwards frame update to |delegate_| (ResizeNotificationView calls it). | |
| 21 - (void)tabContentsViewFrameWillChange:(NSRect)frameRect; | |
| 22 // Notification from TabContents (forwarded by TabContentsNotificationBridge). | |
| 23 - (void)tabContentsRenderViewHostChanged:(RenderViewHost*)oldHost | |
| 24 newHost:(RenderViewHost*)newHost; | |
| 25 @end | |
| 26 | |
| 27 | |
| 28 // A supporting C++ bridge object to register for TabContents notifications. | |
| 29 | |
| 30 class TabContentsNotificationBridge : public NotificationObserver { | |
| 31 public: | |
| 32 explicit TabContentsNotificationBridge(TabContentsController* controller); | |
| 33 | |
| 34 // Overriden from NotificationObserver. | |
| 35 virtual void Observe(NotificationType type, | |
| 36 const NotificationSource& source, | |
| 37 const NotificationDetails& details); | |
| 38 // Register for |contents|'s notifications, remove all prior registrations. | |
| 39 void ChangeTabContents(TabContents* contents); | |
| 40 private: | |
| 41 NotificationRegistrar registrar_; | |
| 42 TabContentsController* controller_; // weak, owns us | |
| 43 }; | |
| 44 | |
| 45 TabContentsNotificationBridge::TabContentsNotificationBridge( | |
| 46 TabContentsController* controller) | |
| 47 : controller_(controller) { | |
| 48 } | |
| 49 | |
| 50 void TabContentsNotificationBridge::Observe( | |
| 51 NotificationType type, | |
| 52 const NotificationSource& source, | |
| 53 const NotificationDetails& details) { | |
| 54 if (type == NotificationType::RENDER_VIEW_HOST_CHANGED) { | |
| 55 RenderViewHostSwitchedDetails* switched_details = | |
| 56 Details<RenderViewHostSwitchedDetails>(details).ptr(); | |
| 57 [controller_ tabContentsRenderViewHostChanged:switched_details->old_host | |
| 58 newHost:switched_details->new_host]; | |
| 59 } else { | |
| 60 NOTREACHED(); | |
| 61 } | |
| 62 } | |
| 63 | |
| 64 void TabContentsNotificationBridge::ChangeTabContents(TabContents* contents) { | |
| 65 registrar_.RemoveAll(); | |
| 66 if (contents) { | |
| 67 registrar_.Add(this, | |
| 68 NotificationType::RENDER_VIEW_HOST_CHANGED, | |
| 69 Source<NavigationController>(&contents->controller())); | |
| 70 } | |
| 71 } | |
| 72 | |
| 73 | |
| 74 // A custom view that notifies |controller| that view's frame is changing. | |
| 75 | |
| 76 @interface ResizeNotificationView : NSView { | |
| 77 TabContentsController* controller_; | |
| 78 } | |
| 79 - (id)initWithController:(TabContentsController*)controller; | |
| 80 @end | |
| 81 | |
| 82 @implementation ResizeNotificationView | |
| 83 | |
| 84 - (id)initWithController:(TabContentsController*)controller { | |
| 85 if ((self = [super initWithFrame:NSZeroRect])) { | |
| 86 controller_ = controller; | |
| 87 } | |
| 88 return self; | |
| 89 } | |
| 90 | |
| 91 - (void)setFrame:(NSRect)frameRect { | |
| 92 [controller_ tabContentsViewFrameWillChange:frameRect]; | |
| 93 [super setFrame:frameRect]; | |
| 94 } | |
| 95 | |
| 96 @end | |
| 97 | |
| 98 | |
| 99 @implementation TabContentsController | |
| 100 @synthesize tabContents = contents_; | |
| 101 | |
| 102 - (id)initWithContents:(TabContents*)contents | |
| 103 delegate:(id<TabContentsControllerDelegate>)delegate { | |
| 104 if ((self = [super initWithNibName:nil bundle:nil])) { | |
| 105 contents_ = contents; | |
| 106 delegate_ = delegate; | |
| 107 tabContentsBridge_.reset(new TabContentsNotificationBridge(self)); | |
| 108 tabContentsBridge_->ChangeTabContents(contents); | |
| 109 } | |
| 110 return self; | |
| 111 } | |
| 112 | |
| 113 - (void)dealloc { | |
| 114 // make sure our contents have been removed from the window | |
| 115 [[self view] removeFromSuperview]; | |
| 116 [super dealloc]; | |
| 117 } | |
| 118 | |
| 119 - (void)loadView { | |
| 120 scoped_nsobject<ResizeNotificationView> view( | |
| 121 [[ResizeNotificationView alloc] initWithController:self]); | |
| 122 [view setAutoresizingMask:NSViewHeightSizable|NSViewWidthSizable]; | |
| 123 [self setView:view]; | |
| 124 } | |
| 125 | |
| 126 - (void)ensureContentsSizeDoesNotChange { | |
| 127 if (contents_) { | |
| 128 NSView* contentsContainer = [self view]; | |
| 129 NSArray* subviews = [contentsContainer subviews]; | |
| 130 if ([subviews count] > 0) | |
| 131 [contents_->GetNativeView() setAutoresizingMask:NSViewNotSizable]; | |
| 132 } | |
| 133 } | |
| 134 | |
| 135 // Call when the tab view is properly sized and the render widget host view | |
| 136 // should be put into the view hierarchy. | |
| 137 - (void)ensureContentsVisible { | |
| 138 if (!contents_) | |
| 139 return; | |
| 140 NSView* contentsContainer = [self view]; | |
| 141 NSArray* subviews = [contentsContainer subviews]; | |
| 142 NSView* contentsNativeView = contents_->GetNativeView(); | |
| 143 | |
| 144 NSRect contentsNativeViewFrame = [contentsContainer frame]; | |
| 145 contentsNativeViewFrame.origin = NSZeroPoint; | |
| 146 | |
| 147 [delegate_ tabContentsViewFrameWillChange:self | |
| 148 frameRect:contentsNativeViewFrame]; | |
| 149 | |
| 150 // Native view is resized to the actual size before it becomes visible | |
| 151 // to avoid flickering. | |
| 152 [contentsNativeView setFrame:contentsNativeViewFrame]; | |
| 153 if ([subviews count] == 0) { | |
| 154 [contentsContainer addSubview:contentsNativeView]; | |
| 155 } else if ([subviews objectAtIndex:0] != contentsNativeView) { | |
| 156 [contentsContainer replaceSubview:[subviews objectAtIndex:0] | |
| 157 with:contentsNativeView]; | |
| 158 } | |
| 159 // Restore autoresizing properties possibly stripped by | |
| 160 // ensureContentsSizeDoesNotChange call. | |
| 161 [contentsNativeView setAutoresizingMask:NSViewWidthSizable| | |
| 162 NSViewHeightSizable]; | |
| 163 } | |
| 164 | |
| 165 - (void)changeTabContents:(TabContents*)newContents { | |
| 166 contents_ = newContents; | |
| 167 tabContentsBridge_->ChangeTabContents(contents_); | |
| 168 } | |
| 169 | |
| 170 - (void)tabContentsViewFrameWillChange:(NSRect)frameRect { | |
| 171 [delegate_ tabContentsViewFrameWillChange:self frameRect:frameRect]; | |
| 172 } | |
| 173 | |
| 174 - (void)tabContentsRenderViewHostChanged:(RenderViewHost*)oldHost | |
| 175 newHost:(RenderViewHost*)newHost { | |
| 176 if (oldHost && newHost && oldHost->view() && newHost->view()) { | |
| 177 newHost->view()->set_reserved_contents_rect( | |
| 178 oldHost->view()->reserved_contents_rect()); | |
| 179 } else { | |
| 180 [delegate_ tabContentsViewFrameWillChange:self | |
| 181 frameRect:[[self view] frame]]; | |
| 182 } | |
| 183 } | |
| 184 | |
| 185 - (void)willBecomeUnselectedTab { | |
| 186 // The RWHV is ripped out of the view hierarchy on tab switches, so it never | |
| 187 // formally resigns first responder status. Handle this by explicitly sending | |
| 188 // a Blur() message to the renderer, but only if the RWHV currently has focus. | |
| 189 RenderViewHost* rvh = [self tabContents]->render_view_host(); | |
| 190 if (rvh && rvh->view() && rvh->view()->HasFocus()) | |
| 191 rvh->Blur(); | |
| 192 } | |
| 193 | |
| 194 - (void)willBecomeSelectedTab { | |
| 195 // Do not explicitly call Focus() here, as the RWHV may not actually have | |
| 196 // focus (for example, if the omnibox has focus instead). The TabContents | |
| 197 // logic will restore focus to the appropriate view. | |
| 198 } | |
| 199 | |
| 200 - (void)tabDidChange:(TabContents*)updatedContents { | |
| 201 // Calling setContentView: here removes any first responder status | |
| 202 // the view may have, so avoid changing the view hierarchy unless | |
| 203 // the view is different. | |
| 204 if ([self tabContents] != updatedContents) { | |
| 205 [self changeTabContents:updatedContents]; | |
| 206 if ([self tabContents]) | |
| 207 [self ensureContentsVisible]; | |
| 208 } | |
| 209 } | |
| 210 | |
| 211 @end | |
| OLD | NEW |