Popular sites on the NTP: check that experiment group StartsWith (rather than IS...
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / browser_window_controller_private.mm
blob6cf444570a8bfad5b63510e52eae920ff225c325
1 // Copyright (c) 2012 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.
5 #import "chrome/browser/ui/cocoa/browser_window_controller_private.h"
7 #include <cmath>
9 #include "base/command_line.h"
10 #include "base/mac/bind_objc_block.h"
11 #include "base/mac/foundation_util.h"
12 #include "base/mac/mac_util.h"
13 #import "base/mac/scoped_nsobject.h"
14 #import "base/mac/sdk_forward_declarations.h"
15 #include "base/metrics/histogram.h"
16 #include "base/prefs/pref_service.h"
17 #include "base/prefs/scoped_user_pref_update.h"
18 #include "chrome/browser/browser_process.h"
19 #include "chrome/browser/fullscreen.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/browser/profiles/profile_avatar_icon_util.h"
22 #include "chrome/browser/ui/bookmarks/bookmark_tab_helper.h"
23 #include "chrome/browser/ui/browser.h"
24 #include "chrome/browser/ui/browser_window_state.h"
25 #import "chrome/browser/ui/cocoa/browser_window_fullscreen_transition.h"
26 #import "chrome/browser/ui/cocoa/browser_window_layout.h"
27 #import "chrome/browser/ui/cocoa/custom_frame_view.h"
28 #import "chrome/browser/ui/cocoa/dev_tools_controller.h"
29 #import "chrome/browser/ui/cocoa/fast_resize_view.h"
30 #import "chrome/browser/ui/cocoa/find_bar/find_bar_cocoa_controller.h"
31 #import "chrome/browser/ui/cocoa/floating_bar_backing_view.h"
32 #import "chrome/browser/ui/cocoa/framed_browser_window.h"
33 #import "chrome/browser/ui/cocoa/fullscreen_window.h"
34 #import "chrome/browser/ui/cocoa/infobars/infobar_container_controller.h"
35 #include "chrome/browser/ui/cocoa/last_active_browser_cocoa.h"
36 #import "chrome/browser/ui/cocoa/presentation_mode_controller.h"
37 #import "chrome/browser/ui/cocoa/profiles/avatar_button_controller.h"
38 #import "chrome/browser/ui/cocoa/profiles/avatar_icon_controller.h"
39 #import "chrome/browser/ui/cocoa/status_bubble_mac.h"
40 #import "chrome/browser/ui/cocoa/tab_contents/overlayable_contents_controller.h"
41 #import "chrome/browser/ui/cocoa/tabs/tab_strip_view.h"
42 #import "chrome/browser/ui/cocoa/toolbar/toolbar_controller.h"
43 #import "chrome/browser/ui/cocoa/website_settings/permission_bubble_cocoa.h"
44 #include "chrome/browser/ui/exclusive_access/fullscreen_controller.h"
45 #include "chrome/browser/ui/tabs/tab_strip_model.h"
46 #include "chrome/browser/ui/website_settings/permission_bubble_manager.h"
47 #include "chrome/common/chrome_switches.h"
48 #include "content/public/browser/render_widget_host_view.h"
49 #include "content/public/browser/web_contents.h"
50 #import "ui/base/cocoa/focus_tracker.h"
51 #import "ui/base/cocoa/nsview_additions.h"
52 #include "ui/base/ui_base_types.h"
54 using content::RenderWidgetHostView;
55 using content::WebContents;
57 namespace {
59 // The screen on which the window was fullscreened, and whether the device had
60 // multiple screens available.
61 enum WindowLocation {
62   PRIMARY_SINGLE_SCREEN = 0,
63   PRIMARY_MULTIPLE_SCREEN = 1,
64   SECONDARY_MULTIPLE_SCREEN = 2,
65   WINDOW_LOCATION_COUNT = 3
68 // There are 2 mechanisms for invoking fullscreen: AppKit and Immersive.
69 // There are 2 types of AppKit Fullscreen: Presentation Mode and Canonical
70 // Fullscreen.
71 enum FullscreenStyle {
72   IMMERSIVE_FULLSCREEN = 0,
73   PRESENTATION_MODE = 1,
74   CANONICAL_FULLSCREEN = 2,
75   FULLSCREEN_STYLE_COUNT = 3
78 // Emits a histogram entry indicating the Fullscreen window location.
79 void RecordFullscreenWindowLocation(NSWindow* window) {
80   NSArray* screens = [NSScreen screens];
81   bool primary_screen = ([[window screen] isEqual:[screens objectAtIndex:0]]);
82   bool multiple_screens = [screens count] > 1;
84   WindowLocation location = PRIMARY_SINGLE_SCREEN;
85   if (multiple_screens) {
86     location =
87         primary_screen ? PRIMARY_MULTIPLE_SCREEN : SECONDARY_MULTIPLE_SCREEN;
88   }
90   UMA_HISTOGRAM_ENUMERATION(
91       "OSX.Fullscreen.Enter.WindowLocation", location, WINDOW_LOCATION_COUNT);
94 // Emits a histogram entry indicating the Fullscreen style.
95 void RecordFullscreenStyle(FullscreenStyle style) {
96   UMA_HISTOGRAM_ENUMERATION(
97       "OSX.Fullscreen.Enter.Style", style, FULLSCREEN_STYLE_COUNT);
100 }  // namespace
102 @implementation BrowserWindowController(Private)
104 // Create the tab strip controller.
105 - (void)createTabStripController {
106   DCHECK([overlayableContentsController_ activeContainer]);
107   DCHECK([[overlayableContentsController_ activeContainer] window]);
108   tabStripController_.reset([[TabStripController alloc]
109       initWithView:[self tabStripView]
110         switchView:[overlayableContentsController_ activeContainer]
111            browser:browser_.get()
112           delegate:self]);
115 - (void)saveWindowPositionIfNeeded {
116   if (!chrome::ShouldSaveWindowPlacement(browser_.get()))
117     return;
119   // If we're in fullscreen mode, save the position of the regular window
120   // instead.
121   NSWindow* window =
122       [self isInAnyFullscreenMode] ? savedRegularWindow_ : [self window];
124   // Window positions are stored relative to the origin of the primary monitor.
125   NSRect monitorFrame = [[[NSScreen screens] objectAtIndex:0] frame];
126   NSScreen* windowScreen = [window screen];
128   // Start with the window's frame, which is in virtual coordinates.
129   // Do some y twiddling to flip the coordinate system.
130   gfx::Rect bounds(NSRectToCGRect([window frame]));
131   bounds.set_y(monitorFrame.size.height - bounds.y() - bounds.height());
133   // Browser::SaveWindowPlacement saves information for session restore.
134   ui::WindowShowState show_state = ui::SHOW_STATE_NORMAL;
135   if ([window isMiniaturized])
136     show_state = ui::SHOW_STATE_MINIMIZED;
137   else if ([self isInAnyFullscreenMode])
138     show_state = ui::SHOW_STATE_FULLSCREEN;
139   chrome::SaveWindowPlacement(browser_.get(), bounds, show_state);
141   // |windowScreen| can be nil (for example, if the monitor arrangement was
142   // changed while in fullscreen mode).  If we see a nil screen, return without
143   // saving.
144   // TODO(rohitrao): We should just not save anything for fullscreen windows.
145   // http://crbug.com/36479.
146   if (!windowScreen)
147     return;
149   // Only save main window information to preferences.
150   PrefService* prefs = browser_->profile()->GetPrefs();
151   if (!prefs || browser_ != chrome::GetLastActiveBrowser())
152     return;
154   // Save the current work area, in flipped coordinates.
155   gfx::Rect workArea(NSRectToCGRect([windowScreen visibleFrame]));
156   workArea.set_y(monitorFrame.size.height - workArea.y() - workArea.height());
158   scoped_ptr<DictionaryPrefUpdate> update =
159       chrome::GetWindowPlacementDictionaryReadWrite(
160           chrome::GetWindowName(browser_.get()),
161           browser_->profile()->GetPrefs());
162   base::DictionaryValue* windowPreferences = update->Get();
163   windowPreferences->SetInteger("left", bounds.x());
164   windowPreferences->SetInteger("top", bounds.y());
165   windowPreferences->SetInteger("right", bounds.right());
166   windowPreferences->SetInteger("bottom", bounds.bottom());
167   windowPreferences->SetBoolean("maximized", false);
168   windowPreferences->SetBoolean("always_on_top", false);
169   windowPreferences->SetInteger("work_area_left", workArea.x());
170   windowPreferences->SetInteger("work_area_top", workArea.y());
171   windowPreferences->SetInteger("work_area_right", workArea.right());
172   windowPreferences->SetInteger("work_area_bottom", workArea.bottom());
175 - (NSRect)window:(NSWindow*)window
176 willPositionSheet:(NSWindow*)sheet
177        usingRect:(NSRect)defaultSheetLocation {
178   // Position the sheet as follows:
179   //  - If the bookmark bar is shown (attached to the normal toolbar), position
180   //    the sheet below the bookmark bar.
181   //  - If the bookmark bar is hidden or shown as a bubble (on the NTP when the
182   //    bookmark bar is disabled), position the sheet immediately below the
183   //    normal toolbar.
184   //  - If the bookmark bar is currently animating, position the sheet according
185   //    to where the bar will be when the animation ends.
186   CGFloat defaultSheetY = defaultSheetLocation.origin.y;
187   if ([self supportsBookmarkBar] &&
188       [bookmarkBarController_ currentState] == BookmarkBar::SHOW) {
189     defaultSheetY = NSMinY([[bookmarkBarController_ view] frame]);
190   } else if ([self hasToolbar]) {
191     defaultSheetY = NSMinY([[toolbarController_ view] frame]);
192   } else {
193     // The toolbar is not shown in popup and application modes. The sheet
194     // should be located at the top of the window, under the title of the
195     // window.
196     defaultSheetY = NSMaxY([[window contentView] frame]);
197   }
199   // AppKit may shift the window up to fit the sheet on screen, but it will
200   // never adjust the height of the sheet, or the origin of the sheet relative
201   // to the window. Adjust the origin to prevent sheets from extending past the
202   // bottom of the screen.
204   // Don't allow the sheet to extend past the bottom of the window. This logic
205   // intentionally ignores the size of the screens, since the window might span
206   // multiple screens, and AppKit may reposition the window.
207   CGFloat sheetHeight = NSHeight([sheet frame]);
208   defaultSheetY = std::max(defaultSheetY, sheetHeight);
210   // It doesn't make sense to provide a Y higher than the height of the window.
211   CGFloat windowHeight = NSHeight([window frame]);
212   defaultSheetY = std::min(defaultSheetY, windowHeight);
214   defaultSheetLocation.origin.y = defaultSheetY;
215   return defaultSheetLocation;
218 - (void)layoutSubviews {
219   // Suppress title drawing if necessary.
220   if ([self.window respondsToSelector:@selector(setShouldHideTitle:)])
221     [(id)self.window setShouldHideTitle:![self hasTitleBar]];
223   [bookmarkBarController_ updateHiddenState];
224   [self updateSubviewZOrder];
226   base::scoped_nsobject<BrowserWindowLayout> layout(
227       [[BrowserWindowLayout alloc] init]);
228   [self updateLayoutParameters:layout];
229   [self applyLayout:layout];
231   [toolbarController_ setDividerOpacity:[self toolbarDividerOpacity]];
233   // Will update the location of the permission bubble when showing/hiding the
234   // top level toolbar in fullscreen.
235   PermissionBubbleManager* manager = [self permissionBubbleManager];
236   if (manager)
237     manager->UpdateAnchorPosition();
239   browser_->GetBubbleManager()->UpdateAllBubbleAnchors();
242 - (void)applyTabStripLayout:(const chrome::TabStripLayout&)layout {
243   // Update the presence of the window controls.
244   if (layout.addCustomWindowControls)
245     [tabStripController_ addCustomWindowControls];
246   else
247     [tabStripController_ removeCustomWindowControls];
249   // Update the layout of the avatar.
250   if (!NSIsEmptyRect(layout.avatarFrame)) {
251     NSView* avatarButton = [avatarButtonController_ view];
252     [avatarButton setFrame:layout.avatarFrame];
253     [avatarButton setHidden:NO];
254   }
256   // Check if the tab strip's frame has changed.
257   BOOL requiresRelayout =
258       !NSEqualRects([[self tabStripView] frame], layout.frame);
260   // Check if the left indent has changed.
261   if (layout.leftIndent != [tabStripController_ leftIndentForControls]) {
262     [tabStripController_ setLeftIndentForControls:layout.leftIndent];
263     requiresRelayout = YES;
264   }
266   // Check if the right indent has changed.
267   if (layout.rightIndent != [tabStripController_ rightIndentForControls]) {
268     [tabStripController_ setRightIndentForControls:layout.rightIndent];
269     requiresRelayout = YES;
270   }
272   // It is undesirable to force tabs relayout when the tap strip's frame did
273   // not change, because it will interrupt tab animations in progress.
274   // In addition, there appears to be an AppKit bug on <10.9 where interrupting
275   // a tab animation resulted in the tab frame being the animator's target
276   // frame instead of the interrupting setFrame. (See http://crbug.com/415093)
277   if (requiresRelayout) {
278     [[self tabStripView] setFrame:layout.frame];
279     [tabStripController_ layoutTabsWithoutAnimation];
280   }
283 - (BOOL)placeBookmarkBarBelowInfoBar {
284   // If we are currently displaying the NTP detached bookmark bar or animating
285   // to/from it (from/to anything else), we display the bookmark bar below the
286   // info bar.
287   return [bookmarkBarController_ isInState:BookmarkBar::DETACHED] ||
288          [bookmarkBarController_ isAnimatingToState:BookmarkBar::DETACHED] ||
289          [bookmarkBarController_ isAnimatingFromState:BookmarkBar::DETACHED];
292 - (void)layoutTabContentArea:(NSRect)newFrame {
293   NSView* tabContentView = [self tabContentArea];
294   NSRect tabContentFrame = [tabContentView frame];
296   bool contentShifted =
297       NSMaxY(tabContentFrame) != NSMaxY(newFrame) ||
298       NSMinX(tabContentFrame) != NSMinX(newFrame);
300   tabContentFrame = newFrame;
301   [tabContentView setFrame:tabContentFrame];
303   // If the relayout shifts the content area up or down, let the renderer know.
304   if (contentShifted) {
305     WebContents* contents = [self webContents];
306     if (contents) {
307       RenderWidgetHostView* rwhv = contents->GetRenderWidgetHostView();
308       if (rwhv)
309         rwhv->WindowFrameChanged();
310     }
311   }
314 - (void)adjustToolbarAndBookmarkBarForCompression:(CGFloat)compression {
315   CGFloat newHeight =
316       [toolbarController_ desiredHeightForCompression:compression];
317   NSRect toolbarFrame = [[toolbarController_ view] frame];
318   CGFloat deltaH = newHeight - toolbarFrame.size.height;
320   if (deltaH == 0)
321     return;
323   toolbarFrame.size.height = newHeight;
324   NSRect bookmarkFrame = [[bookmarkBarController_ view] frame];
325   bookmarkFrame.size.height = bookmarkFrame.size.height - deltaH;
326   [[toolbarController_ view] setFrame:toolbarFrame];
327   [[bookmarkBarController_ view] setFrame:bookmarkFrame];
328   [self layoutSubviews];
331 // Fullscreen and presentation mode methods
333 - (void)moveViewsForImmersiveFullscreen:(BOOL)fullscreen
334                           regularWindow:(NSWindow*)regularWindow
335                        fullscreenWindow:(NSWindow*)fullscreenWindow {
336   NSWindow* sourceWindow = fullscreen ? regularWindow : fullscreenWindow;
337   NSWindow* destWindow = fullscreen ? fullscreenWindow : regularWindow;
339   // Close the bookmark bubble, if it's open.  Use |-ok:| instead of |-cancel:|
340   // or |-close| because that matches the behavior when the bubble loses key
341   // status.
342   [bookmarkBubbleController_ ok:self];
344   // Save the current first responder so we can restore after views are moved.
345   base::scoped_nsobject<FocusTracker> focusTracker(
346       [[FocusTracker alloc] initWithWindow:sourceWindow]);
348   // While we move views (and focus) around, disable any bar visibility changes.
349   [self disableBarVisibilityUpdates];
351   // Retain the tab strip view while we remove it from its superview.
352   base::scoped_nsobject<NSView> tabStripView;
353   if ([self hasTabStrip]) {
354     tabStripView.reset([[self tabStripView] retain]);
355     [tabStripView removeFromSuperview];
356   }
358   // Disable autoresizing of subviews while we move views around. This prevents
359   // spurious renderer resizes.
360   [self.chromeContentView setAutoresizesSubviews:NO];
361   [self.chromeContentView removeFromSuperview];
363   // Have to do this here, otherwise later calls can crash because the window
364   // has no delegate.
365   [sourceWindow setDelegate:nil];
366   [destWindow setDelegate:self];
368   // With this call, valgrind complains that a "Conditional jump or move depends
369   // on uninitialised value(s)".  The error happens in -[NSThemeFrame
370   // drawOverlayRect:].  I'm pretty convinced this is an Apple bug, but there is
371   // no visual impact.  I have been unable to tickle it away with other window
372   // or view manipulation Cocoa calls.  Stack added to suppressions_mac.txt.
373   [self.chromeContentView setAutoresizesSubviews:YES];
374   [[destWindow contentView] addSubview:self.chromeContentView
375                             positioned:NSWindowBelow
376                             relativeTo:nil];
377   [self.chromeContentView setFrame:[[destWindow contentView] bounds]];
379   // Move the incognito badge if present.
380   if ([self shouldShowAvatar]) {
381     NSView* avatarButtonView = [avatarButtonController_ view];
383     [avatarButtonView removeFromSuperview];
384     [avatarButtonView setHidden:YES];  // Will be shown in layout.
385     [[destWindow contentView] addSubview:avatarButtonView];
386   }
388   // Add the tab strip after setting the content view and moving the incognito
389   // badge (if any), so that the tab strip will be on top (in the z-order).
390   if ([self hasTabStrip])
391     [[destWindow contentView] addSubview:tabStripView];
393   [sourceWindow setWindowController:nil];
394   [self setWindow:destWindow];
395   [destWindow setWindowController:self];
397   // Move the status bubble over, if we have one.
398   if (statusBubble_)
399     statusBubble_->SwitchParentWindow(destWindow);
401   // Updates the bubble position.
402   PermissionBubbleManager* manager = [self permissionBubbleManager];
403   if (manager)
404     manager->UpdateAnchorPosition();
406   // Move the title over.
407   [destWindow setTitle:[sourceWindow title]];
409   // The window needs to be onscreen before we can set its first responder.
410   // Ordering the window to the front can change the active Space (either to
411   // the window's old Space or to the application's assigned Space). To prevent
412   // this by temporarily change the collectionBehavior.
413   NSWindowCollectionBehavior behavior = [sourceWindow collectionBehavior];
414   [destWindow setCollectionBehavior:
415       NSWindowCollectionBehaviorMoveToActiveSpace];
416   [destWindow makeKeyAndOrderFront:self];
417   [destWindow setCollectionBehavior:behavior];
419   if (![focusTracker restoreFocusInWindow:destWindow]) {
420     // During certain types of fullscreen transitions, the view that had focus
421     // may have gone away (e.g., the one for a Flash FS widget).  In this case,
422     // FocusTracker will fail to restore focus to anything, so we set the focus
423     // to the tab contents as a reasonable fall-back.
424     [self focusTabContents];
425   }
426   [sourceWindow orderOut:self];
428   // We're done moving focus, so re-enable bar visibility changes.
429   [self enableBarVisibilityUpdates];
432 - (void)permissionBubbleWindowWillClose:(NSNotification*)notification {
433   NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
434   [center removeObserver:self
435                     name:NSWindowWillCloseNotification
436                   object:[notification object]];
437   [self releaseBarVisibilityForOwner:[notification object]
438                        withAnimation:YES
439                                delay:YES];
442 - (void)configurePresentationModeController {
443   BOOL fullscreen_for_tab = browser_->exclusive_access_manager()
444                                 ->fullscreen_controller()
445                                 ->IsWindowFullscreenForTabOrPending();
446   BOOL kiosk_mode =
447       base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kKioskMode);
448   BOOL showDropdown =
449       !fullscreen_for_tab && !kiosk_mode && ([self floatingBarHasFocus]);
451   PermissionBubbleManager* manager = [self permissionBubbleManager];
452   if (manager && manager->IsBubbleVisible()) {
453     NSWindow* bubbleWindow = manager->GetBubbleWindow();
454     DCHECK(bubbleWindow);
455     // A visible permission bubble will force the dropdown to remain
456     // visible.
457     [self lockBarVisibilityForOwner:bubbleWindow withAnimation:NO delay:NO];
458     showDropdown = YES;
459     // Register to be notified when the permission bubble is closed, to
460     // allow fullscreen to hide the dropdown.
461     NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
462     [center addObserver:self
463                selector:@selector(permissionBubbleWindowWillClose:)
464                    name:NSWindowWillCloseNotification
465                  object:bubbleWindow];
466   }
468   NSView* contentView = [[self window] contentView];
469   [presentationModeController_
470       enterPresentationModeForContentView:contentView
471                              showDropdown:showDropdown];
474 - (void)adjustUIForExitingFullscreenAndStopOmniboxSliding {
475   [presentationModeController_ exitPresentationMode];
476   presentationModeController_.reset();
478   // Force the bookmark bar z-order to update.
479   [[bookmarkBarController_ view] removeFromSuperview];
480   [self layoutSubviews];
483 - (void)adjustUIForSlidingFullscreenStyle:(fullscreen_mac::SlidingStyle)style {
484   if (!presentationModeController_) {
485     presentationModeController_.reset(
486         [self newPresentationModeControllerWithStyle:style]);
487     [self configurePresentationModeController];
488   } else {
489     presentationModeController_.get().slidingStyle = style;
490   }
492   if (!floatingBarBackingView_.get() &&
493       ([self hasTabStrip] || [self hasToolbar] || [self hasLocationBar])) {
494     floatingBarBackingView_.reset(
495         [[FloatingBarBackingView alloc] initWithFrame:NSZeroRect]);
496     [floatingBarBackingView_
497         setAutoresizingMask:(NSViewWidthSizable | NSViewMinYMargin)];
498   }
500   // Force the bookmark bar z-order to update.
501   [[bookmarkBarController_ view] removeFromSuperview];
502   [self layoutSubviews];
505 - (PresentationModeController*)newPresentationModeControllerWithStyle:
506     (fullscreen_mac::SlidingStyle)style {
507   return [[PresentationModeController alloc] initWithBrowserController:self
508                                                                  style:style];
511 - (void)enterImmersiveFullscreen {
512   RecordFullscreenWindowLocation([self window]);
513   RecordFullscreenStyle(IMMERSIVE_FULLSCREEN);
515   // Set to NO by |-windowDidEnterFullScreen:|.
516   enteringImmersiveFullscreen_ = YES;
518   // Fade to black.
519   const CGDisplayReservationInterval kFadeDurationSeconds = 0.6;
520   Boolean didFadeOut = NO;
521   CGDisplayFadeReservationToken token;
522   if (CGAcquireDisplayFadeReservation(kFadeDurationSeconds, &token)
523       == kCGErrorSuccess) {
524     didFadeOut = YES;
525     CGDisplayFade(token, kFadeDurationSeconds / 2, kCGDisplayBlendNormal,
526         kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, /*synchronous=*/true);
527   }
529   // Create the fullscreen window.
530   fullscreenWindow_.reset([[self createFullscreenWindow] retain]);
531   savedRegularWindow_ = [[self window] retain];
532   savedRegularWindowFrame_ = [savedRegularWindow_ frame];
534   [self moveViewsForImmersiveFullscreen:YES
535                           regularWindow:[self window]
536                        fullscreenWindow:fullscreenWindow_.get()];
538   fullscreen_mac::SlidingStyle style = fullscreen_mac::OMNIBOX_TABS_HIDDEN;
539   [self adjustUIForSlidingFullscreenStyle:style];
541   // AppKit is helpful and prevents NSWindows from having the same height as
542   // the screen while the menu bar is showing. This only applies to windows on
543   // a secondary screen, in a separate space. Calling [NSWindow
544   // setFrame:display:] with the screen's height will always reduce the
545   // height by the height of the MenuBar. Calling the method with any other
546   // height works fine. The relevant method in the 10.10 AppKit SDK is called:
547   // _canAdjustSizeForScreensHaveSeparateSpacesIfFillingSecondaryScreen
548   //
549   // TODO(erikchen): Refactor the logic to allow the window to be shown after
550   // the menubar has been hidden. This would remove the need for this hack.
551   // http://crbug.com/403203
552   NSRect frame = [[[self window] screen] frame];
553   if (!NSEqualRects(frame, [fullscreenWindow_ frame]))
554     [fullscreenWindow_ setFrame:[[[self window] screen] frame] display:YES];
556   [self layoutSubviews];
558   [self windowDidEnterFullScreen:nil];
560   // Fade back in.
561   if (didFadeOut) {
562     CGDisplayFade(token, kFadeDurationSeconds / 2, kCGDisplayBlendSolidColor,
563         kCGDisplayBlendNormal, 0.0, 0.0, 0.0, /*synchronous=*/false);
564     CGReleaseDisplayFadeReservation(token);
565   }
568 - (void)exitImmersiveFullscreen {
569   // Fade to black.
570   const CGDisplayReservationInterval kFadeDurationSeconds = 0.6;
571   Boolean didFadeOut = NO;
572   CGDisplayFadeReservationToken token;
573   if (CGAcquireDisplayFadeReservation(kFadeDurationSeconds, &token)
574       == kCGErrorSuccess) {
575     didFadeOut = YES;
576     CGDisplayFade(token, kFadeDurationSeconds / 2, kCGDisplayBlendNormal,
577         kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, /*synchronous=*/true);
578   }
580   [self windowWillExitFullScreen:nil];
582   [self moveViewsForImmersiveFullscreen:NO
583                           regularWindow:savedRegularWindow_
584                        fullscreenWindow:fullscreenWindow_.get()];
586   // When exiting fullscreen mode, we need to call layoutSubviews manually.
587   [savedRegularWindow_ autorelease];
588   savedRegularWindow_ = nil;
590   // No close event is thrown when a window is dealloc'd after orderOut.
591   // Explicitly close the window to notify bubbles.
592   [fullscreenWindow_.get() close];
593   fullscreenWindow_.reset();
594   [self layoutSubviews];
596   [self windowDidExitFullScreen:nil];
598   // Fade back in.
599   if (didFadeOut) {
600     CGDisplayFade(token, kFadeDurationSeconds / 2, kCGDisplayBlendSolidColor,
601         kCGDisplayBlendNormal, 0.0, 0.0, 0.0, /*synchronous=*/false);
602     CGReleaseDisplayFadeReservation(token);
603   }
606 - (void)showFullscreenExitBubbleIfNecessary {
607   // This method is called in response to
608   // |-updateFullscreenExitBubbleURL:bubbleType:|. If we're in the middle of the
609   // transition into fullscreen (i.e., using the AppKit Fullscreen API), do not
610   // show the bubble because it will cause visual jank
611   // (http://crbug.com/130649). This will be called again as part of
612   // |-windowDidEnterFullScreen:|, so arrange to do that work then instead.
613   if (enteringAppKitFullscreen_)
614     return;
616   [self hideOverlayIfPossibleWithAnimation:NO delay:NO];
618   if (exclusiveAccessBubbleType_ == EXCLUSIVE_ACCESS_BUBBLE_TYPE_NONE ||
619       exclusiveAccessBubbleType_ ==
620           EXCLUSIVE_ACCESS_BUBBLE_TYPE_BROWSER_FULLSCREEN_EXIT_INSTRUCTION) {
621     // Show no exit instruction bubble on Mac when in Browser Fullscreen.
622     [self destroyFullscreenExitBubbleIfNecessary];
623   } else {
624     [exclusiveAccessBubbleWindowController_ closeImmediately];
625     exclusiveAccessBubbleWindowController_.reset(
626         [[ExclusiveAccessBubbleWindowController alloc]
627                        initWithOwner:self
628             exclusive_access_manager:browser_.get()->exclusive_access_manager()
629                              profile:browser_.get()->profile()
630                                  url:fullscreenUrl_
631                           bubbleType:exclusiveAccessBubbleType_]);
632     [exclusiveAccessBubbleWindowController_ showWindow];
633   }
636 - (void)destroyFullscreenExitBubbleIfNecessary {
637   [exclusiveAccessBubbleWindowController_ closeImmediately];
638   exclusiveAccessBubbleWindowController_.reset();
641 - (void)contentViewDidResize:(NSNotification*)notification {
642   [self layoutSubviews];
645 - (void)registerForContentViewResizeNotifications {
646   [[NSNotificationCenter defaultCenter]
647       addObserver:self
648          selector:@selector(contentViewDidResize:)
649              name:NSViewFrameDidChangeNotification
650            object:[[self window] contentView]];
653 - (void)deregisterForContentViewResizeNotifications {
654   [[NSNotificationCenter defaultCenter]
655       removeObserver:self
656                 name:NSViewFrameDidChangeNotification
657               object:[[self window] contentView]];
660 - (NSSize)window:(NSWindow*)window
661     willUseFullScreenContentSize:(NSSize)proposedSize {
662   return proposedSize;
665 - (NSApplicationPresentationOptions)window:(NSWindow*)window
666     willUseFullScreenPresentationOptions:(NSApplicationPresentationOptions)opt {
667   return (opt |
668           NSApplicationPresentationAutoHideDock |
669           NSApplicationPresentationAutoHideMenuBar);
672 - (void)windowWillEnterFullScreen:(NSNotification*)notification {
673   RecordFullscreenWindowLocation([self window]);
674   RecordFullscreenStyle(enteringPresentationMode_ ? PRESENTATION_MODE
675                                                   : CANONICAL_FULLSCREEN);
677   if (notification)  // For System Fullscreen when non-nil.
678     [self registerForContentViewResizeNotifications];
680   NSWindow* window = [self window];
681   savedRegularWindowFrame_ = [window frame];
682   BOOL mode = enteringPresentationMode_ ||
683               browser_->exclusive_access_manager()
684                   ->fullscreen_controller()
685                   ->IsWindowFullscreenForTabOrPending();
686   enteringAppKitFullscreen_ = YES;
687   enteringAppKitFullscreenOnPrimaryScreen_ =
688       [[[self window] screen] isEqual:[[NSScreen screens] objectAtIndex:0]];
690   fullscreen_mac::SlidingStyle style =
691       mode ? fullscreen_mac::OMNIBOX_TABS_HIDDEN
692            : fullscreen_mac::OMNIBOX_TABS_PRESENT;
694   [self adjustUIForSlidingFullscreenStyle:style];
697 - (void)windowDidEnterFullScreen:(NSNotification*)notification {
698   fullscreenTransition_.reset();
700   // In Yosemite, some combination of the titlebar and toolbar always show in
701   // full-screen mode. We do not want either to show. Search for the window that
702   // contains the views, and hide it. There is no need to ever unhide the view.
703   // http://crbug.com/380235
704   if (base::mac::IsOSYosemiteOrLater()) {
705     for (NSWindow* window in [[NSApplication sharedApplication] windows]) {
706       if ([window
707               isKindOfClass:NSClassFromString(@"NSToolbarFullScreenWindow")]) {
708         [[window contentView] setHidden:YES];
709       }
710     }
711   }
713   if ([self shouldUseMavericksAppKitFullscreenHack]) {
714     // Apply a hack to fix the size of the window. This is the last run of the
715     // MessageLoop where the hack will not work, so dispatch the hack to the
716     // top of the MessageLoop.
717     base::Callback<void(void)> callback = base::BindBlock(^{
718         if (![self isInAppKitFullscreen])
719           return;
721         // The window's frame should be exactly 22 points too short.
722         CGFloat kExpectedHeightDifference = 22;
723         NSRect currentFrame = [[self window] frame];
724         NSRect expectedFrame = [[[self window] screen] frame];
725         if (!NSEqualPoints(currentFrame.origin, expectedFrame.origin))
726           return;
727         if (currentFrame.size.width != expectedFrame.size.width)
728           return;
729         CGFloat heightDelta =
730             expectedFrame.size.height - currentFrame.size.height;
731         if (fabs(heightDelta - kExpectedHeightDifference) > 0.01)
732           return;
734         [[self window] setFrame:expectedFrame display:YES];
735     });
736     base::MessageLoop::current()->PostTask(FROM_HERE, callback);
737   }
739   if (notification)  // For System Fullscreen when non-nil.
740     [self deregisterForContentViewResizeNotifications];
741   enteringAppKitFullscreen_ = NO;
742   enteringImmersiveFullscreen_ = NO;
743   enteringPresentationMode_ = NO;
745   [self showFullscreenExitBubbleIfNecessary];
746   browser_->WindowFullscreenStateChanged();
749 - (void)windowWillExitFullScreen:(NSNotification*)notification {
750   if (notification)  // For System Fullscreen when non-nil.
751     [self registerForContentViewResizeNotifications];
752   exitingAppKitFullscreen_ = YES;
754   [self destroyFullscreenExitBubbleIfNecessary];
755   [self adjustUIForExitingFullscreenAndStopOmniboxSliding];
758 - (void)windowDidExitFullScreen:(NSNotification*)notification {
759   DCHECK(exitingAppKitFullscreen_);
761   if (notification)  // For System Fullscreen when non-nil.
762     [self deregisterForContentViewResizeNotifications];
764   // Since the content view was forcefully resized during the transition, we
765   // want to ensure that the subviews are layout correctly after it ended.
766   [self layoutSubviews];
767   browser_->WindowFullscreenStateChanged();
769   exitingAppKitFullscreen_ = NO;
770   fullscreenTransition_.reset();
773 - (void)windowDidFailToEnterFullScreen:(NSWindow*)window {
774   [self deregisterForContentViewResizeNotifications];
775   enteringAppKitFullscreen_ = NO;
776   fullscreenTransition_.reset();
777   [self adjustUIForExitingFullscreenAndStopOmniboxSliding];
780 - (void)windowDidFailToExitFullScreen:(NSWindow*)window {
781   [self deregisterForContentViewResizeNotifications];
782   exitingAppKitFullscreen_ = NO;
783   fullscreenTransition_.reset();
784   // Force a relayout to try and get the window back into a reasonable state.
785   [self layoutSubviews];
788 - (void)enableBarVisibilityUpdates {
789   // Early escape if there's nothing to do.
790   if (barVisibilityUpdatesEnabled_)
791     return;
793   barVisibilityUpdatesEnabled_ = YES;
795   if ([barVisibilityLocks_ count])
796     [presentationModeController_ ensureOverlayShownWithAnimation:NO delay:NO];
797   else
798     [presentationModeController_ ensureOverlayHiddenWithAnimation:NO delay:NO];
801 - (void)disableBarVisibilityUpdates {
802   // Early escape if there's nothing to do.
803   if (!barVisibilityUpdatesEnabled_)
804     return;
806   barVisibilityUpdatesEnabled_ = NO;
807   [presentationModeController_ cancelAnimationAndTimers];
810 - (void)hideOverlayIfPossibleWithAnimation:(BOOL)animation delay:(BOOL)delay {
811   if (!barVisibilityUpdatesEnabled_ || [barVisibilityLocks_ count])
812     return;
813   [presentationModeController_ ensureOverlayHiddenWithAnimation:animation
814                                                           delay:delay];
817 - (CGFloat)toolbarDividerOpacity {
818   return [bookmarkBarController_ toolbarDividerOpacity];
821 - (void)updateInfoBarTipVisibility {
822   // If there's no toolbar then hide the infobar tip.
823   [infoBarContainerController_
824       setShouldSuppressTopInfoBarTip:![self hasToolbar]];
827 - (NSInteger)pageInfoBubblePointY {
828   LocationBarViewMac* locationBarView = [self locationBarBridge];
830   // The point, in window coordinates.
831   NSPoint iconBottom = locationBarView->GetPageInfoBubblePoint();
833   // The toolbar, in window coordinates.
834   NSView* toolbar = [toolbarController_ view];
835   CGFloat toolbarY = NSMinY([toolbar convertRect:[toolbar bounds] toView:nil]);
837   return iconBottom.y - toolbarY;
840 - (void)enterAppKitFullscreen {
841   DCHECK(base::mac::IsOSLionOrLater());
842   if (FramedBrowserWindow* framedBrowserWindow =
843           base::mac::ObjCCast<FramedBrowserWindow>([self window])) {
844     [framedBrowserWindow toggleSystemFullScreen];
845   }
848 - (void)exitAppKitFullscreen {
849   DCHECK(base::mac::IsOSLionOrLater());
850   if (FramedBrowserWindow* framedBrowserWindow =
851           base::mac::ObjCCast<FramedBrowserWindow>([self window])) {
852     [framedBrowserWindow toggleSystemFullScreen];
853   }
856 - (NSRect)fullscreenButtonFrame {
857   // NSWindowFullScreenButton is 10.7+ and results in log spam on 10.6 if used.
858   if (base::mac::IsOSSnowLeopard())
859     return NSZeroRect;
861   NSButton* fullscreenButton =
862       [[self window] standardWindowButton:NSWindowFullScreenButton];
863   if (!fullscreenButton)
864     return NSZeroRect;
866   NSRect buttonFrame = [fullscreenButton frame];
868   // When called from -windowWillExitFullScreen:, the button's frame may not
869   // be updated yet to match the new window size.
870   // We need to return where the button should be positioned.
871   NSView* rootView = [[[self window] contentView] superview];
872   if ([rootView respondsToSelector:@selector(_fullScreenButtonOrigin)])
873     buttonFrame.origin = [rootView _fullScreenButtonOrigin];
875   return buttonFrame;
878 - (void)updateLayoutParameters:(BrowserWindowLayout*)layout {
879   [layout setContentViewSize:[[[self window] contentView] bounds].size];
881   NSSize windowSize = (fullscreenTransition_.get())
882                           ? [fullscreenTransition_ desiredWindowLayoutSize]
883                           : [[self window] frame].size;
885   [layout setWindowSize:windowSize];
887   [layout setInAnyFullscreen:[self isInAnyFullscreenMode]];
888   [layout setFullscreenSlidingStyle:
889       presentationModeController_.get().slidingStyle];
890   [layout setFullscreenMenubarOffset:
891       [presentationModeController_ menubarOffset]];
892   [layout setFullscreenToolbarFraction:
893       [presentationModeController_ toolbarFraction]];
895   [layout setHasTabStrip:[self hasTabStrip]];
896   [layout setFullscreenButtonFrame:[self fullscreenButtonFrame]];
898   if ([self shouldShowAvatar]) {
899     NSView* avatar = [avatarButtonController_ view];
900     [layout setShouldShowAvatar:YES];
901     [layout setShouldUseNewAvatar:[self shouldUseNewAvatarButton]];
902     [layout setAvatarSize:[avatar frame].size];
903     [layout setAvatarLineWidth:[[avatar superview] cr_lineWidth]];
904   }
906   [layout setHasToolbar:[self hasToolbar]];
907   [layout setToolbarHeight:NSHeight([[toolbarController_ view] bounds])];
909   [layout setHasLocationBar:[self hasLocationBar]];
911   [layout setPlaceBookmarkBarBelowInfoBar:[self placeBookmarkBarBelowInfoBar]];
912   [layout setBookmarkBarHidden:[bookmarkBarController_ view].isHidden];
913   [layout setBookmarkBarHeight:
914       NSHeight([[bookmarkBarController_ view] bounds])];
916   [layout setInfoBarHeight:[infoBarContainerController_ heightOfInfoBars]];
917   [layout setPageInfoBubblePointY:[self pageInfoBubblePointY]];
919   [layout setHasDownloadShelf:(downloadShelfController_.get() != nil)];
920   [layout setDownloadShelfHeight:
921       NSHeight([[downloadShelfController_ view] bounds])];
924 - (void)applyLayout:(BrowserWindowLayout*)layout {
925   chrome::LayoutOutput output = [layout computeLayout];
927   if (!NSIsEmptyRect(output.tabStripLayout.frame))
928     [self applyTabStripLayout:output.tabStripLayout];
930   if (!NSIsEmptyRect(output.toolbarFrame))
931     [[toolbarController_ view] setFrame:output.toolbarFrame];
933   if (!NSIsEmptyRect(output.bookmarkFrame)) {
934     NSView* bookmarkBarView = [bookmarkBarController_ view];
935     [bookmarkBarView setFrame:output.bookmarkFrame];
937     // Pin the bookmark bar to the top of the window and make the width
938     // flexible.
939     [bookmarkBarView setAutoresizingMask:NSViewWidthSizable | NSViewMinYMargin];
941     [bookmarkBarController_ layoutSubviews];
942   }
944   // The info bar is never hidden. Sometimes it has zero effective height.
945   [[infoBarContainerController_ view] setFrame:output.infoBarFrame];
946   [infoBarContainerController_
947       setMaxTopArrowHeight:output.infoBarMaxTopArrowHeight];
948   [infoBarContainerController_
949       setInfobarArrowX:[self locationBarBridge]->GetPageInfoBubblePoint().x];
951   if (!NSIsEmptyRect(output.downloadShelfFrame))
952     [[downloadShelfController_ view] setFrame:output.downloadShelfFrame];
954   [self layoutTabContentArea:output.contentAreaFrame];
956   if (!NSIsEmptyRect(output.fullscreenBackingBarFrame)) {
957     [floatingBarBackingView_ setFrame:output.fullscreenBackingBarFrame];
958     [presentationModeController_
959         overlayFrameChanged:output.fullscreenBackingBarFrame];
960   }
962   [findBarCocoaController_
963       positionFindBarViewAtMaxY:output.findBarMaxY
964                        maxWidth:NSWidth(output.contentAreaFrame)];
966   [exclusiveAccessBubbleWindowController_
967       positionInWindowAtTop:output.fullscreenExitButtonMaxY
968                       width:NSWidth(output.contentAreaFrame)];
971 - (void)updateSubviewZOrder {
972   if ([self isInAnyFullscreenMode])
973     [self updateSubviewZOrderFullscreen];
974   else
975     [self updateSubviewZOrderNormal];
978 - (void)updateSubviewZOrderNormal {
979   base::scoped_nsobject<NSMutableArray> subviews([[NSMutableArray alloc] init]);
980   if ([downloadShelfController_ view])
981     [subviews addObject:[downloadShelfController_ view]];
982   if ([bookmarkBarController_ view])
983     [subviews addObject:[bookmarkBarController_ view]];
984   if ([toolbarController_ view])
985     [subviews addObject:[toolbarController_ view]];
986   if ([infoBarContainerController_ view])
987     [subviews addObject:[infoBarContainerController_ view]];
988   if ([self tabContentArea])
989     [subviews addObject:[self tabContentArea]];
990   if ([findBarCocoaController_ view])
991     [subviews addObject:[findBarCocoaController_ view]];
993   [self setContentViewSubviews:subviews];
996 - (void)updateSubviewZOrderFullscreen {
997   base::scoped_nsobject<NSMutableArray> subviews([[NSMutableArray alloc] init]);
998   if ([downloadShelfController_ view])
999     [subviews addObject:[downloadShelfController_ view]];
1000   if ([self tabContentArea])
1001     [subviews addObject:[self tabContentArea]];
1002   if ([self placeBookmarkBarBelowInfoBar]) {
1003     if ([bookmarkBarController_ view])
1004       [subviews addObject:[bookmarkBarController_ view]];
1005     if (floatingBarBackingView_)
1006       [subviews addObject:floatingBarBackingView_];
1007   } else {
1008     if (floatingBarBackingView_)
1009       [subviews addObject:floatingBarBackingView_];
1010     if ([bookmarkBarController_ view])
1011       [subviews addObject:[bookmarkBarController_ view]];
1012   }
1013   if ([toolbarController_ view])
1014     [subviews addObject:[toolbarController_ view]];
1015   if ([infoBarContainerController_ view])
1016     [subviews addObject:[infoBarContainerController_ view]];
1017   if ([findBarCocoaController_ view])
1018     [subviews addObject:[findBarCocoaController_ view]];
1020   [self setContentViewSubviews:subviews];
1023 - (void)setContentViewSubviews:(NSArray*)subviews {
1024   // Subviews already match.
1025   if ([[self.chromeContentView subviews] isEqual:subviews])
1026     return;
1028   // The tabContentArea isn't a subview, so just set all the subviews.
1029   NSView* tabContentArea = [self tabContentArea];
1030   if (![[self.chromeContentView subviews] containsObject:tabContentArea]) {
1031     [self.chromeContentView setSubviews:subviews];
1032     return;
1033   }
1035   // Remove all subviews that aren't the tabContentArea.
1036   for (NSView* view in [[[self.chromeContentView subviews] copy] autorelease]) {
1037     if (view != tabContentArea)
1038       [view removeFromSuperview];
1039   }
1041   // Add in the subviews below the tabContentArea.
1042   NSInteger index = [subviews indexOfObject:tabContentArea];
1043   for (int i = index - 1; i >= 0; --i) {
1044     NSView* view = [subviews objectAtIndex:i];
1045     [self.chromeContentView addSubview:view
1046                             positioned:NSWindowBelow
1047                             relativeTo:nil];
1048   }
1050   // Add in the subviews above the tabContentArea.
1051   for (NSUInteger i = index + 1; i < [subviews count]; ++i) {
1052     NSView* view = [subviews objectAtIndex:i];
1053     [self.chromeContentView addSubview:view
1054                             positioned:NSWindowAbove
1055                             relativeTo:nil];
1056   }
1059 + (BOOL)systemSettingsRequireMavericksAppKitFullscreenHack {
1060   if (!base::mac::IsOSMavericks())
1061     return NO;
1062   return [NSScreen respondsToSelector:@selector(screensHaveSeparateSpaces)] &&
1063          [NSScreen screensHaveSeparateSpaces];
1066 - (BOOL)shouldUseMavericksAppKitFullscreenHack {
1067   if (![[self class] systemSettingsRequireMavericksAppKitFullscreenHack])
1068     return NO;
1069   if (!enteringAppKitFullscreen_)
1070     return NO;
1071   if (enteringAppKitFullscreenOnPrimaryScreen_)
1072     return NO;
1074   return YES;
1077 - (BOOL)shouldUseCustomAppKitFullscreenTransition {
1078   if (base::mac::IsOSMountainLionOrEarlier())
1079     return NO;
1081   NSView* root = [[self.window contentView] superview];
1082   if (!root.layer)
1083     return NO;
1085   // AppKit on OSX 10.9 has a bug for applications linked against OSX 10.8 SDK
1086   // and earlier. Under specific circumstances, it prevents the custom AppKit
1087   // transition from working well. See http://crbug.com/396980 for more
1088   // details.
1089   if ([[self class] systemSettingsRequireMavericksAppKitFullscreenHack] &&
1090       ![[[self window] screen] isEqual:[[NSScreen screens] objectAtIndex:0]]) {
1091     return NO;
1092   }
1094   return YES;
1097 - (NSArray*)customWindowsToEnterFullScreenForWindow:(NSWindow*)window {
1098   DCHECK([window isEqual:self.window]);
1100   if (![self shouldUseCustomAppKitFullscreenTransition])
1101     return nil;
1103   FramedBrowserWindow* framedBrowserWindow =
1104       base::mac::ObjCCast<FramedBrowserWindow>([self window]);
1105   fullscreenTransition_.reset([[BrowserWindowFullscreenTransition alloc]
1106       initEnterWithWindow:framedBrowserWindow]);
1107   return [fullscreenTransition_ customWindowsForFullScreenTransition];
1110 - (NSArray*)customWindowsToExitFullScreenForWindow:(NSWindow*)window {
1111   DCHECK([window isEqual:self.window]);
1113   if (![self shouldUseCustomAppKitFullscreenTransition])
1114     return nil;
1116   FramedBrowserWindow* framedBrowserWindow =
1117       base::mac::ObjCCast<FramedBrowserWindow>([self window]);
1118   fullscreenTransition_.reset([[BrowserWindowFullscreenTransition alloc]
1119       initExitWithWindow:framedBrowserWindow
1120                    frame:savedRegularWindowFrame_]);
1122   return [fullscreenTransition_ customWindowsForFullScreenTransition];
1125 - (void)window:(NSWindow*)window
1126     startCustomAnimationToEnterFullScreenWithDuration:(NSTimeInterval)duration {
1127   DCHECK([window isEqual:self.window]);
1128   [fullscreenTransition_ startCustomFullScreenAnimationWithDuration:duration];
1131 - (void)window:(NSWindow*)window
1132     startCustomAnimationToExitFullScreenWithDuration:(NSTimeInterval)duration {
1133   DCHECK([window isEqual:self.window]);
1135   [fullscreenTransition_ startCustomFullScreenAnimationWithDuration:duration];
1138 - (BOOL)shouldConstrainFrameRect {
1139   if ([fullscreenTransition_ shouldWindowBeUnconstrained])
1140     return NO;
1142   return [super shouldConstrainFrameRect];
1145 - (WebContents*)webContents {
1146   return browser_->tab_strip_model()->GetActiveWebContents();
1149 - (PermissionBubbleManager*)permissionBubbleManager {
1150   if (WebContents* contents = [self webContents])
1151     return PermissionBubbleManager::FromWebContents(contents);
1152   return nil;
1155 @end  // @implementation BrowserWindowController(Private)