BookmarkManager: Fix 'new folder text field size changes on clicking it' issue.
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / browser_window_controller_private.mm
blob99ef304ce6729e554005c42a8fe0fa3db73f2f27
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_NONE;
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];
683   enteringAppKitFullscreen_ = YES;
684   enteringAppKitFullscreenOnPrimaryScreen_ =
685       [[[self window] screen] isEqual:[[NSScreen screens] objectAtIndex:0]];
687   fullscreen_mac::SlidingStyle style;
688   if (browser_->exclusive_access_manager()
689           ->fullscreen_controller()
690           ->IsWindowFullscreenForTabOrPending()) {
691     style = fullscreen_mac::OMNIBOX_TABS_NONE;
692   } else if (enteringPresentationMode_) {
693     style = fullscreen_mac::OMNIBOX_TABS_HIDDEN;
694   } else {
695     style = fullscreen_mac::OMNIBOX_TABS_PRESENT;
696   }
698   [self adjustUIForSlidingFullscreenStyle:style];
701 - (void)windowDidEnterFullScreen:(NSNotification*)notification {
702   fullscreenTransition_.reset();
704   // In Yosemite, some combination of the titlebar and toolbar always show in
705   // full-screen mode. We do not want either to show. Search for the window that
706   // contains the views, and hide it. There is no need to ever unhide the view.
707   // http://crbug.com/380235
708   if (base::mac::IsOSYosemiteOrLater()) {
709     for (NSWindow* window in [[NSApplication sharedApplication] windows]) {
710       if ([window
711               isKindOfClass:NSClassFromString(@"NSToolbarFullScreenWindow")]) {
712         [[window contentView] setHidden:YES];
713       }
714     }
715   }
717   if ([self shouldUseMavericksAppKitFullscreenHack]) {
718     // Apply a hack to fix the size of the window. This is the last run of the
719     // MessageLoop where the hack will not work, so dispatch the hack to the
720     // top of the MessageLoop.
721     base::Callback<void(void)> callback = base::BindBlock(^{
722         if (![self isInAppKitFullscreen])
723           return;
725         // The window's frame should be exactly 22 points too short.
726         CGFloat kExpectedHeightDifference = 22;
727         NSRect currentFrame = [[self window] frame];
728         NSRect expectedFrame = [[[self window] screen] frame];
729         if (!NSEqualPoints(currentFrame.origin, expectedFrame.origin))
730           return;
731         if (currentFrame.size.width != expectedFrame.size.width)
732           return;
733         CGFloat heightDelta =
734             expectedFrame.size.height - currentFrame.size.height;
735         if (fabs(heightDelta - kExpectedHeightDifference) > 0.01)
736           return;
738         [[self window] setFrame:expectedFrame display:YES];
739     });
740     base::MessageLoop::current()->PostTask(FROM_HERE, callback);
741   }
743   if (notification)  // For System Fullscreen when non-nil.
744     [self deregisterForContentViewResizeNotifications];
745   enteringAppKitFullscreen_ = NO;
746   enteringImmersiveFullscreen_ = NO;
747   enteringPresentationMode_ = NO;
749   [self showFullscreenExitBubbleIfNecessary];
750   browser_->WindowFullscreenStateChanged();
753 - (void)windowWillExitFullScreen:(NSNotification*)notification {
754   if (notification)  // For System Fullscreen when non-nil.
755     [self registerForContentViewResizeNotifications];
756   exitingAppKitFullscreen_ = YES;
758   [self destroyFullscreenExitBubbleIfNecessary];
759   [self adjustUIForExitingFullscreenAndStopOmniboxSliding];
762 - (void)windowDidExitFullScreen:(NSNotification*)notification {
763   DCHECK(exitingAppKitFullscreen_);
765   if (notification)  // For System Fullscreen when non-nil.
766     [self deregisterForContentViewResizeNotifications];
768   // Since the content view was forcefully resized during the transition, we
769   // want to ensure that the subviews are layout correctly after it ended.
770   [self layoutSubviews];
771   browser_->WindowFullscreenStateChanged();
773   exitingAppKitFullscreen_ = NO;
774   fullscreenTransition_.reset();
777 - (void)windowDidFailToEnterFullScreen:(NSWindow*)window {
778   [self deregisterForContentViewResizeNotifications];
779   enteringAppKitFullscreen_ = NO;
780   fullscreenTransition_.reset();
781   [self adjustUIForExitingFullscreenAndStopOmniboxSliding];
784 - (void)windowDidFailToExitFullScreen:(NSWindow*)window {
785   [self deregisterForContentViewResizeNotifications];
786   exitingAppKitFullscreen_ = NO;
787   fullscreenTransition_.reset();
788   // Force a relayout to try and get the window back into a reasonable state.
789   [self layoutSubviews];
792 - (void)enableBarVisibilityUpdates {
793   // Early escape if there's nothing to do.
794   if (barVisibilityUpdatesEnabled_)
795     return;
797   barVisibilityUpdatesEnabled_ = YES;
799   if ([barVisibilityLocks_ count])
800     [presentationModeController_ ensureOverlayShownWithAnimation:NO delay:NO];
801   else
802     [presentationModeController_ ensureOverlayHiddenWithAnimation:NO delay:NO];
805 - (void)disableBarVisibilityUpdates {
806   // Early escape if there's nothing to do.
807   if (!barVisibilityUpdatesEnabled_)
808     return;
810   barVisibilityUpdatesEnabled_ = NO;
811   [presentationModeController_ cancelAnimationAndTimers];
814 - (void)hideOverlayIfPossibleWithAnimation:(BOOL)animation delay:(BOOL)delay {
815   if (!barVisibilityUpdatesEnabled_ || [barVisibilityLocks_ count])
816     return;
817   [presentationModeController_ ensureOverlayHiddenWithAnimation:animation
818                                                           delay:delay];
821 - (CGFloat)toolbarDividerOpacity {
822   return [bookmarkBarController_ toolbarDividerOpacity];
825 - (void)updateInfoBarTipVisibility {
826   // If there's no toolbar then hide the infobar tip.
827   [infoBarContainerController_
828       setShouldSuppressTopInfoBarTip:![self hasToolbar]];
831 - (NSInteger)pageInfoBubblePointY {
832   LocationBarViewMac* locationBarView = [self locationBarBridge];
834   // The point, in window coordinates.
835   NSPoint iconBottom = locationBarView->GetPageInfoBubblePoint();
837   // The toolbar, in window coordinates.
838   NSView* toolbar = [toolbarController_ view];
839   CGFloat toolbarY = NSMinY([toolbar convertRect:[toolbar bounds] toView:nil]);
841   return iconBottom.y - toolbarY;
844 - (void)enterAppKitFullscreen {
845   DCHECK(base::mac::IsOSLionOrLater());
846   if (FramedBrowserWindow* framedBrowserWindow =
847           base::mac::ObjCCast<FramedBrowserWindow>([self window])) {
848     [framedBrowserWindow toggleSystemFullScreen];
849   }
852 - (void)exitAppKitFullscreen {
853   DCHECK(base::mac::IsOSLionOrLater());
854   if (FramedBrowserWindow* framedBrowserWindow =
855           base::mac::ObjCCast<FramedBrowserWindow>([self window])) {
856     [framedBrowserWindow toggleSystemFullScreen];
857   }
860 - (NSRect)fullscreenButtonFrame {
861   // NSWindowFullScreenButton is 10.7+ and results in log spam on 10.6 if used.
862   if (base::mac::IsOSSnowLeopard())
863     return NSZeroRect;
865   NSButton* fullscreenButton =
866       [[self window] standardWindowButton:NSWindowFullScreenButton];
867   if (!fullscreenButton)
868     return NSZeroRect;
870   NSRect buttonFrame = [fullscreenButton frame];
872   // When called from -windowWillExitFullScreen:, the button's frame may not
873   // be updated yet to match the new window size.
874   // We need to return where the button should be positioned.
875   NSView* rootView = [[[self window] contentView] superview];
876   if ([rootView respondsToSelector:@selector(_fullScreenButtonOrigin)])
877     buttonFrame.origin = [rootView _fullScreenButtonOrigin];
879   return buttonFrame;
882 - (void)updateLayoutParameters:(BrowserWindowLayout*)layout {
883   [layout setContentViewSize:[[[self window] contentView] bounds].size];
885   NSSize windowSize = (fullscreenTransition_.get())
886                           ? [fullscreenTransition_ desiredWindowLayoutSize]
887                           : [[self window] frame].size;
889   [layout setWindowSize:windowSize];
891   [layout setInAnyFullscreen:[self isInAnyFullscreenMode]];
892   [layout setFullscreenSlidingStyle:
893       presentationModeController_.get().slidingStyle];
894   [layout setFullscreenMenubarOffset:
895       [presentationModeController_ menubarOffset]];
896   [layout setFullscreenToolbarFraction:
897       [presentationModeController_ toolbarFraction]];
899   [layout setHasTabStrip:[self hasTabStrip]];
900   [layout setFullscreenButtonFrame:[self fullscreenButtonFrame]];
902   if ([self shouldShowAvatar]) {
903     NSView* avatar = [avatarButtonController_ view];
904     [layout setShouldShowAvatar:YES];
905     [layout setShouldUseNewAvatar:[self shouldUseNewAvatarButton]];
906     [layout setAvatarSize:[avatar frame].size];
907     [layout setAvatarLineWidth:[[avatar superview] cr_lineWidth]];
908   }
910   [layout setHasToolbar:[self hasToolbar]];
911   [layout setToolbarHeight:NSHeight([[toolbarController_ view] bounds])];
913   [layout setHasLocationBar:[self hasLocationBar]];
915   [layout setPlaceBookmarkBarBelowInfoBar:[self placeBookmarkBarBelowInfoBar]];
916   [layout setBookmarkBarHidden:[bookmarkBarController_ view].isHidden];
917   [layout setBookmarkBarHeight:
918       NSHeight([[bookmarkBarController_ view] bounds])];
920   [layout setInfoBarHeight:[infoBarContainerController_ heightOfInfoBars]];
921   [layout setPageInfoBubblePointY:[self pageInfoBubblePointY]];
923   [layout setHasDownloadShelf:(downloadShelfController_.get() != nil)];
924   [layout setDownloadShelfHeight:
925       NSHeight([[downloadShelfController_ view] bounds])];
928 - (void)applyLayout:(BrowserWindowLayout*)layout {
929   chrome::LayoutOutput output = [layout computeLayout];
931   if (!NSIsEmptyRect(output.tabStripLayout.frame))
932     [self applyTabStripLayout:output.tabStripLayout];
934   if (!NSIsEmptyRect(output.toolbarFrame))
935     [[toolbarController_ view] setFrame:output.toolbarFrame];
937   if (!NSIsEmptyRect(output.bookmarkFrame)) {
938     NSView* bookmarkBarView = [bookmarkBarController_ view];
939     [bookmarkBarView setFrame:output.bookmarkFrame];
941     // Pin the bookmark bar to the top of the window and make the width
942     // flexible.
943     [bookmarkBarView setAutoresizingMask:NSViewWidthSizable | NSViewMinYMargin];
945     [bookmarkBarController_ layoutSubviews];
946   }
948   // The info bar is never hidden. Sometimes it has zero effective height.
949   [[infoBarContainerController_ view] setFrame:output.infoBarFrame];
950   [infoBarContainerController_
951       setMaxTopArrowHeight:output.infoBarMaxTopArrowHeight];
952   [infoBarContainerController_
953       setInfobarArrowX:[self locationBarBridge]->GetPageInfoBubblePoint().x];
955   if (!NSIsEmptyRect(output.downloadShelfFrame))
956     [[downloadShelfController_ view] setFrame:output.downloadShelfFrame];
958   [self layoutTabContentArea:output.contentAreaFrame];
960   if (!NSIsEmptyRect(output.fullscreenBackingBarFrame)) {
961     [floatingBarBackingView_ setFrame:output.fullscreenBackingBarFrame];
962     [presentationModeController_
963         overlayFrameChanged:output.fullscreenBackingBarFrame];
964   }
966   [findBarCocoaController_
967       positionFindBarViewAtMaxY:output.findBarMaxY
968                        maxWidth:NSWidth(output.contentAreaFrame)];
970   [exclusiveAccessBubbleWindowController_
971       positionInWindowAtTop:output.fullscreenExitButtonMaxY
972                       width:NSWidth(output.contentAreaFrame)];
975 - (void)updateSubviewZOrder {
976   if ([self isInAnyFullscreenMode])
977     [self updateSubviewZOrderFullscreen];
978   else
979     [self updateSubviewZOrderNormal];
982 - (void)updateSubviewZOrderNormal {
983   base::scoped_nsobject<NSMutableArray> subviews([[NSMutableArray alloc] init]);
984   if ([downloadShelfController_ view])
985     [subviews addObject:[downloadShelfController_ view]];
986   if ([bookmarkBarController_ view])
987     [subviews addObject:[bookmarkBarController_ view]];
988   if ([toolbarController_ view])
989     [subviews addObject:[toolbarController_ view]];
990   if ([infoBarContainerController_ view])
991     [subviews addObject:[infoBarContainerController_ view]];
992   if ([self tabContentArea])
993     [subviews addObject:[self tabContentArea]];
994   if ([findBarCocoaController_ view])
995     [subviews addObject:[findBarCocoaController_ view]];
997   [self setContentViewSubviews:subviews];
1000 - (void)updateSubviewZOrderFullscreen {
1001   base::scoped_nsobject<NSMutableArray> subviews([[NSMutableArray alloc] init]);
1002   if ([downloadShelfController_ view])
1003     [subviews addObject:[downloadShelfController_ view]];
1004   if ([self tabContentArea])
1005     [subviews addObject:[self tabContentArea]];
1006   if ([self placeBookmarkBarBelowInfoBar]) {
1007     if ([bookmarkBarController_ view])
1008       [subviews addObject:[bookmarkBarController_ view]];
1009     if (floatingBarBackingView_)
1010       [subviews addObject:floatingBarBackingView_];
1011   } else {
1012     if (floatingBarBackingView_)
1013       [subviews addObject:floatingBarBackingView_];
1014     if ([bookmarkBarController_ view])
1015       [subviews addObject:[bookmarkBarController_ view]];
1016   }
1017   if ([toolbarController_ view])
1018     [subviews addObject:[toolbarController_ view]];
1019   if ([infoBarContainerController_ view])
1020     [subviews addObject:[infoBarContainerController_ view]];
1021   if ([findBarCocoaController_ view])
1022     [subviews addObject:[findBarCocoaController_ view]];
1024   [self setContentViewSubviews:subviews];
1027 - (void)setContentViewSubviews:(NSArray*)subviews {
1028   // Subviews already match.
1029   if ([[self.chromeContentView subviews] isEqual:subviews])
1030     return;
1032   // The tabContentArea isn't a subview, so just set all the subviews.
1033   NSView* tabContentArea = [self tabContentArea];
1034   if (![[self.chromeContentView subviews] containsObject:tabContentArea]) {
1035     [self.chromeContentView setSubviews:subviews];
1036     return;
1037   }
1039   // Remove all subviews that aren't the tabContentArea.
1040   for (NSView* view in [[[self.chromeContentView subviews] copy] autorelease]) {
1041     if (view != tabContentArea)
1042       [view removeFromSuperview];
1043   }
1045   // Add in the subviews below the tabContentArea.
1046   NSInteger index = [subviews indexOfObject:tabContentArea];
1047   for (int i = index - 1; i >= 0; --i) {
1048     NSView* view = [subviews objectAtIndex:i];
1049     [self.chromeContentView addSubview:view
1050                             positioned:NSWindowBelow
1051                             relativeTo:nil];
1052   }
1054   // Add in the subviews above the tabContentArea.
1055   for (NSUInteger i = index + 1; i < [subviews count]; ++i) {
1056     NSView* view = [subviews objectAtIndex:i];
1057     [self.chromeContentView addSubview:view
1058                             positioned:NSWindowAbove
1059                             relativeTo:nil];
1060   }
1063 + (BOOL)systemSettingsRequireMavericksAppKitFullscreenHack {
1064   if (!base::mac::IsOSMavericks())
1065     return NO;
1066   return [NSScreen respondsToSelector:@selector(screensHaveSeparateSpaces)] &&
1067          [NSScreen screensHaveSeparateSpaces];
1070 - (BOOL)shouldUseMavericksAppKitFullscreenHack {
1071   if (![[self class] systemSettingsRequireMavericksAppKitFullscreenHack])
1072     return NO;
1073   if (!enteringAppKitFullscreen_)
1074     return NO;
1075   if (enteringAppKitFullscreenOnPrimaryScreen_)
1076     return NO;
1078   return YES;
1081 - (BOOL)shouldUseCustomAppKitFullscreenTransition:(BOOL)enterFullScreen {
1082   // We are temporary disabling exit fullscreen animation because it only
1083   // works on OSX 10.10.
1084   // TODO(spqchan): Fix exit fullscreen animation so that it works on all
1085   // OSX versions.
1086   if (!enterFullScreen)
1087     return NO;
1089   if (base::mac::IsOSMountainLionOrEarlier())
1090     return NO;
1092   NSView* root = [[self.window contentView] superview];
1093   if (!root.layer)
1094     return NO;
1096   // AppKit on OSX 10.9 has a bug for applications linked against OSX 10.8 SDK
1097   // and earlier. Under specific circumstances, it prevents the custom AppKit
1098   // transition from working well. See http://crbug.com/396980 for more
1099   // details.
1100   if ([[self class] systemSettingsRequireMavericksAppKitFullscreenHack] &&
1101       ![[[self window] screen] isEqual:[[NSScreen screens] objectAtIndex:0]]) {
1102     return NO;
1103   }
1105   return YES;
1108 - (NSArray*)customWindowsToEnterFullScreenForWindow:(NSWindow*)window {
1109   DCHECK([window isEqual:self.window]);
1111   if (![self shouldUseCustomAppKitFullscreenTransition:YES])
1112     return nil;
1114   FramedBrowserWindow* framedBrowserWindow =
1115       base::mac::ObjCCast<FramedBrowserWindow>([self window]);
1116   fullscreenTransition_.reset([[BrowserWindowFullscreenTransition alloc]
1117       initEnterWithWindow:framedBrowserWindow]);
1118   return [fullscreenTransition_ customWindowsForFullScreenTransition];
1121 - (NSArray*)customWindowsToExitFullScreenForWindow:(NSWindow*)window {
1122   DCHECK([window isEqual:self.window]);
1124   if (![self shouldUseCustomAppKitFullscreenTransition:NO])
1125     return nil;
1127   FramedBrowserWindow* framedBrowserWindow =
1128       base::mac::ObjCCast<FramedBrowserWindow>([self window]);
1129   fullscreenTransition_.reset([[BrowserWindowFullscreenTransition alloc]
1130       initExitWithWindow:framedBrowserWindow
1131                    frame:savedRegularWindowFrame_]);
1133   return [fullscreenTransition_ customWindowsForFullScreenTransition];
1136 - (void)window:(NSWindow*)window
1137     startCustomAnimationToEnterFullScreenWithDuration:(NSTimeInterval)duration {
1138   DCHECK([window isEqual:self.window]);
1139   [fullscreenTransition_ startCustomFullScreenAnimationWithDuration:duration];
1142 - (void)window:(NSWindow*)window
1143     startCustomAnimationToExitFullScreenWithDuration:(NSTimeInterval)duration {
1144   DCHECK([window isEqual:self.window]);
1146   [fullscreenTransition_ startCustomFullScreenAnimationWithDuration:duration];
1149 - (BOOL)shouldConstrainFrameRect {
1150   if ([fullscreenTransition_ shouldWindowBeUnconstrained])
1151     return NO;
1153   return [super shouldConstrainFrameRect];
1156 - (WebContents*)webContents {
1157   return browser_->tab_strip_model()->GetActiveWebContents();
1160 - (PermissionBubbleManager*)permissionBubbleManager {
1161   if (WebContents* contents = [self webContents])
1162     return PermissionBubbleManager::FromWebContents(contents);
1163   return nil;
1166 @end  // @implementation BrowserWindowController(Private)