blob: 996e3d6b3ce3110895a4ad4dbf89adf27122978f [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "chrome/browser/ui/cocoa/browser_window_controller_private.h"
#include <cmath>
#import "base/auto_reset.h"
#include "base/command_line.h"
#include "base/mac/bind_objc_block.h"
#include "base/mac/foundation_util.h"
#include "base/mac/mac_util.h"
#import "base/mac/scoped_nsobject.h"
#import "base/mac/sdk_forward_declarations.h"
#include "base/metrics/histogram.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/fullscreen.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_avatar_icon_util.h"
#include "chrome/browser/ui/bookmarks/bookmark_tab_helper.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_window_state.h"
#import "chrome/browser/ui/cocoa/browser_window_fullscreen_transition.h"
#import "chrome/browser/ui/cocoa/browser_window_layout.h"
#import "chrome/browser/ui/cocoa/browser/exclusive_access_controller_views.h"
#import "chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet_controller.h"
#import "chrome/browser/ui/cocoa/custom_frame_view.h"
#import "chrome/browser/ui/cocoa/dev_tools_controller.h"
#import "chrome/browser/ui/cocoa/fast_resize_view.h"
#import "chrome/browser/ui/cocoa/find_bar/find_bar_cocoa_controller.h"
#import "chrome/browser/ui/cocoa/floating_bar_backing_view.h"
#import "chrome/browser/ui/cocoa/framed_browser_window.h"
#include "chrome/browser/ui/cocoa/fullscreen_low_power_coordinator.h"
#import "chrome/browser/ui/cocoa/fullscreen_window.h"
#import "chrome/browser/ui/cocoa/infobars/infobar_container_controller.h"
#include "chrome/browser/ui/cocoa/last_active_browser_cocoa.h"
#import "chrome/browser/ui/cocoa/presentation_mode_controller.h"
#import "chrome/browser/ui/cocoa/profiles/avatar_button_controller.h"
#import "chrome/browser/ui/cocoa/profiles/avatar_icon_controller.h"
#import "chrome/browser/ui/cocoa/status_bubble_mac.h"
#import "chrome/browser/ui/cocoa/tab_contents/overlayable_contents_controller.h"
#import "chrome/browser/ui/cocoa/tab_contents/tab_contents_controller.h"
#import "chrome/browser/ui/cocoa/tabs/tab_strip_view.h"
#import "chrome/browser/ui/cocoa/toolbar/toolbar_controller.h"
#import "chrome/browser/ui/cocoa/website_settings/permission_bubble_cocoa.h"
#include "chrome/browser/ui/exclusive_access/fullscreen_controller.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/website_settings/permission_bubble_manager.h"
#include "chrome/common/chrome_switches.h"
#include "components/prefs/pref_service.h"
#include "components/prefs/scoped_user_pref_update.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/web_contents.h"
#import "ui/base/cocoa/focus_tracker.h"
#import "ui/base/cocoa/nsview_additions.h"
#include "ui/base/ui_base_types.h"
using content::RenderWidgetHostView;
using content::WebContents;
namespace {
// The screen on which the window was fullscreened, and whether the device had
// multiple screens available.
enum WindowLocation {
PRIMARY_SINGLE_SCREEN = 0,
PRIMARY_MULTIPLE_SCREEN = 1,
SECONDARY_MULTIPLE_SCREEN = 2,
WINDOW_LOCATION_COUNT = 3
};
// There are 2 mechanisms for invoking fullscreen: AppKit and Immersive.
// There are 2 types of AppKit Fullscreen: Presentation Mode and Canonical
// Fullscreen.
enum FullscreenStyle {
IMMERSIVE_FULLSCREEN = 0,
PRESENTATION_MODE = 1,
CANONICAL_FULLSCREEN = 2,
FULLSCREEN_STYLE_COUNT = 3
};
// Emits a histogram entry indicating the Fullscreen window location.
void RecordFullscreenWindowLocation(NSWindow* window) {
NSArray* screens = [NSScreen screens];
bool primary_screen = ([[window screen] isEqual:[screens objectAtIndex:0]]);
bool multiple_screens = [screens count] > 1;
WindowLocation location = PRIMARY_SINGLE_SCREEN;
if (multiple_screens) {
location =
primary_screen ? PRIMARY_MULTIPLE_SCREEN : SECONDARY_MULTIPLE_SCREEN;
}
UMA_HISTOGRAM_ENUMERATION(
"OSX.Fullscreen.Enter.WindowLocation", location, WINDOW_LOCATION_COUNT);
}
// Emits a histogram entry indicating the Fullscreen style.
void RecordFullscreenStyle(FullscreenStyle style) {
UMA_HISTOGRAM_ENUMERATION(
"OSX.Fullscreen.Enter.Style", style, FULLSCREEN_STYLE_COUNT);
}
} // namespace
@interface NSWindow (NSPrivateApis)
// Note: These functions are private, use -[NSObject respondsToSelector:]
// before calling them.
- (NSWindow*)_windowForToolbar;
@end
@implementation BrowserWindowController(Private)
// Create the tab strip controller.
- (void)createTabStripController {
DCHECK([overlayableContentsController_ activeContainer]);
DCHECK([[overlayableContentsController_ activeContainer] window]);
tabStripController_.reset([[TabStripController alloc]
initWithView:[self tabStripView]
switchView:[overlayableContentsController_ activeContainer]
browser:browser_.get()
delegate:self]);
}
- (void)updateFullscreenCollectionBehavior {
// Set the window to participate in Lion Fullscreen mode. Setting this flag
// has no effect on Snow Leopard or earlier. Panels can share a fullscreen
// space with a tabbed window, but they can not be primary fullscreen
// windows.
// This ensures the fullscreen button is appropriately positioned. It must
// be done before calling layoutSubviews because the new avatar button's
// position depends on the fullscreen button's position, as well as
// TabStripController's rightIndentForControls.
// The fullscreen button's position may depend on the old avatar button's
// width, but that does not require calling layoutSubviews first.
NSWindow* window = [self window];
NSUInteger collectionBehavior = [window collectionBehavior];
collectionBehavior &= ~NSWindowCollectionBehaviorFullScreenAuxiliary;
collectionBehavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
collectionBehavior |= browser_->type() == Browser::TYPE_TABBED ||
browser_->type() == Browser::TYPE_POPUP
? NSWindowCollectionBehaviorFullScreenPrimary
: NSWindowCollectionBehaviorFullScreenAuxiliary;
[window setCollectionBehavior:collectionBehavior];
}
- (void)saveWindowPositionIfNeeded {
if (!chrome::ShouldSaveWindowPlacement(browser_.get()))
return;
// If we're in fullscreen mode, save the position of the regular window
// instead.
NSWindow* window =
[self isInAnyFullscreenMode] ? savedRegularWindow_ : [self window];
// Window positions are stored relative to the origin of the primary monitor.
NSRect monitorFrame = [[[NSScreen screens] firstObject] frame];
NSScreen* windowScreen = [window screen];
// Start with the window's frame, which is in virtual coordinates.
// Do some y twiddling to flip the coordinate system.
gfx::Rect bounds(NSRectToCGRect([window frame]));
bounds.set_y(monitorFrame.size.height - bounds.y() - bounds.height());
// Browser::SaveWindowPlacement saves information for session restore.
ui::WindowShowState show_state = ui::SHOW_STATE_NORMAL;
if ([window isMiniaturized])
show_state = ui::SHOW_STATE_MINIMIZED;
else if ([self isInAnyFullscreenMode])
show_state = ui::SHOW_STATE_FULLSCREEN;
chrome::SaveWindowPlacement(browser_.get(), bounds, show_state);
// |windowScreen| can be nil (for example, if the monitor arrangement was
// changed while in fullscreen mode). If we see a nil screen, return without
// saving.
// TODO(rohitrao): We should just not save anything for fullscreen windows.
// http://crbug.com/36479.
if (!windowScreen)
return;
// Only save main window information to preferences.
PrefService* prefs = browser_->profile()->GetPrefs();
if (!prefs || browser_.get() != chrome::GetLastActiveBrowser())
return;
// Save the current work area, in flipped coordinates.
gfx::Rect workArea(NSRectToCGRect([windowScreen visibleFrame]));
workArea.set_y(monitorFrame.size.height - workArea.y() - workArea.height());
std::unique_ptr<DictionaryPrefUpdate> update =
chrome::GetWindowPlacementDictionaryReadWrite(
chrome::GetWindowName(browser_.get()),
browser_->profile()->GetPrefs());
base::DictionaryValue* windowPreferences = update->Get();
windowPreferences->SetInteger("left", bounds.x());
windowPreferences->SetInteger("top", bounds.y());
windowPreferences->SetInteger("right", bounds.right());
windowPreferences->SetInteger("bottom", bounds.bottom());
windowPreferences->SetBoolean("maximized", false);
windowPreferences->SetBoolean("always_on_top", false);
windowPreferences->SetInteger("work_area_left", workArea.x());
windowPreferences->SetInteger("work_area_top", workArea.y());
windowPreferences->SetInteger("work_area_right", workArea.right());
windowPreferences->SetInteger("work_area_bottom", workArea.bottom());
}
- (NSRect)window:(NSWindow*)window
willPositionSheet:(NSWindow*)sheet
usingRect:(NSRect)defaultSheetLocation {
// Position the sheet as follows:
// - If the bookmark bar is shown (attached to the normal toolbar), position
// the sheet below the bookmark bar.
// - If the bookmark bar is hidden or shown as a bubble (on the NTP when the
// bookmark bar is disabled), position the sheet immediately below the
// normal toolbar.
// - If the bookmark bar is currently animating, position the sheet according
// to where the bar will be when the animation ends.
CGFloat defaultSheetY = defaultSheetLocation.origin.y;
if ([self supportsBookmarkBar] &&
[bookmarkBarController_ currentState] == BookmarkBar::SHOW) {
defaultSheetY = NSMinY([[bookmarkBarController_ view] frame]);
} else if ([self hasToolbar]) {
defaultSheetY = NSMinY([[toolbarController_ view] frame]);
} else {
// The toolbar is not shown in popup and application modes. The sheet
// should be located at the top of the window, under the title of the
// window.
defaultSheetY = NSMaxY([[window contentView] frame]);
}
// AppKit may shift the window up to fit the sheet on screen, but it will
// never adjust the height of the sheet, or the origin of the sheet relative
// to the window. Adjust the origin to prevent sheets from extending past the
// bottom of the screen.
// Don't allow the sheet to extend past the bottom of the window. This logic
// intentionally ignores the size of the screens, since the window might span
// multiple screens, and AppKit may reposition the window.
CGFloat sheetHeight = NSHeight([sheet frame]);
defaultSheetY = std::max(defaultSheetY, sheetHeight);
// It doesn't make sense to provide a Y higher than the height of the window.
CGFloat windowHeight = NSHeight([window frame]);
defaultSheetY = std::min(defaultSheetY, windowHeight);
defaultSheetLocation.origin.y = defaultSheetY;
return defaultSheetLocation;
}
- (void)layoutSubviews {
// TODO(spqchan): Change blockLayoutSubviews so that it only blocks the web
// content from resizing.
if (blockLayoutSubviews_)
return;
// Suppress title drawing if necessary.
if ([self.window respondsToSelector:@selector(setShouldHideTitle:)])
[(id)self.window setShouldHideTitle:![self hasTitleBar]];
[bookmarkBarController_ updateHiddenState];
[self updateSubviewZOrder];
base::scoped_nsobject<BrowserWindowLayout> layout(
[[BrowserWindowLayout alloc] init]);
[self updateLayoutParameters:layout];
[self applyLayout:layout];
[toolbarController_ setDividerOpacity:[self toolbarDividerOpacity]];
// Will update the location of the permission bubble when showing/hiding the
// top level toolbar in fullscreen.
PermissionBubbleManager* manager = [self permissionBubbleManager];
if (manager)
manager->UpdateAnchorPosition();
browser_->GetBubbleManager()->UpdateAllBubbleAnchors();
}
- (void)applyTabStripLayout:(const chrome::TabStripLayout&)layout {
// Update the presence of the window controls.
if (layout.addCustomWindowControls)
[tabStripController_ addCustomWindowControls];
else
[tabStripController_ removeCustomWindowControls];
// Update the layout of the avatar.
if (!NSIsEmptyRect(layout.avatarFrame)) {
NSView* avatarButton = [avatarButtonController_ view];
[avatarButton setFrame:layout.avatarFrame];
[avatarButton setHidden:NO];
}
// Check if the tab strip's frame has changed.
BOOL requiresRelayout =
!NSEqualRects([[self tabStripView] frame], layout.frame);
// Check if the left indent has changed.
if (layout.leftIndent != [tabStripController_ leftIndentForControls]) {
[tabStripController_ setLeftIndentForControls:layout.leftIndent];
requiresRelayout = YES;
}
// Check if the right indent has changed.
if (layout.rightIndent != [tabStripController_ rightIndentForControls]) {
[tabStripController_ setRightIndentForControls:layout.rightIndent];
requiresRelayout = YES;
}
// It is undesirable to force tabs relayout when the tap strip's frame did
// not change, because it will interrupt tab animations in progress.
// In addition, there appears to be an AppKit bug on <10.9 where interrupting
// a tab animation resulted in the tab frame being the animator's target
// frame instead of the interrupting setFrame. (See http://crbug.com/415093)
if (requiresRelayout) {
[[self tabStripView] setFrame:layout.frame];
[tabStripController_ layoutTabsWithoutAnimation];
}
}
- (BOOL)placeBookmarkBarBelowInfoBar {
// If we are currently displaying the NTP detached bookmark bar or animating
// to/from it (from/to anything else), we display the bookmark bar below the
// info bar.
return [bookmarkBarController_ isInState:BookmarkBar::DETACHED] ||
[bookmarkBarController_ isAnimatingToState:BookmarkBar::DETACHED] ||
[bookmarkBarController_ isAnimatingFromState:BookmarkBar::DETACHED];
}
- (void)layoutTabContentArea:(NSRect)newFrame {
NSView* tabContentView = [self tabContentArea];
NSRect tabContentFrame = [tabContentView frame];
tabContentFrame = newFrame;
[tabContentView setFrame:tabContentFrame];
}
- (void)adjustToolbarAndBookmarkBarForCompression:(CGFloat)compression {
CGFloat newHeight =
[toolbarController_ desiredHeightForCompression:compression];
NSRect toolbarFrame = [[toolbarController_ view] frame];
CGFloat deltaH = newHeight - toolbarFrame.size.height;
if (deltaH == 0)
return;
toolbarFrame.size.height = newHeight;
NSRect bookmarkFrame = [[bookmarkBarController_ view] frame];
bookmarkFrame.size.height = bookmarkFrame.size.height - deltaH;
[[toolbarController_ view] setFrame:toolbarFrame];
[[bookmarkBarController_ view] setFrame:bookmarkFrame];
[self layoutSubviews];
}
// Fullscreen and presentation mode methods
- (void)moveViewsForImmersiveFullscreen:(BOOL)fullscreen
regularWindow:(NSWindow*)regularWindow
fullscreenWindow:(NSWindow*)fullscreenWindow {
NSWindow* sourceWindow = fullscreen ? regularWindow : fullscreenWindow;
NSWindow* destWindow = fullscreen ? fullscreenWindow : regularWindow;
// Close the bookmark bubble, if it's open. Use |-ok:| instead of |-cancel:|
// or |-close| because that matches the behavior when the bubble loses key
// status.
[bookmarkBubbleController_ ok:self];
// Save the current first responder so we can restore after views are moved.
base::scoped_nsobject<FocusTracker> focusTracker(
[[FocusTracker alloc] initWithWindow:sourceWindow]);
// While we move views (and focus) around, disable any bar visibility changes.
[self disableBarVisibilityUpdates];
// Retain the tab strip view while we remove it from its superview.
base::scoped_nsobject<NSView> tabStripView;
if ([self hasTabStrip]) {
tabStripView.reset([[self tabStripView] retain]);
[tabStripView removeFromSuperview];
}
// Disable autoresizing of subviews while we move views around. This prevents
// spurious renderer resizes.
[self.chromeContentView setAutoresizesSubviews:NO];
[self.chromeContentView removeFromSuperview];
// Have to do this here, otherwise later calls can crash because the window
// has no delegate.
[sourceWindow setDelegate:nil];
[destWindow setDelegate:self];
// With this call, valgrind complains that a "Conditional jump or move depends
// on uninitialised value(s)". The error happens in -[NSThemeFrame
// drawOverlayRect:]. I'm pretty convinced this is an Apple bug, but there is
// no visual impact. I have been unable to tickle it away with other window
// or view manipulation Cocoa calls. Stack added to suppressions_mac.txt.
[self.chromeContentView setAutoresizesSubviews:YES];
[[destWindow contentView] addSubview:self.chromeContentView
positioned:NSWindowBelow
relativeTo:nil];
[self.chromeContentView setFrame:[[destWindow contentView] bounds]];
// Move the incognito badge if present.
if ([self shouldShowAvatar]) {
NSView* avatarButtonView = [avatarButtonController_ view];
[avatarButtonView removeFromSuperview];
[avatarButtonView setHidden:YES]; // Will be shown in layout.
[[destWindow contentView] addSubview:avatarButtonView];
}
// Add the tab strip after setting the content view and moving the incognito
// badge (if any), so that the tab strip will be on top (in the z-order).
if ([self hasTabStrip])
[[destWindow contentView] addSubview:tabStripView];
[sourceWindow setWindowController:nil];
[self setWindow:destWindow];
[destWindow setWindowController:self];
// Move the status bubble over, if we have one.
if (statusBubble_)
statusBubble_->SwitchParentWindow(destWindow);
// Updates the bubble position.
PermissionBubbleManager* manager = [self permissionBubbleManager];
if (manager)
manager->UpdateAnchorPosition();
// Move the title over.
[destWindow setTitle:[sourceWindow title]];
// The window needs to be onscreen before we can set its first responder.
// Ordering the window to the front can change the active Space (either to
// the window's old Space or to the application's assigned Space). To prevent
// this by temporarily change the collectionBehavior.
NSWindowCollectionBehavior behavior = [sourceWindow collectionBehavior];
[destWindow setCollectionBehavior:
NSWindowCollectionBehaviorMoveToActiveSpace];
[destWindow makeKeyAndOrderFront:self];
[destWindow setCollectionBehavior:behavior];
if (![focusTracker restoreFocusInWindow:destWindow]) {
// During certain types of fullscreen transitions, the view that had focus
// may have gone away (e.g., the one for a Flash FS widget). In this case,
// FocusTracker will fail to restore focus to anything, so we set the focus
// to the tab contents as a reasonable fall-back.
[self focusTabContents];
}
[sourceWindow orderOut:self];
// We're done moving focus, so re-enable bar visibility changes.
[self enableBarVisibilityUpdates];
}
- (void)permissionBubbleWindowWillClose:(NSNotification*)notification {
NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
[center removeObserver:self
name:NSWindowWillCloseNotification
object:[notification object]];
[self releaseBarVisibilityForOwner:[notification object]
withAnimation:YES
delay:YES];
}
- (void)configurePresentationModeController {
BOOL fullscreenForTab = [self isFullscreenForTabContentOrExtension];
BOOL kioskMode =
base::CommandLine::ForCurrentProcess()->HasSwitch(switches::kKioskMode);
BOOL showDropdown =
!fullscreenForTab && !kioskMode && ([self floatingBarHasFocus]);
PermissionBubbleManager* manager = [self permissionBubbleManager];
if (manager && manager->IsBubbleVisible()) {
NSWindow* bubbleWindow = manager->GetBubbleWindow();
DCHECK(bubbleWindow);
// A visible permission bubble will force the dropdown to remain
// visible.
[self lockBarVisibilityForOwner:bubbleWindow withAnimation:NO delay:NO];
showDropdown = YES;
// Register to be notified when the permission bubble is closed, to
// allow fullscreen to hide the dropdown.
NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
[center addObserver:self
selector:@selector(permissionBubbleWindowWillClose:)
name:NSWindowWillCloseNotification
object:bubbleWindow];
}
NSView* contentView = [[self window] contentView];
[presentationModeController_
enterPresentationModeForContentView:contentView
showDropdown:showDropdown];
}
- (void)adjustUIForExitingFullscreenAndStopOmniboxSliding {
[presentationModeController_ exitPresentationMode];
presentationModeController_.reset();
// Force the bookmark bar z-order to update.
[[bookmarkBarController_ view] removeFromSuperview];
[self layoutSubviews];
}
- (void)adjustUIForSlidingFullscreenStyle:(fullscreen_mac::SlidingStyle)style {
if (!presentationModeController_) {
presentationModeController_.reset(
[self newPresentationModeControllerWithStyle:style]);
[self configurePresentationModeController];
} else {
presentationModeController_.get().slidingStyle = style;
}
if (!floatingBarBackingView_.get() &&
([self hasTabStrip] || [self hasToolbar] || [self hasLocationBar])) {
floatingBarBackingView_.reset(
[[FloatingBarBackingView alloc] initWithFrame:NSZeroRect]);
[floatingBarBackingView_
setAutoresizingMask:(NSViewWidthSizable | NSViewMinYMargin)];
}
// Force the bookmark bar z-order to update.
[[bookmarkBarController_ view] removeFromSuperview];
[self layoutSubviews];
}
- (PresentationModeController*)newPresentationModeControllerWithStyle:
(fullscreen_mac::SlidingStyle)style {
return [[PresentationModeController alloc] initWithBrowserController:self
style:style];
}
- (void)enterImmersiveFullscreen {
RecordFullscreenWindowLocation([self window]);
RecordFullscreenStyle(IMMERSIVE_FULLSCREEN);
// Set to NO by |-windowDidEnterFullScreen:|.
enteringImmersiveFullscreen_ = YES;
// Fade to black.
const CGDisplayReservationInterval kFadeDurationSeconds = 0.6;
Boolean didFadeOut = NO;
CGDisplayFadeReservationToken token;
if (CGAcquireDisplayFadeReservation(kFadeDurationSeconds, &token)
== kCGErrorSuccess) {
didFadeOut = YES;
CGDisplayFade(token, kFadeDurationSeconds / 2, kCGDisplayBlendNormal,
kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, /*synchronous=*/true);
}
// Create the fullscreen window.
fullscreenWindow_.reset([[self createFullscreenWindow] retain]);
savedRegularWindow_ = [[self window] retain];
savedRegularWindowFrame_ = [savedRegularWindow_ frame];
[self moveViewsForImmersiveFullscreen:YES
regularWindow:[self window]
fullscreenWindow:fullscreenWindow_.get()];
fullscreen_mac::SlidingStyle style = fullscreen_mac::OMNIBOX_TABS_HIDDEN;
[self adjustUIForSlidingFullscreenStyle:style];
[fullscreenWindow_ display];
// AppKit is helpful and prevents NSWindows from having the same height as
// the screen while the menu bar is showing. This only applies to windows on
// a secondary screen, in a separate space. Calling [NSWindow
// setFrame:display:] with the screen's height will always reduce the
// height by the height of the MenuBar. Calling the method with any other
// height works fine. The relevant method in the 10.10 AppKit SDK is called:
// _canAdjustSizeForScreensHaveSeparateSpacesIfFillingSecondaryScreen
//
// TODO(erikchen): Refactor the logic to allow the window to be shown after
// the menubar has been hidden. This would remove the need for this hack.
// http://crbug.com/403203
NSRect frame = [[[self window] screen] frame];
if (!NSEqualRects(frame, [fullscreenWindow_ frame]))
[fullscreenWindow_ setFrame:[[[self window] screen] frame] display:YES];
[self layoutSubviews];
[self windowDidEnterFullScreen:nil];
// Fade back in.
if (didFadeOut) {
CGDisplayFade(token, kFadeDurationSeconds / 2, kCGDisplayBlendSolidColor,
kCGDisplayBlendNormal, 0.0, 0.0, 0.0, /*synchronous=*/false);
CGReleaseDisplayFadeReservation(token);
}
}
- (void)exitImmersiveFullscreen {
// Fade to black.
const CGDisplayReservationInterval kFadeDurationSeconds = 0.6;
Boolean didFadeOut = NO;
CGDisplayFadeReservationToken token;
if (CGAcquireDisplayFadeReservation(kFadeDurationSeconds, &token)
== kCGErrorSuccess) {
didFadeOut = YES;
CGDisplayFade(token, kFadeDurationSeconds / 2, kCGDisplayBlendNormal,
kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, /*synchronous=*/true);
}
[self windowWillExitFullScreen:nil];
[self moveViewsForImmersiveFullscreen:NO
regularWindow:savedRegularWindow_
fullscreenWindow:fullscreenWindow_.get()];
// When exiting fullscreen mode, we need to call layoutSubviews manually.
[savedRegularWindow_ autorelease];
savedRegularWindow_ = nil;
// No close event is thrown when a window is dealloc'd after orderOut.
// Explicitly close the window to notify bubbles.
[fullscreenWindow_.get() close];
fullscreenWindow_.reset();
[self layoutSubviews];
[self windowDidExitFullScreen:nil];
// Fade back in.
if (didFadeOut) {
CGDisplayFade(token, kFadeDurationSeconds / 2, kCGDisplayBlendSolidColor,
kCGDisplayBlendNormal, 0.0, 0.0, 0.0, /*synchronous=*/false);
CGReleaseDisplayFadeReservation(token);
}
}
- (void)showFullscreenExitBubbleIfNecessary {
// This method is called in response to
// |-updateFullscreenExitBubbleURL:bubbleType:|. If we're in the middle of the
// transition into fullscreen (i.e., using the AppKit Fullscreen API), do not
// show the bubble because it will cause visual jank
// (http://crbug.com/130649). This will be called again as part of
// |-windowDidEnterFullScreen:|, so arrange to do that work then instead.
if (enteringAppKitFullscreen_)
return;
[self hideOverlayIfPossibleWithAnimation:NO delay:NO];
switch (exclusiveAccessController_->bubble_type()) {
case EXCLUSIVE_ACCESS_BUBBLE_TYPE_NONE:
case EXCLUSIVE_ACCESS_BUBBLE_TYPE_BROWSER_FULLSCREEN_EXIT_INSTRUCTION:
// Show no exit instruction bubble on Mac when in Browser Fullscreen.
exclusiveAccessController_->Destroy();
break;
default:
exclusiveAccessController_->Show();
}
}
- (void)contentViewDidResize:(NSNotification*)notification {
[self layoutSubviews];
}
- (void)registerForContentViewResizeNotifications {
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(contentViewDidResize:)
name:NSViewFrameDidChangeNotification
object:[[self window] contentView]];
}
- (void)deregisterForContentViewResizeNotifications {
[[NSNotificationCenter defaultCenter]
removeObserver:self
name:NSViewFrameDidChangeNotification
object:[[self window] contentView]];
}
- (NSSize)window:(NSWindow*)window
willUseFullScreenContentSize:(NSSize)proposedSize {
return proposedSize;
}
- (NSApplicationPresentationOptions)window:(NSWindow*)window
willUseFullScreenPresentationOptions:(NSApplicationPresentationOptions)opt {
return (opt |
NSApplicationPresentationAutoHideDock |
NSApplicationPresentationAutoHideMenuBar);
}
- (void)windowWillEnterFullScreen:(NSNotification*)notification {
RecordFullscreenWindowLocation([self window]);
RecordFullscreenStyle(enteringPresentationMode_ ? PRESENTATION_MODE
: CANONICAL_FULLSCREEN);
if (notification) // For System Fullscreen when non-nil.
[self registerForContentViewResizeNotifications];
[[tabStripController_ activeTabContentsController]
setBlockFullscreenResize:YES];
NSWindow* window = [self window];
savedRegularWindowFrame_ = [window frame];
enteringAppKitFullscreen_ = YES;
enteringAppKitFullscreenOnPrimaryScreen_ =
[[[self window] screen] isEqual:[[NSScreen screens] firstObject]];
[self setSheetHiddenForFullscreenTransition:YES];
[self adjustUIForEnteringFullscreen];
}
- (void)windowDidEnterFullScreen:(NSNotification*)notification {
// In Yosemite, some combination of the titlebar and toolbar always show in
// full-screen mode. We do not want either to show. Search for the window that
// contains the views, and hide it. There is no need to ever unhide the view.
// http://crbug.com/380235
if (base::mac::IsOSYosemiteOrLater()) {
for (NSWindow* window in [[NSApplication sharedApplication] windows]) {
if ([window
isKindOfClass:NSClassFromString(@"NSToolbarFullScreenWindow")]) {
// Hide the toolbar if it is for a FramedBrowserWindow.
if ([window respondsToSelector:@selector(_windowForToolbar)]) {
if ([[window _windowForToolbar]
isKindOfClass:[FramedBrowserWindow class]])
[[window contentView] setHidden:YES];
}
}
}
}
if ([self shouldUseMavericksAppKitFullscreenHack]) {
// Apply a hack to fix the size of the window. This is the last run of the
// MessageLoop where the hack will not work, so dispatch the hack to the
// top of the MessageLoop.
base::Callback<void(void)> callback = base::BindBlock(^{
if (![self isInAppKitFullscreen])
return;
// The window's frame should be exactly 22 points too short.
CGFloat kExpectedHeightDifference = 22;
NSRect currentFrame = [[self window] frame];
NSRect expectedFrame = [[[self window] screen] frame];
if (!NSEqualPoints(currentFrame.origin, expectedFrame.origin))
return;
if (currentFrame.size.width != expectedFrame.size.width)
return;
CGFloat heightDelta =
expectedFrame.size.height - currentFrame.size.height;
if (fabs(heightDelta - kExpectedHeightDifference) > 0.01)
return;
[[self window] setFrame:expectedFrame display:YES];
});
base::MessageLoop::current()->PostTask(FROM_HERE, callback);
}
if (notification) // For System Fullscreen when non-nil.
[self deregisterForContentViewResizeNotifications];
enteringImmersiveFullscreen_ = NO;
enteringPresentationMode_ = NO;
[self resetCustomAppKitFullscreenVariables];
[[tabStripController_ activeTabContentsController]
updateFullscreenWidgetFrame];
[self showFullscreenExitBubbleIfNecessary];
browser_->WindowFullscreenStateChanged();
if (fullscreenLowPowerCoordinator_)
fullscreenLowPowerCoordinator_->SetInFullscreenTransition(false);
if (shouldExitAfterEnteringFullscreen_) {
shouldExitAfterEnteringFullscreen_ = NO;
[self exitAppKitFullscreen];
}
}
- (void)windowWillExitFullScreen:(NSNotification*)notification {
if (fullscreenLowPowerCoordinator_)
fullscreenLowPowerCoordinator_->SetInFullscreenTransition(true);
if (notification) // For System Fullscreen when non-nil.
[self registerForContentViewResizeNotifications];
exitingAppKitFullscreen_ = YES;
// Like windowWillEnterFullScreen, if we use custom animations,
// adjustUIForExitingFullscreen should be called after the layout resizes in
// startCustomAnimationToExitFullScreenWithDuration.
if (isUsingCustomAnimation_) {
blockLayoutSubviews_ = YES;
[self.chromeContentView setAutoresizesSubviews:NO];
[self setSheetHiddenForFullscreenTransition:YES];
} else {
[self adjustUIForExitingFullscreen];
}
}
- (void)windowDidExitFullScreen:(NSNotification*)notification {
DCHECK(exitingAppKitFullscreen_);
// If the custom transition isn't complete, then just set the flag and
// return. Once the transition is completed, windowDidExitFullscreen will
// be called again.
if (isUsingCustomAnimation_ &&
![fullscreenTransition_ isTransitionCompleted]) {
appKitDidExitFullscreen_ = YES;
return;
}
// Destroy the NSWindow used for fullscreen low power mode.
fullscreenLowPowerCoordinator_.reset();
if (notification) // For System Fullscreen when non-nil.
[self deregisterForContentViewResizeNotifications];
browser_->WindowFullscreenStateChanged();
[self.chromeContentView setAutoresizesSubviews:YES];
[self resetCustomAppKitFullscreenVariables];
// Ensures that the permission bubble shows up properly at the front.
PermissionBubbleManager* manager = [self permissionBubbleManager];
if (manager && manager->IsBubbleVisible()) {
NSWindow* bubbleWindow = manager->GetBubbleWindow();
DCHECK(bubbleWindow);
[bubbleWindow orderFront:nil];
}
// Ensure that the window is layout properly.
[self layoutSubviews];
}
- (void)windowDidFailToEnterFullScreen:(NSWindow*)window {
[self deregisterForContentViewResizeNotifications];
[self resetCustomAppKitFullscreenVariables];
[self adjustUIForExitingFullscreenAndStopOmniboxSliding];
fullscreenLowPowerCoordinator_.reset();
}
- (void)windowDidFailToExitFullScreen:(NSWindow*)window {
[self deregisterForContentViewResizeNotifications];
[self resetCustomAppKitFullscreenVariables];
// Force a relayout to try and get the window back into a reasonable state.
[self layoutSubviews];
}
- (void)setSheetHiddenForFullscreenTransition:(BOOL)shoudHide {
if (!isUsingCustomAnimation_)
return;
ConstrainedWindowSheetController* sheetController =
[ConstrainedWindowSheetController
controllerForParentWindow:[self window]];
if (shoudHide)
[sheetController hideSheetForFullscreenTransition];
else
[sheetController unhideSheetForFullscreenTransition];
}
- (void)adjustUIForExitingFullscreen {
exclusiveAccessController_->Destroy();
[self adjustUIForExitingFullscreenAndStopOmniboxSliding];
}
- (void)adjustUIForEnteringFullscreen {
fullscreen_mac::SlidingStyle style;
if ([self isFullscreenForTabContentOrExtension]) {
style = fullscreen_mac::OMNIBOX_TABS_NONE;
} else if (enteringPresentationMode_ || ![self shouldShowFullscreenToolbar]) {
style = fullscreen_mac::OMNIBOX_TABS_HIDDEN;
} else {
style = fullscreen_mac::OMNIBOX_TABS_PRESENT;
}
[self adjustUIForSlidingFullscreenStyle:style];
}
- (void)enableBarVisibilityUpdates {
// Early escape if there's nothing to do.
if (barVisibilityUpdatesEnabled_)
return;
barVisibilityUpdatesEnabled_ = YES;
if ([barVisibilityLocks_ count])
[presentationModeController_ ensureOverlayShownWithAnimation:NO delay:NO];
else
[presentationModeController_ ensureOverlayHiddenWithAnimation:NO delay:NO];
}
- (void)disableBarVisibilityUpdates {
// Early escape if there's nothing to do.
if (!barVisibilityUpdatesEnabled_)
return;
barVisibilityUpdatesEnabled_ = NO;
[presentationModeController_ cancelAnimationAndTimers];
}
- (void)hideOverlayIfPossibleWithAnimation:(BOOL)animation delay:(BOOL)delay {
if (!barVisibilityUpdatesEnabled_ || [barVisibilityLocks_ count])
return;
[presentationModeController_ ensureOverlayHiddenWithAnimation:animation
delay:delay];
}
- (CGFloat)toolbarDividerOpacity {
return [bookmarkBarController_ toolbarDividerOpacity];
}
- (void)updateInfoBarTipVisibility {
// If there's no toolbar then hide the infobar tip.
[infoBarContainerController_
setShouldSuppressTopInfoBarTip:![self hasToolbar]];
}
- (NSInteger)pageInfoBubblePointY {
LocationBarViewMac* locationBarView = [self locationBarBridge];
// The point, in window coordinates.
NSPoint iconBottom = locationBarView->GetPageInfoBubblePoint();
// The toolbar, in window coordinates.
NSView* toolbar = [toolbarController_ view];
CGFloat toolbarY = NSMinY([toolbar convertRect:[toolbar bounds] toView:nil]);
return iconBottom.y - toolbarY;
}
- (void)enterAppKitFullscreen {
if (FramedBrowserWindow* framedBrowserWindow =
base::mac::ObjCCast<FramedBrowserWindow>([self window])) {
[framedBrowserWindow toggleSystemFullScreen];
}
}
- (void)exitAppKitFullscreen {
if (FramedBrowserWindow* framedBrowserWindow =
base::mac::ObjCCast<FramedBrowserWindow>([self window])) {
// If we're in the process of entering fullscreen, toggleSystemFullscreen
// will get ignored. Set |shouldExitAfterEnteringFullscreen_| to true so
// the browser will exit fullscreen immediately after it enters it.
if (enteringAppKitFullscreen_) {
shouldExitAfterEnteringFullscreen_ = YES;
return;
}
[framedBrowserWindow toggleSystemFullScreen];
}
}
- (NSRect)fullscreenButtonFrame {
NSButton* fullscreenButton =
[[self window] standardWindowButton:NSWindowFullScreenButton];
if (!fullscreenButton)
return NSZeroRect;
NSRect buttonFrame = [fullscreenButton frame];
// When called from -windowWillExitFullScreen:, the button's frame may not
// be updated yet to match the new window size.
// We need to return where the button should be positioned.
NSView* rootView = [[[self window] contentView] superview];
if ([rootView respondsToSelector:@selector(_fullScreenButtonOrigin)])
buttonFrame.origin = [rootView _fullScreenButtonOrigin];
return buttonFrame;
}
- (void)updateLayoutParameters:(BrowserWindowLayout*)layout {
[layout setContentViewSize:[[[self window] contentView] bounds].size];
NSSize windowSize = (fullscreenTransition_.get())
? [fullscreenTransition_ desiredWindowLayoutSize]
: [[self window] frame].size;
[layout setWindowSize:windowSize];
[layout setInAnyFullscreen:[self isInAnyFullscreenMode]];
[layout setFullscreenSlidingStyle:
presentationModeController_.get().slidingStyle];
[layout setFullscreenMenubarOffset:
[presentationModeController_ menubarOffset]];
[layout setFullscreenToolbarFraction:
[presentationModeController_ toolbarFraction]];
[layout setHasTabStrip:[self hasTabStrip]];
[layout setFullscreenButtonFrame:[self fullscreenButtonFrame]];
if ([self shouldShowAvatar]) {
NSView* avatar = [avatarButtonController_ view];
[layout setShouldShowAvatar:YES];
[layout setShouldUseNewAvatar:[self shouldUseNewAvatarButton]];
[layout setAvatarSize:[avatar frame].size];
[layout setAvatarLineWidth:[[avatar superview] cr_lineWidth]];
}
[layout setHasToolbar:[self hasToolbar]];
[layout setToolbarHeight:NSHeight([[toolbarController_ view] bounds])];
[layout setHasLocationBar:[self hasLocationBar]];
[layout setPlaceBookmarkBarBelowInfoBar:[self placeBookmarkBarBelowInfoBar]];
[layout setBookmarkBarHidden:[bookmarkBarController_ view].isHidden];
[layout setBookmarkBarHeight:
NSHeight([[bookmarkBarController_ view] bounds])];
[layout setInfoBarHeight:[infoBarContainerController_ heightOfInfoBars]];
[layout setPageInfoBubblePointY:[self pageInfoBubblePointY]];
[layout setHasDownloadShelf:(downloadShelfController_.get() != nil)];
[layout setDownloadShelfHeight:
NSHeight([[downloadShelfController_ view] bounds])];
}
- (void)applyLayout:(BrowserWindowLayout*)layout {
chrome::LayoutOutput output = [layout computeLayout];
if (!NSIsEmptyRect(output.tabStripLayout.frame))
[self applyTabStripLayout:output.tabStripLayout];
if (!NSIsEmptyRect(output.toolbarFrame))
[[toolbarController_ view] setFrame:output.toolbarFrame];
if (!NSIsEmptyRect(output.bookmarkFrame)) {
NSView* bookmarkBarView = [bookmarkBarController_ view];
[bookmarkBarView setFrame:output.bookmarkFrame];
// Pin the bookmark bar to the top of the window and make the width
// flexible.
[bookmarkBarView setAutoresizingMask:NSViewWidthSizable | NSViewMinYMargin];
[bookmarkBarController_ layoutSubviews];
}
// The info bar is never hidden. Sometimes it has zero effective height.
[[infoBarContainerController_ view] setFrame:output.infoBarFrame];
[infoBarContainerController_
setMaxTopArrowHeight:output.infoBarMaxTopArrowHeight];
[infoBarContainerController_
setInfobarArrowX:[self locationBarBridge]->GetPageInfoBubblePoint().x];
if (!NSIsEmptyRect(output.downloadShelfFrame))
[[downloadShelfController_ view] setFrame:output.downloadShelfFrame];
[self layoutTabContentArea:output.contentAreaFrame];
if (!NSIsEmptyRect(output.fullscreenBackingBarFrame)) {
[floatingBarBackingView_ setFrame:output.fullscreenBackingBarFrame];
[presentationModeController_
overlayFrameChanged:output.fullscreenBackingBarFrame];
}
[findBarCocoaController_
positionFindBarViewAtMaxY:output.findBarMaxY
maxWidth:NSWidth(output.contentAreaFrame)];
exclusiveAccessController_->Layout(output.fullscreenExitButtonMaxY);
if (fullscreenLowPowerCoordinator_) {
fullscreenLowPowerCoordinator_->SetLayoutParameters(
output.toolbarFrame, output.infoBarFrame, output.contentAreaFrame,
output.downloadShelfFrame);
}
}
- (void)updateSubviewZOrder {
if ([self isInAnyFullscreenMode])
[self updateSubviewZOrderFullscreen];
else
[self updateSubviewZOrderNormal];
}
- (void)updateSubviewZOrderNormal {
base::scoped_nsobject<NSMutableArray> subviews([[NSMutableArray alloc] init]);
if ([downloadShelfController_ view])
[subviews addObject:[downloadShelfController_ view]];
if ([bookmarkBarController_ view])
[subviews addObject:[bookmarkBarController_ view]];
if ([toolbarController_ view])
[subviews addObject:[toolbarController_ view]];
if ([infoBarContainerController_ view])
[subviews addObject:[infoBarContainerController_ view]];
if ([self tabContentArea])
[subviews addObject:[self tabContentArea]];
if ([findBarCocoaController_ view])
[subviews addObject:[findBarCocoaController_ view]];
[self setContentViewSubviews:subviews];
}
- (void)updateSubviewZOrderFullscreen {
base::scoped_nsobject<NSMutableArray> subviews([[NSMutableArray alloc] init]);
if ([downloadShelfController_ view])
[subviews addObject:[downloadShelfController_ view]];
if ([self tabContentArea])
[subviews addObject:[self tabContentArea]];
if ([self placeBookmarkBarBelowInfoBar]) {
if ([bookmarkBarController_ view])
[subviews addObject:[bookmarkBarController_ view]];
if (floatingBarBackingView_)
[subviews addObject:floatingBarBackingView_];
} else {
if (floatingBarBackingView_)
[subviews addObject:floatingBarBackingView_];
if ([bookmarkBarController_ view])
[subviews addObject:[bookmarkBarController_ view]];
}
if ([toolbarController_ view])
[subviews addObject:[toolbarController_ view]];
if ([infoBarContainerController_ view])
[subviews addObject:[infoBarContainerController_ view]];
if ([findBarCocoaController_ view])
[subviews addObject:[findBarCocoaController_ view]];
[self setContentViewSubviews:subviews];
}
- (void)setContentViewSubviews:(NSArray*)subviews {
// Subviews already match.
if ([[self.chromeContentView subviews] isEqual:subviews])
return;
// The tabContentArea isn't a subview, so just set all the subviews.
NSView* tabContentArea = [self tabContentArea];
if (![[self.chromeContentView subviews] containsObject:tabContentArea]) {
[self.chromeContentView setSubviews:subviews];
return;
}
// Remove all subviews that aren't the tabContentArea.
for (NSView* view in [[[self.chromeContentView subviews] copy] autorelease]) {
if (view != tabContentArea)
[view removeFromSuperview];
}
// Add in the subviews below the tabContentArea.
NSInteger index = [subviews indexOfObject:tabContentArea];
for (int i = index - 1; i >= 0; --i) {
NSView* view = [subviews objectAtIndex:i];
[self.chromeContentView addSubview:view
positioned:NSWindowBelow
relativeTo:nil];
}
// Add in the subviews above the tabContentArea.
for (NSUInteger i = index + 1; i < [subviews count]; ++i) {
NSView* view = [subviews objectAtIndex:i];
[self.chromeContentView addSubview:view
positioned:NSWindowAbove
relativeTo:nil];
}
}
+ (BOOL)systemSettingsRequireMavericksAppKitFullscreenHack {
if (!base::mac::IsOSMavericks())
return NO;
return [NSScreen respondsToSelector:@selector(screensHaveSeparateSpaces)] &&
[NSScreen screensHaveSeparateSpaces];
}
- (BOOL)shouldUseMavericksAppKitFullscreenHack {
if (![[self class] systemSettingsRequireMavericksAppKitFullscreenHack])
return NO;
if (!enteringAppKitFullscreen_)
return NO;
if (enteringAppKitFullscreenOnPrimaryScreen_)
return NO;
return YES;
}
- (BOOL)shouldUseCustomAppKitFullscreenTransition:(BOOL)enterFullScreen {
// Disable the custom exit animation in OSX 10.9: http://crbug.com/526327#c3.
if (base::mac::IsOSMavericks() && !enterFullScreen)
return NO;
NSView* root = [[self.window contentView] superview];
if (!root.layer)
return NO;
// AppKit on OSX 10.9 has a bug for applications linked against OSX 10.8 SDK
// and earlier. Under specific circumstances, it prevents the custom AppKit
// transition from working well. See http://crbug.com/396980 for more
// details.
if ([[self class] systemSettingsRequireMavericksAppKitFullscreenHack] &&
![[[self window] screen] isEqual:[[NSScreen screens] firstObject]]) {
return NO;
}
return YES;
}
- (void)resetCustomAppKitFullscreenVariables {
[self setSheetHiddenForFullscreenTransition:NO];
[self.chromeContentView setAutoresizesSubviews:YES];
fullscreenTransition_.reset();
[[tabStripController_ activeTabContentsController]
setBlockFullscreenResize:NO];
blockLayoutSubviews_ = NO;
enteringAppKitFullscreen_ = NO;
exitingAppKitFullscreen_ = NO;
isUsingCustomAnimation_ = NO;
}
- (NSArray*)customWindowsToEnterFullScreenForWindow:(NSWindow*)window {
DCHECK([window isEqual:self.window]);
if (![self shouldUseCustomAppKitFullscreenTransition:YES])
return nil;
NSWindow* lowPowerWindow = nil;
static const bool fullscreen_low_power_disabled_at_command_line =
base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kDisableFullscreenLowPowerMode);
if (!fullscreen_low_power_disabled_at_command_line) {
WebContents* webContents = [self webContents];
if (webContents && webContents->GetRenderWidgetHostView()) {
fullscreenLowPowerCoordinator_.reset(
new FullscreenLowPowerCoordinatorCocoa(
[self window], webContents->GetRenderWidgetHostView()
->GetAcceleratedWidgetMac()));
lowPowerWindow =
fullscreenLowPowerCoordinator_->GetFullscreenLowPowerWindow();
}
}
fullscreenTransition_.reset(
[[BrowserWindowFullscreenTransition alloc] initEnterWithController:self]);
NSArray* customWindows =
[fullscreenTransition_ customWindowsForFullScreenTransition];
if (customWindows && lowPowerWindow)
customWindows = [customWindows arrayByAddingObject:lowPowerWindow];
else
fullscreenLowPowerCoordinator_.reset();
isUsingCustomAnimation_ = customWindows != nil;
return customWindows;
}
- (NSArray*)customWindowsToExitFullScreenForWindow:(NSWindow*)window {
DCHECK([window isEqual:self.window]);
if (![self shouldUseCustomAppKitFullscreenTransition:NO])
return nil;
fullscreenTransition_.reset(
[[BrowserWindowFullscreenTransition alloc] initExitWithController:self]);
NSArray* customWindows =
[fullscreenTransition_ customWindowsForFullScreenTransition];
isUsingCustomAnimation_ = customWindows != nil;
return customWindows;
}
- (void)window:(NSWindow*)window
startCustomAnimationToEnterFullScreenWithDuration:(NSTimeInterval)duration {
DCHECK([window isEqual:self.window]);
[fullscreenTransition_ startCustomFullScreenAnimationWithDuration:duration];
}
- (void)window:(NSWindow*)window
startCustomAnimationToExitFullScreenWithDuration:(NSTimeInterval)duration {
DCHECK([window isEqual:self.window]);
[fullscreenTransition_ startCustomFullScreenAnimationWithDuration:duration];
base::AutoReset<BOOL> autoReset(&blockLayoutSubviews_, NO);
[self adjustUIForExitingFullscreen];
}
- (BOOL)shouldConstrainFrameRect {
if ([fullscreenTransition_ shouldWindowBeUnconstrained])
return NO;
return [super shouldConstrainFrameRect];
}
- (WebContents*)webContents {
return browser_->tab_strip_model()->GetActiveWebContents();
}
- (PermissionBubbleManager*)permissionBubbleManager {
if (WebContents* contents = [self webContents])
return PermissionBubbleManager::FromWebContents(contents);
return nil;
}
- (BOOL)isFullscreenForTabContentOrExtension {
FullscreenController* controller =
browser_->exclusive_access_manager()->fullscreen_controller();
return controller->IsWindowFullscreenForTabOrPending() ||
controller->IsExtensionFullscreenOrPending();
}
- (void)windowWillBeginSheet:(NSNotification*)notification {
if (fullscreenLowPowerCoordinator_)
fullscreenLowPowerCoordinator_->SetHasActiveSheet(true);
}
- (void)windowDidEndSheet:(NSNotification*)notification {
if (fullscreenLowPowerCoordinator_)
fullscreenLowPowerCoordinator_->SetHasActiveSheet(false);
}
- (void)childWindowsDidChange {
if (fullscreenLowPowerCoordinator_)
fullscreenLowPowerCoordinator_->ChildWindowsChanged();
}
@end // @implementation BrowserWindowController(Private)