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