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"
9 #include "base/command_line.h"
10 #import "base/mac/scoped_nsobject.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/prefs/scoped_user_pref_update.h"
13 #include "chrome/browser/browser_process.h"
14 #include "chrome/browser/fullscreen.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/browser/profiles/profile_info_util.h"
17 #include "chrome/browser/ui/bookmarks/bookmark_tab_helper.h"
18 #include "chrome/browser/ui/browser.h"
19 #include "chrome/browser/ui/browser_window_state.h"
20 #import "chrome/browser/ui/cocoa/browser/avatar_button_controller.h"
21 #import "chrome/browser/ui/cocoa/dev_tools_controller.h"
22 #import "chrome/browser/ui/cocoa/fast_resize_view.h"
23 #import "chrome/browser/ui/cocoa/find_bar/find_bar_cocoa_controller.h"
24 #import "chrome/browser/ui/cocoa/floating_bar_backing_view.h"
25 #import "chrome/browser/ui/cocoa/framed_browser_window.h"
26 #import "chrome/browser/ui/cocoa/fullscreen_mode_controller.h"
27 #import "chrome/browser/ui/cocoa/fullscreen_window.h"
28 #import "chrome/browser/ui/cocoa/infobars/infobar_container_controller.h"
29 #include "chrome/browser/ui/cocoa/last_active_browser_cocoa.h"
30 #import "chrome/browser/ui/cocoa/nsview_additions.h"
31 #import "chrome/browser/ui/cocoa/presentation_mode_controller.h"
32 #import "chrome/browser/ui/cocoa/status_bubble_mac.h"
33 #import "chrome/browser/ui/cocoa/tab_contents/overlayable_contents_controller.h"
34 #import "chrome/browser/ui/cocoa/tabs/tab_strip_controller.h"
35 #import "chrome/browser/ui/cocoa/tabs/tab_strip_view.h"
36 #import "chrome/browser/ui/cocoa/toolbar/toolbar_controller.h"
37 #include "chrome/browser/ui/fullscreen/fullscreen_controller.h"
38 #include "chrome/browser/ui/tabs/tab_strip_model.h"
39 #include "chrome/common/chrome_switches.h"
40 #include "chrome/common/pref_names.h"
41 #include "content/public/browser/render_widget_host_view.h"
42 #include "content/public/browser/web_contents.h"
43 #include "content/public/browser/web_contents_view.h"
44 #import "ui/base/cocoa/focus_tracker.h"
45 #include "ui/base/ui_base_types.h"
47 using content::RenderWidgetHostView;
48 using content::WebContents;
52 // Space between the incognito badge and the right edge of the window.
53 const CGFloat kAvatarRightOffset = 4;
55 // The amount by which to shrink the tab strip (on the right) when the
56 // incognito badge is present.
57 const CGFloat kAvatarTabStripShrink = 18;
59 // Insets for the location bar, used when the full toolbar is hidden.
60 // TODO(viettrungluu): We can argue about the "correct" insetting; I like the
61 // following best, though arguably 0 inset is better/more correct.
62 const CGFloat kLocBarLeftRightInset = 1;
63 const CGFloat kLocBarTopInset = 0;
64 const CGFloat kLocBarBottomInset = 1;
68 @implementation BrowserWindowController(Private)
70 // Create the tab strip controller.
71 - (void)createTabStripController {
72 DCHECK([overlayableContentsController_ activeContainer]);
73 DCHECK([[overlayableContentsController_ activeContainer] window]);
74 tabStripController_.reset([[TabStripController alloc]
75 initWithView:[self tabStripView]
76 switchView:[overlayableContentsController_ activeContainer]
77 browser:browser_.get()
81 - (void)saveWindowPositionIfNeeded {
82 if (!chrome::ShouldSaveWindowPlacement(browser_.get()))
85 // If we're in fullscreen mode, save the position of the regular window
87 NSWindow* window = [self isFullscreen] ? savedRegularWindow_ : [self window];
89 // Window positions are stored relative to the origin of the primary monitor.
90 NSRect monitorFrame = [[[NSScreen screens] objectAtIndex:0] frame];
91 NSScreen* windowScreen = [window screen];
93 // Start with the window's frame, which is in virtual coordinates.
94 // Do some y twiddling to flip the coordinate system.
95 gfx::Rect bounds(NSRectToCGRect([window frame]));
96 bounds.set_y(monitorFrame.size.height - bounds.y() - bounds.height());
98 // Browser::SaveWindowPlacement saves information for session restore.
99 ui::WindowShowState show_state = ui::SHOW_STATE_NORMAL;
100 if ([window isMiniaturized])
101 show_state = ui::SHOW_STATE_MINIMIZED;
102 else if ([self isFullscreen])
103 show_state = ui::SHOW_STATE_FULLSCREEN;
104 chrome::SaveWindowPlacement(browser_.get(), bounds, show_state);
106 // |windowScreen| can be nil (for example, if the monitor arrangement was
107 // changed while in fullscreen mode). If we see a nil screen, return without
109 // TODO(rohitrao): We should just not save anything for fullscreen windows.
110 // http://crbug.com/36479.
114 // Only save main window information to preferences.
115 PrefService* prefs = browser_->profile()->GetPrefs();
116 if (!prefs || browser_ != chrome::GetLastActiveBrowser())
119 // Save the current work area, in flipped coordinates.
120 gfx::Rect workArea(NSRectToCGRect([windowScreen visibleFrame]));
121 workArea.set_y(monitorFrame.size.height - workArea.y() - workArea.height());
123 DictionaryPrefUpdate update(
125 chrome::GetWindowPlacementKey(browser_.get()).c_str());
126 base::DictionaryValue* windowPreferences = update.Get();
127 windowPreferences->SetInteger("left", bounds.x());
128 windowPreferences->SetInteger("top", bounds.y());
129 windowPreferences->SetInteger("right", bounds.right());
130 windowPreferences->SetInteger("bottom", bounds.bottom());
131 windowPreferences->SetBoolean("maximized", false);
132 windowPreferences->SetBoolean("always_on_top", false);
133 windowPreferences->SetInteger("work_area_left", workArea.x());
134 windowPreferences->SetInteger("work_area_top", workArea.y());
135 windowPreferences->SetInteger("work_area_right", workArea.right());
136 windowPreferences->SetInteger("work_area_bottom", workArea.bottom());
139 - (NSRect)window:(NSWindow*)window
140 willPositionSheet:(NSWindow*)sheet
141 usingRect:(NSRect)defaultSheetRect {
142 // Position the sheet as follows:
143 // - If the bookmark bar is hidden or shown as a bubble (on the NTP when the
144 // bookmark bar is disabled), position the sheet immediately below the
146 // - If the bookmark bar is shown (attached to the normal toolbar), position
147 // the sheet below the bookmark bar.
148 // - If the bookmark bar is currently animating, position the sheet according
149 // to where the bar will be when the animation ends.
150 switch ([bookmarkBarController_ currentState]) {
151 case BookmarkBar::SHOW: {
152 NSRect bookmarkBarFrame = [[bookmarkBarController_ view] frame];
153 defaultSheetRect.origin.y = bookmarkBarFrame.origin.y;
156 case BookmarkBar::HIDDEN:
157 case BookmarkBar::DETACHED: {
158 if ([self hasToolbar]) {
159 NSRect toolbarFrame = [[toolbarController_ view] frame];
160 defaultSheetRect.origin.y = toolbarFrame.origin.y;
162 // The toolbar is not shown in application mode. The sheet should be
163 // located at the top of the window, under the title of the window.
164 defaultSheetRect.origin.y = NSHeight([[window contentView] frame]) -
165 defaultSheetRect.size.height;
170 return defaultSheetRect;
173 - (void)layoutSubviews {
174 // With the exception of the top tab strip, the subviews which we lay out are
175 // subviews of the content view, so we mainly work in the content view's
176 // coordinate system. Note, however, that the content view's coordinate system
177 // and the window's base coordinate system should coincide.
178 NSWindow* window = [self window];
179 NSView* contentView = [window contentView];
180 NSRect contentBounds = [contentView bounds];
181 CGFloat minX = NSMinX(contentBounds);
182 CGFloat minY = NSMinY(contentBounds);
183 CGFloat width = NSWidth(contentBounds);
185 BOOL useSimplifiedFullscreen = CommandLine::ForCurrentProcess()->HasSwitch(
186 switches::kEnableSimplifiedFullscreen);
188 // Suppress title drawing if necessary.
189 if ([window respondsToSelector:@selector(setShouldHideTitle:)])
190 [(id)window setShouldHideTitle:![self hasTitleBar]];
192 // Update z-order. The code below depends on this.
193 [self updateSubviewZOrder:[self inPresentationMode]];
195 BOOL inPresentationMode = [self inPresentationMode];
196 CGFloat floatingBarHeight = [self floatingBarHeight];
197 // In presentation mode, |yOffset| accounts for the sliding position of the
198 // floating bar and the extra offset needed to dodge the menu bar.
199 CGFloat yOffset = inPresentationMode && !useSimplifiedFullscreen ?
200 (std::floor((1 - floatingBarShownFraction_) * floatingBarHeight) -
201 [presentationModeController_ floatingBarVerticalOffset]) : 0;
202 CGFloat maxY = NSMaxY(contentBounds) + yOffset;
204 if ([self hasTabStrip]) {
205 // If we need to lay out the top tab strip, replace |maxY| with a higher
206 // value, and then lay out the tab strip.
207 NSRect windowFrame = [contentView convertRect:[window frame] fromView:nil];
208 maxY = NSHeight(windowFrame) + yOffset;
209 if (useSimplifiedFullscreen && [self isFullscreen]) {
210 CGFloat tabStripHeight = NSHeight([[self tabStripView] frame]);
211 CGFloat revealAmount = (1 - floatingBarShownFraction_) * tabStripHeight;
212 // In simplified fullscreen, only the toolbar is visible by default, and
213 // the tabstrip and menu bar come down (each separately) when the user
214 // mouses near the top of the window. Push the maxY of the toolbar up by
215 // the amount of the tabstrip that is revealed, while removing the amount
216 // of space needed by the menu bar.
218 revealAmount - [fullscreenModeController_ menuBarHeight]);
220 maxY = [self layoutTabStripAtMaxY:maxY
222 fullscreen:[self isFullscreen]];
225 // Sanity-check |maxY|.
226 DCHECK_GE(maxY, minY);
227 DCHECK_LE(maxY, NSMaxY(contentBounds) + yOffset);
229 // Place the toolbar at the top of the reserved area.
230 maxY = [self layoutToolbarAtMinX:minX maxY:maxY width:width];
232 // If we're not displaying the bookmark bar below the info bar, then it goes
233 // immediately below the toolbar.
234 BOOL placeBookmarkBarBelowInfoBar = [self placeBookmarkBarBelowInfoBar];
235 if (!placeBookmarkBarBelowInfoBar)
236 maxY = [self layoutBookmarkBarAtMinX:minX maxY:maxY width:width];
238 // The floating bar backing view doesn't actually add any height.
239 NSRect floatingBarBackingRect =
240 NSMakeRect(minX, maxY, width, floatingBarHeight);
241 [self layoutFloatingBarBackingView:floatingBarBackingRect
242 presentationMode:inPresentationMode];
244 // Place the find bar immediately below the toolbar/attached bookmark bar. In
245 // presentation mode, it hangs off the top of the screen when the bar is
247 [findBarCocoaController_ positionFindBarViewAtMaxY:maxY maxWidth:width];
248 [fullscreenExitBubbleController_ positionInWindowAtTop:maxY width:width];
250 // If in presentation mode, reset |maxY| to top of screen, so that the
251 // floating bar slides over the things which appear to be in the content area.
252 if (inPresentationMode ||
253 (useSimplifiedFullscreen && !fullscreenUrl_.is_empty())) {
254 maxY = NSMaxY(contentBounds);
257 // Also place the info bar container immediate below the toolbar, except in
258 // presentation mode in which case it's at the top of the visual content area.
259 maxY = [self layoutInfoBarAtMinX:minX maxY:maxY width:width];
261 // If the bookmark bar is detached, place it next in the visual content area.
262 if (placeBookmarkBarBelowInfoBar)
263 maxY = [self layoutBookmarkBarAtMinX:minX maxY:maxY width:width];
265 // Place the download shelf, if any, at the bottom of the view.
266 minY = [self layoutDownloadShelfAtMinX:minX minY:minY width:width];
268 // Finally, the content area takes up all of the remaining space.
269 NSRect contentAreaRect = NSMakeRect(minX, minY, width, maxY - minY);
270 [self layoutTabContentArea:contentAreaRect];
272 // Normally, we don't need to tell the toolbar whether or not to show the
273 // divider, but things break down during animation.
274 [toolbarController_ setDividerOpacity:[self toolbarDividerOpacity]];
277 - (CGFloat)floatingBarHeight {
278 if (![self inPresentationMode])
281 CGFloat totalHeight = [presentationModeController_ floatingBarVerticalOffset];
283 if ([self hasTabStrip])
284 totalHeight += NSHeight([[self tabStripView] frame]);
286 if ([self hasToolbar]) {
287 totalHeight += NSHeight([[toolbarController_ view] frame]);
288 } else if ([self hasLocationBar]) {
289 totalHeight += NSHeight([[toolbarController_ view] frame]) +
290 kLocBarTopInset + kLocBarBottomInset;
293 if (![self placeBookmarkBarBelowInfoBar])
294 totalHeight += NSHeight([[bookmarkBarController_ view] frame]);
299 - (CGFloat)layoutTabStripAtMaxY:(CGFloat)maxY
301 fullscreen:(BOOL)fullscreen {
302 // Nothing to do if no tab strip.
303 if (![self hasTabStrip])
306 NSView* tabStripView = [self tabStripView];
307 CGFloat tabStripHeight = NSHeight([tabStripView frame]);
308 maxY -= tabStripHeight;
309 [tabStripView setFrame:NSMakeRect(0, maxY, width, tabStripHeight)];
311 // Set left indentation based on fullscreen mode status.
312 [tabStripController_ setLeftIndentForControls:(fullscreen ? 0 :
313 [[tabStripController_ class] defaultLeftIndentForControls])];
315 // Lay out the icognito/avatar badge because calculating the indentation on
316 // the right depends on it.
317 if ([self shouldShowAvatar]) {
318 NSView* avatarButton = [avatarButtonController_ view];
319 CGFloat buttonHeight = std::min(
320 static_cast<CGFloat>(profiles::kAvatarIconHeight), tabStripHeight);
321 [avatarButton setFrameSize:NSMakeSize(NSWidth([avatarButton frame]),
324 // Actually place the badge *above* |maxY|, by +2 to miss the divider.
325 CGFloat badgeXOffset = -kAvatarRightOffset;
326 CGFloat badgeYOffset = 2 * [[avatarButton superview] cr_lineWidth];
328 NSMakePoint(width - NSWidth([avatarButton frame]) + badgeXOffset,
329 maxY + badgeYOffset);
330 [avatarButton setFrameOrigin:origin];
331 [avatarButton setHidden:NO]; // Make sure it's shown.
334 // Calculate the right indentation. The default indentation built into the
335 // tabstrip leaves enough room for the fullscreen button or presentation mode
336 // toggle button on Lion. On non-Lion systems, the right indent needs to be
337 // adjusted to make room for the new tab button when an avatar is present.
338 CGFloat rightIndent = 0;
339 if (base::mac::IsOSLionOrLater() &&
340 [[self window] isKindOfClass:[FramedBrowserWindow class]]) {
341 FramedBrowserWindow* window =
342 static_cast<FramedBrowserWindow*>([self window]);
343 rightIndent += -[window fullScreenButtonOriginAdjustment].x;
344 } else if ([self shouldShowAvatar]) {
345 rightIndent += kAvatarTabStripShrink;
346 NSButton* labelButton = [avatarButtonController_ labelButtonView];
348 rightIndent += NSWidth([labelButton frame]) + kAvatarRightOffset;
350 [tabStripController_ setRightIndentForControls:rightIndent];
352 // Go ahead and layout the tabs.
353 [tabStripController_ layoutTabsWithoutAnimation];
358 - (CGFloat)layoutToolbarAtMinX:(CGFloat)minX
360 width:(CGFloat)width {
361 NSView* toolbarView = [toolbarController_ view];
362 NSRect toolbarFrame = [toolbarView frame];
363 if ([self hasToolbar]) {
364 // The toolbar is present in the window, so we make room for it.
365 DCHECK(![toolbarView isHidden]);
366 toolbarFrame.origin.x = minX;
367 toolbarFrame.origin.y = maxY - NSHeight(toolbarFrame);
368 toolbarFrame.size.width = width;
369 maxY -= NSHeight(toolbarFrame);
371 if ([self hasLocationBar]) {
372 // Location bar is present with no toolbar. Put a border of
373 // |kLocBar...Inset| pixels around the location bar.
374 // TODO(viettrungluu): This is moderately ridiculous. The toolbar should
375 // really be aware of what its height should be (the way the toolbar
376 // compression stuff is currently set up messes things up).
377 DCHECK(![toolbarView isHidden]);
378 toolbarFrame.origin.x = kLocBarLeftRightInset;
379 toolbarFrame.origin.y = maxY - NSHeight(toolbarFrame) - kLocBarTopInset;
380 toolbarFrame.size.width = width - 2 * kLocBarLeftRightInset;
381 maxY -= kLocBarTopInset + NSHeight(toolbarFrame) + kLocBarBottomInset;
383 DCHECK([toolbarView isHidden]);
386 [toolbarView setFrame:toolbarFrame];
390 - (BOOL)placeBookmarkBarBelowInfoBar {
391 // If we are currently displaying the NTP detached bookmark bar or animating
392 // to/from it (from/to anything else), we display the bookmark bar below the
394 return [bookmarkBarController_ isInState:BookmarkBar::DETACHED] ||
395 [bookmarkBarController_ isAnimatingToState:BookmarkBar::DETACHED] ||
396 [bookmarkBarController_ isAnimatingFromState:BookmarkBar::DETACHED];
399 - (CGFloat)layoutBookmarkBarAtMinX:(CGFloat)minX
401 width:(CGFloat)width {
402 [bookmarkBarController_ updateHiddenState];
404 NSView* bookmarkBarView = [bookmarkBarController_ view];
405 NSRect frame = [bookmarkBarView frame];
406 frame.origin.x = minX;
407 frame.origin.y = maxY - NSHeight(frame);
408 frame.size.width = width;
409 [bookmarkBarView setFrame:frame];
410 maxY -= NSHeight(frame);
412 // Pin the bookmark bar to the top of the window and make the width flexible.
413 [bookmarkBarView setAutoresizingMask:NSViewWidthSizable | NSViewMinYMargin];
415 // TODO(viettrungluu): Does this really belong here? Calling it shouldn't be
416 // necessary in the non-NTP case.
417 [bookmarkBarController_ layoutSubviews];
422 - (void)layoutFloatingBarBackingView:(NSRect)frame
423 presentationMode:(BOOL)presentationMode {
424 // Only display when in presentation mode.
425 if (presentationMode) {
426 // For certain window types such as app windows (e.g., the dev tools
427 // window), there's no actual overlay. (Displaying one would result in an
428 // overly sliding in only under the menu, which gives an ugly effect.)
429 if (floatingBarBackingView_.get()) {
431 [floatingBarBackingView_ setFrame:frame];
434 // But we want the logic to work as usual (for show/hide/etc. purposes).
435 [presentationModeController_ overlayFrameChanged:frame];
437 // Okay to call even if |floatingBarBackingView_| is nil.
438 if ([floatingBarBackingView_ superview])
439 [floatingBarBackingView_ removeFromSuperview];
443 - (CGFloat)layoutInfoBarAtMinX:(CGFloat)minX
445 width:(CGFloat)width {
446 NSView* containerView = [infoBarContainerController_ view];
447 NSRect containerFrame = [containerView frame];
448 maxY -= NSHeight(containerFrame);
449 maxY += [infoBarContainerController_ overlappingTipHeight];
450 containerFrame.origin.x = minX;
451 containerFrame.origin.y = maxY;
452 containerFrame.size.width = width;
453 [containerView setFrame:containerFrame];
457 - (CGFloat)layoutDownloadShelfAtMinX:(CGFloat)minX
459 width:(CGFloat)width {
460 if (downloadShelfController_.get()) {
461 NSView* downloadView = [downloadShelfController_ view];
462 NSRect downloadFrame = [downloadView frame];
463 downloadFrame.origin.x = minX;
464 downloadFrame.origin.y = minY;
465 downloadFrame.size.width = width;
466 [downloadView setFrame:downloadFrame];
467 minY += NSHeight(downloadFrame);
472 - (void)layoutTabContentArea:(NSRect)newFrame {
473 NSView* tabContentView = [self tabContentArea];
474 NSRect tabContentFrame = [tabContentView frame];
476 bool contentShifted =
477 NSMaxY(tabContentFrame) != NSMaxY(newFrame) ||
478 NSMinX(tabContentFrame) != NSMinX(newFrame);
480 tabContentFrame = newFrame;
481 [tabContentView setFrame:tabContentFrame];
483 // If the relayout shifts the content area up or down, let the renderer know.
484 if (contentShifted) {
485 if (WebContents* contents =
486 browser_->tab_strip_model()->GetActiveWebContents()) {
487 if (RenderWidgetHostView* rwhv = contents->GetRenderWidgetHostView())
488 rwhv->WindowFrameChanged();
493 - (void)adjustToolbarAndBookmarkBarForCompression:(CGFloat)compression {
495 [toolbarController_ desiredHeightForCompression:compression];
496 NSRect toolbarFrame = [[toolbarController_ view] frame];
497 CGFloat deltaH = newHeight - toolbarFrame.size.height;
502 toolbarFrame.size.height = newHeight;
503 NSRect bookmarkFrame = [[bookmarkBarController_ view] frame];
504 bookmarkFrame.size.height = bookmarkFrame.size.height - deltaH;
505 [[toolbarController_ view] setFrame:toolbarFrame];
506 [[bookmarkBarController_ view] setFrame:bookmarkFrame];
507 [self layoutSubviews];
510 // Fullscreen and presentation mode methods
512 - (BOOL)shouldShowPresentationModeToggle {
513 return chrome::mac::SupportsSystemFullscreen() && [self isFullscreen];
516 - (void)moveViewsForFullscreenForSnowLeopard:(BOOL)fullscreen
517 regularWindow:(NSWindow*)regularWindow
518 fullscreenWindow:(NSWindow*)fullscreenWindow {
519 // This method is only for systems without fullscreen support.
520 DCHECK(!chrome::mac::SupportsSystemFullscreen());
522 NSWindow* sourceWindow = fullscreen ? regularWindow : fullscreenWindow;
523 NSWindow* destWindow = fullscreen ? fullscreenWindow : regularWindow;
525 // Close the bookmark bubble, if it's open. Use |-ok:| instead of |-cancel:|
526 // or |-close| because that matches the behavior when the bubble loses key
528 [bookmarkBubbleController_ ok:self];
530 // Save the current first responder so we can restore after views are moved.
531 base::scoped_nsobject<FocusTracker> focusTracker(
532 [[FocusTracker alloc] initWithWindow:sourceWindow]);
534 // While we move views (and focus) around, disable any bar visibility changes.
535 [self disableBarVisibilityUpdates];
537 // Retain the tab strip view while we remove it from its superview.
538 base::scoped_nsobject<NSView> tabStripView;
539 if ([self hasTabStrip]) {
540 tabStripView.reset([[self tabStripView] retain]);
541 [tabStripView removeFromSuperview];
544 // Ditto for the content view.
545 base::scoped_nsobject<NSView> contentView(
546 [[sourceWindow contentView] retain]);
547 // Disable autoresizing of subviews while we move views around. This prevents
548 // spurious renderer resizes.
549 [contentView setAutoresizesSubviews:NO];
550 [contentView removeFromSuperview];
552 // Have to do this here, otherwise later calls can crash because the window
554 [sourceWindow setDelegate:nil];
555 [destWindow setDelegate:self];
557 // With this call, valgrind complains that a "Conditional jump or move depends
558 // on uninitialised value(s)". The error happens in -[NSThemeFrame
559 // drawOverlayRect:]. I'm pretty convinced this is an Apple bug, but there is
560 // no visual impact. I have been unable to tickle it away with other window
561 // or view manipulation Cocoa calls. Stack added to suppressions_mac.txt.
562 [contentView setAutoresizesSubviews:YES];
563 [destWindow setContentView:contentView];
565 // Move the incognito badge if present.
566 if ([self shouldShowAvatar]) {
567 [[avatarButtonController_ view] removeFromSuperview];
568 [[avatarButtonController_ view] setHidden:YES]; // Will be shown in layout.
569 [[[destWindow contentView] superview] addSubview:
570 [avatarButtonController_ view]];
573 // Add the tab strip after setting the content view and moving the incognito
574 // badge (if any), so that the tab strip will be on top (in the z-order).
575 if ([self hasTabStrip])
576 [[[destWindow contentView] superview] addSubview:tabStripView];
578 [sourceWindow setWindowController:nil];
579 [self setWindow:destWindow];
580 [destWindow setWindowController:self];
582 // Move the status bubble over, if we have one.
584 statusBubble_->SwitchParentWindow(destWindow);
586 // Move the title over.
587 [destWindow setTitle:[sourceWindow title]];
589 // The window needs to be onscreen before we can set its first responder.
590 // Ordering the window to the front can change the active Space (either to
591 // the window's old Space or to the application's assigned Space). To prevent
592 // this by temporarily change the collectionBehavior.
593 NSWindowCollectionBehavior behavior = [sourceWindow collectionBehavior];
594 [destWindow setCollectionBehavior:
595 NSWindowCollectionBehaviorMoveToActiveSpace];
596 [destWindow makeKeyAndOrderFront:self];
597 [destWindow setCollectionBehavior:behavior];
599 [focusTracker restoreFocusInWindow:destWindow];
600 [sourceWindow orderOut:self];
602 // We're done moving focus, so re-enable bar visibility changes.
603 [self enableBarVisibilityUpdates];
606 - (void)setPresentationModeInternal:(BOOL)presentationMode
607 forceDropdown:(BOOL)forceDropdown {
608 if (presentationMode == [self inPresentationMode])
611 if (presentationMode) {
612 BOOL fullscreen_for_tab =
613 browser_->fullscreen_controller()->IsFullscreenForTabOrPending();
615 CommandLine::ForCurrentProcess()->HasSwitch(switches::kKioskMode);
616 BOOL showDropdown = !fullscreen_for_tab &&
618 (forceDropdown || [self floatingBarHasFocus]);
619 NSView* contentView = [[self window] contentView];
620 presentationModeController_.reset(
621 [[PresentationModeController alloc] initWithBrowserController:self]);
622 [presentationModeController_ enterPresentationModeForContentView:contentView
623 showDropdown:showDropdown];
625 [presentationModeController_ exitPresentationMode];
626 presentationModeController_.reset();
629 [self adjustUIForPresentationMode:presentationMode];
630 [self layoutSubviews];
633 // TODO(rohitrao): This method is misnamed now, since there is a flag that
634 // enables 10.6-style fullscreen on newer OSes.
635 - (void)enterFullscreenForSnowLeopard {
637 const CGDisplayReservationInterval kFadeDurationSeconds = 0.6;
638 Boolean didFadeOut = NO;
639 CGDisplayFadeReservationToken token;
640 if (CGAcquireDisplayFadeReservation(kFadeDurationSeconds, &token)
641 == kCGErrorSuccess) {
643 CGDisplayFade(token, kFadeDurationSeconds / 2, kCGDisplayBlendNormal,
644 kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, /*synchronous=*/true);
647 // Create the fullscreen window. After this line, isFullscreen will return
649 fullscreenWindow_.reset([[self createFullscreenWindow] retain]);
650 savedRegularWindow_ = [[self window] retain];
651 savedRegularWindowFrame_ = [savedRegularWindow_ frame];
653 [self moveViewsForFullscreenForSnowLeopard:YES
654 regularWindow:[self window]
655 fullscreenWindow:fullscreenWindow_.get()];
657 // When simplified fullscreen is enabled, do not enter presentation mode.
658 const CommandLine* command_line = CommandLine::ForCurrentProcess();
659 if (command_line->HasSwitch(switches::kEnableSimplifiedFullscreen)) {
660 // TODO(rohitrao): Add code to manage the menubar here.
662 [self adjustUIForPresentationMode:YES];
663 [self setPresentationModeInternal:YES forceDropdown:NO];
666 [self layoutSubviews];
668 [self windowDidEnterFullScreen:nil];
672 CGDisplayFade(token, kFadeDurationSeconds / 2, kCGDisplayBlendSolidColor,
673 kCGDisplayBlendNormal, 0.0, 0.0, 0.0, /*synchronous=*/false);
674 CGReleaseDisplayFadeReservation(token);
678 - (void)exitFullscreenForSnowLeopard {
679 // TODO(rohitrao): This method is misnamed now, since there is a flag that
680 // enables 10.6-style fullscreen on newer OSes.
681 DCHECK(!chrome::mac::SupportsSystemFullscreen());
684 const CGDisplayReservationInterval kFadeDurationSeconds = 0.6;
685 Boolean didFadeOut = NO;
686 CGDisplayFadeReservationToken token;
687 if (CGAcquireDisplayFadeReservation(kFadeDurationSeconds, &token)
688 == kCGErrorSuccess) {
690 CGDisplayFade(token, kFadeDurationSeconds / 2, kCGDisplayBlendNormal,
691 kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, /*synchronous=*/true);
694 // When simplified fullscreen is enabled, the menubar status is managed
696 const CommandLine* command_line = CommandLine::ForCurrentProcess();
697 if (command_line->HasSwitch(switches::kEnableSimplifiedFullscreen)) {
698 // TODO(rohitrao): Add code to manage the menubar here.
701 [self windowWillExitFullScreen:nil];
703 [self moveViewsForFullscreenForSnowLeopard:NO
704 regularWindow:savedRegularWindow_
705 fullscreenWindow:fullscreenWindow_.get()];
707 // When exiting fullscreen mode, we need to call layoutSubviews manually.
708 [savedRegularWindow_ autorelease];
709 savedRegularWindow_ = nil;
710 fullscreenWindow_.reset();
711 [self layoutSubviews];
713 [self windowDidExitFullScreen:nil];
717 CGDisplayFade(token, kFadeDurationSeconds / 2, kCGDisplayBlendSolidColor,
718 kCGDisplayBlendNormal, 0.0, 0.0, 0.0, /*synchronous=*/false);
719 CGReleaseDisplayFadeReservation(token);
723 // TODO(rohitrao): This function has shrunk into uselessness, and
724 // |-setFullscreen:| has grown rather large. Find a good way to break up
725 // |-setFullscreen:| into smaller pieces. http://crbug.com/36449
726 - (void)adjustUIForPresentationMode:(BOOL)fullscreen {
727 // Create the floating bar backing view if necessary.
728 if (fullscreen && !floatingBarBackingView_.get() &&
729 ([self hasTabStrip] || [self hasToolbar] || [self hasLocationBar])) {
730 floatingBarBackingView_.reset(
731 [[FloatingBarBackingView alloc] initWithFrame:NSZeroRect]);
732 [floatingBarBackingView_ setAutoresizingMask:(NSViewWidthSizable |
736 // Force the bookmark bar z-order to update.
737 [[bookmarkBarController_ view] removeFromSuperview];
738 [self updateSubviewZOrder:fullscreen];
739 [self updateAllowOverlappingViews:fullscreen];
742 - (void)showFullscreenExitBubbleIfNecessary {
743 // This method is called in response to
744 // |-updateFullscreenExitBubbleURL:bubbleType:|. If on Lion the system is
745 // transitioning, do not show the bubble because it will cause visual jank
746 // <http://crbug.com/130649>. This will be called again as part of
747 // |-windowDidEnterFullScreen:|, so arrange to do that work then instead.
748 if (enteringFullscreen_)
751 [presentationModeController_ ensureOverlayHiddenWithAnimation:NO delay:NO];
753 if (fullscreenBubbleType_ == FEB_TYPE_NONE ||
754 fullscreenBubbleType_ == FEB_TYPE_BROWSER_FULLSCREEN_EXIT_INSTRUCTION) {
755 // Show no exit instruction bubble on Mac when in Browser Fullscreen.
756 [self destroyFullscreenExitBubbleIfNecessary];
758 [fullscreenExitBubbleController_ closeImmediately];
759 fullscreenExitBubbleController_.reset(
760 [[FullscreenExitBubbleController alloc]
762 browser:browser_.get()
764 bubbleType:fullscreenBubbleType_]);
765 [fullscreenExitBubbleController_ showWindow];
769 - (void)destroyFullscreenExitBubbleIfNecessary {
770 [fullscreenExitBubbleController_ closeImmediately];
771 fullscreenExitBubbleController_.reset();
774 - (void)contentViewDidResize:(NSNotification*)notification {
775 [self layoutSubviews];
778 - (void)registerForContentViewResizeNotifications {
779 [[NSNotificationCenter defaultCenter]
781 selector:@selector(contentViewDidResize:)
782 name:NSViewFrameDidChangeNotification
783 object:[[self window] contentView]];
786 - (void)deregisterForContentViewResizeNotifications {
787 [[NSNotificationCenter defaultCenter]
789 name:NSViewFrameDidChangeNotification
790 object:[[self window] contentView]];
793 - (NSSize)window:(NSWindow*)window
794 willUseFullScreenContentSize:(NSSize)proposedSize {
798 - (NSApplicationPresentationOptions)window:(NSWindow*)window
799 willUseFullScreenPresentationOptions:(NSApplicationPresentationOptions)opt {
801 NSApplicationPresentationAutoHideDock |
802 NSApplicationPresentationAutoHideMenuBar);
805 - (void)windowWillEnterFullScreen:(NSNotification*)notification {
806 [self registerForContentViewResizeNotifications];
808 NSWindow* window = [self window];
809 savedRegularWindowFrame_ = [window frame];
810 BOOL mode = enteringPresentationMode_ ||
811 browser_->fullscreen_controller()->IsFullscreenForTabOrPending();
812 enteringFullscreen_ = YES;
813 [self setPresentationModeInternal:mode forceDropdown:NO];
816 - (void)windowDidEnterFullScreen:(NSNotification*)notification {
817 if (chrome::mac::SupportsSystemFullscreen())
818 [self deregisterForContentViewResizeNotifications];
819 enteringFullscreen_ = NO;
820 enteringPresentationMode_ = NO;
822 const CommandLine* command_line = CommandLine::ForCurrentProcess();
823 if (command_line->HasSwitch(switches::kEnableSimplifiedFullscreen) &&
824 fullscreenUrl_.is_empty()) {
825 fullscreenModeController_.reset([[FullscreenModeController alloc]
826 initWithBrowserWindowController:self]);
829 [self showFullscreenExitBubbleIfNecessary];
830 browser_->WindowFullscreenStateChanged();
833 - (void)windowWillExitFullScreen:(NSNotification*)notification {
834 if (chrome::mac::SupportsSystemFullscreen())
835 [self registerForContentViewResizeNotifications];
836 fullscreenModeController_.reset();
837 [self destroyFullscreenExitBubbleIfNecessary];
838 [self setPresentationModeInternal:NO forceDropdown:NO];
841 - (void)windowDidExitFullScreen:(NSNotification*)notification {
842 if (chrome::mac::SupportsSystemFullscreen())
843 [self deregisterForContentViewResizeNotifications];
844 browser_->WindowFullscreenStateChanged();
847 - (void)windowDidFailToEnterFullScreen:(NSWindow*)window {
848 [self deregisterForContentViewResizeNotifications];
849 enteringFullscreen_ = NO;
850 [self setPresentationModeInternal:NO forceDropdown:NO];
852 // Force a relayout to try and get the window back into a reasonable state.
853 [self layoutSubviews];
856 - (void)windowDidFailToExitFullScreen:(NSWindow*)window {
857 [self deregisterForContentViewResizeNotifications];
859 // Force a relayout to try and get the window back into a reasonable state.
860 [self layoutSubviews];
863 - (void)enableBarVisibilityUpdates {
864 // Early escape if there's nothing to do.
865 if (barVisibilityUpdatesEnabled_)
868 barVisibilityUpdatesEnabled_ = YES;
870 if ([barVisibilityLocks_ count])
871 [presentationModeController_ ensureOverlayShownWithAnimation:NO delay:NO];
873 [presentationModeController_ ensureOverlayHiddenWithAnimation:NO delay:NO];
876 - (void)disableBarVisibilityUpdates {
877 // Early escape if there's nothing to do.
878 if (!barVisibilityUpdatesEnabled_)
881 barVisibilityUpdatesEnabled_ = NO;
882 [presentationModeController_ cancelAnimationAndTimers];
885 - (CGFloat)toolbarDividerOpacity {
886 return [bookmarkBarController_ toolbarDividerOpacity];
889 - (void)updateSubviewZOrder:(BOOL)inPresentationMode {
890 NSView* contentView = [[self window] contentView];
891 NSView* toolbarView = [toolbarController_ view];
893 if (inPresentationMode) {
894 // Toolbar is above tab contents so that it can slide down from top of
896 [contentView cr_ensureSubview:toolbarView
897 isPositioned:NSWindowAbove
898 relativeTo:[self tabContentArea]];
900 // Toolbar is below tab contents so that the info bar arrow can appear above
902 [contentView cr_ensureSubview:toolbarView
903 isPositioned:NSWindowBelow
904 relativeTo:[self tabContentArea]];
907 // The bookmark bar is always below the toolbar.
908 [contentView cr_ensureSubview:[bookmarkBarController_ view]
909 isPositioned:NSWindowBelow
910 relativeTo:toolbarView];
912 if (inPresentationMode) {
913 // In presentation mode the info bar is below all other views.
914 [contentView cr_ensureSubview:[infoBarContainerController_ view]
915 isPositioned:NSWindowBelow
916 relativeTo:[self tabContentArea]];
918 // Above the toolbar but still below tab contents. Similar to the bookmark
919 // bar, this allows Instant results to be above the info bar.
920 [contentView cr_ensureSubview:[infoBarContainerController_ view]
921 isPositioned:NSWindowAbove
922 relativeTo:toolbarView];
925 // The find bar is above everything.
926 if (findBarCocoaController_) {
927 NSView* relativeView = nil;
928 if (inPresentationMode)
929 relativeView = toolbarView;
931 relativeView = [self tabContentArea];
932 [contentView cr_ensureSubview:[findBarCocoaController_ view]
933 isPositioned:NSWindowAbove
934 relativeTo:relativeView];
937 if (floatingBarBackingView_) {
938 if ([floatingBarBackingView_ cr_isBelowView:[self tabContentArea]])
939 [floatingBarBackingView_ removeFromSuperview];
940 if ([self placeBookmarkBarBelowInfoBar]) {
941 [contentView cr_ensureSubview:floatingBarBackingView_
942 isPositioned:NSWindowAbove
943 relativeTo:[bookmarkBarController_ view]];
945 [contentView cr_ensureSubview:floatingBarBackingView_
946 isPositioned:NSWindowBelow
947 relativeTo:[bookmarkBarController_ view]];
952 - (BOOL)shouldAllowOverlappingViews:(BOOL)inPresentationMode {
953 if (inPresentationMode)
956 if (findBarCocoaController_ &&
957 ![[findBarCocoaController_ findBarView] isHidden]) {
961 if (overlappedViewCount_)
967 - (void)updateAllowOverlappingViews:(BOOL)inPresentationMode {
968 WebContents* contents = browser_->tab_strip_model()->GetActiveWebContents();
972 BOOL allowOverlappingViews =
973 [self shouldAllowOverlappingViews:inPresentationMode];
975 // The rendering path with overlapping views disabled causes bugs when
976 // transitioning between composited and non-composited mode.
977 // http://crbug.com/279472
978 allowOverlappingViews = YES;
980 if (allowOverlappingViews &&
981 [self coreAnimationStatus] ==
982 browser_window_controller::kCoreAnimationEnabledLazy) {
983 [[[self window] contentView] setWantsLayer:YES];
984 [[self tabStripView] setWantsLayer:YES];
987 contents->GetView()->SetAllowOverlappingViews(allowOverlappingViews);
989 DevToolsWindow* devToolsWindow =
990 DevToolsWindow::GetDockedInstanceForInspectedTab(contents);
991 if (devToolsWindow) {
992 devToolsWindow->web_contents()->GetView()->
993 SetAllowOverlappingViews(allowOverlappingViews);
997 - (void)updateInfoBarTipVisibility {
998 // If there's no toolbar then hide the infobar tip.
999 [infoBarContainerController_
1000 setShouldSuppressTopInfoBarTip:![self hasToolbar]];
1003 - (browser_window_controller::CoreAnimationStatus)coreAnimationStatus {
1004 // TODO(sail) Remove this.
1005 if (!CommandLine::ForCurrentProcess()->HasSwitch(
1006 switches::kUseCoreAnimation)) {
1007 return browser_window_controller::kCoreAnimationDisabled;
1009 if (CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
1010 switches::kUseCoreAnimation) == "lazy") {
1011 return browser_window_controller::kCoreAnimationEnabledLazy;
1013 if (CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
1014 switches::kUseCoreAnimation) == "disabled") {
1015 return browser_window_controller::kCoreAnimationDisabled;
1017 return browser_window_controller::kCoreAnimationEnabledAlways;
1020 @end // @implementation BrowserWindowController(Private)