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/browser/avatar_icon_controller.h"
22 #import "chrome/browser/ui/cocoa/dev_tools_controller.h"
23 #import "chrome/browser/ui/cocoa/fast_resize_view.h"
24 #import "chrome/browser/ui/cocoa/find_bar/find_bar_cocoa_controller.h"
25 #import "chrome/browser/ui/cocoa/floating_bar_backing_view.h"
26 #import "chrome/browser/ui/cocoa/framed_browser_window.h"
27 #import "chrome/browser/ui/cocoa/fullscreen_mode_controller.h"
28 #import "chrome/browser/ui/cocoa/fullscreen_window.h"
29 #import "chrome/browser/ui/cocoa/infobars/infobar_container_controller.h"
30 #include "chrome/browser/ui/cocoa/last_active_browser_cocoa.h"
31 #import "chrome/browser/ui/cocoa/nsview_additions.h"
32 #import "chrome/browser/ui/cocoa/presentation_mode_controller.h"
33 #import "chrome/browser/ui/cocoa/status_bubble_mac.h"
34 #import "chrome/browser/ui/cocoa/tab_contents/overlayable_contents_controller.h"
35 #import "chrome/browser/ui/cocoa/tabs/tab_strip_controller.h"
36 #import "chrome/browser/ui/cocoa/tabs/tab_strip_view.h"
37 #import "chrome/browser/ui/cocoa/toolbar/toolbar_controller.h"
38 #include "chrome/browser/ui/fullscreen/fullscreen_controller.h"
39 #include "chrome/browser/ui/tabs/tab_strip_model.h"
40 #include "chrome/common/chrome_switches.h"
41 #include "chrome/common/pref_names.h"
42 #include "content/public/browser/render_widget_host_view.h"
43 #include "content/public/browser/web_contents.h"
44 #include "content/public/browser/web_contents_view.h"
45 #import "ui/base/cocoa/focus_tracker.h"
46 #include "ui/base/ui_base_types.h"
48 using content::RenderWidgetHostView;
49 using content::WebContents;
53 // Space between the incognito badge and the right edge of the window.
54 const CGFloat kAvatarRightOffset = 4;
56 // The amount by which to shrink the tab strip (on the right) when the
57 // incognito badge is present.
58 const CGFloat kAvatarTabStripShrink = 18;
60 // Width of the full screen icon. Used to position the AvatarButton to the
62 const CGFloat kFullscreenIconWidth = 32;
64 // Insets for the location bar, used when the full toolbar is hidden.
65 // TODO(viettrungluu): We can argue about the "correct" insetting; I like the
66 // following best, though arguably 0 inset is better/more correct.
67 const CGFloat kLocBarLeftRightInset = 1;
68 const CGFloat kLocBarTopInset = 0;
69 const CGFloat kLocBarBottomInset = 1;
73 @implementation BrowserWindowController(Private)
75 // Create the tab strip controller.
76 - (void)createTabStripController {
77 DCHECK([overlayableContentsController_ activeContainer]);
78 DCHECK([[overlayableContentsController_ activeContainer] window]);
79 tabStripController_.reset([[TabStripController alloc]
80 initWithView:[self tabStripView]
81 switchView:[overlayableContentsController_ activeContainer]
82 browser:browser_.get()
86 - (void)saveWindowPositionIfNeeded {
87 if (!chrome::ShouldSaveWindowPlacement(browser_.get()))
90 // If we're in fullscreen mode, save the position of the regular window
92 NSWindow* window = [self isFullscreen] ? savedRegularWindow_ : [self window];
94 // Window positions are stored relative to the origin of the primary monitor.
95 NSRect monitorFrame = [[[NSScreen screens] objectAtIndex:0] frame];
96 NSScreen* windowScreen = [window screen];
98 // Start with the window's frame, which is in virtual coordinates.
99 // Do some y twiddling to flip the coordinate system.
100 gfx::Rect bounds(NSRectToCGRect([window frame]));
101 bounds.set_y(monitorFrame.size.height - bounds.y() - bounds.height());
103 // Browser::SaveWindowPlacement saves information for session restore.
104 ui::WindowShowState show_state = ui::SHOW_STATE_NORMAL;
105 if ([window isMiniaturized])
106 show_state = ui::SHOW_STATE_MINIMIZED;
107 else if ([self isFullscreen])
108 show_state = ui::SHOW_STATE_FULLSCREEN;
109 chrome::SaveWindowPlacement(browser_.get(), bounds, show_state);
111 // |windowScreen| can be nil (for example, if the monitor arrangement was
112 // changed while in fullscreen mode). If we see a nil screen, return without
114 // TODO(rohitrao): We should just not save anything for fullscreen windows.
115 // http://crbug.com/36479.
119 // Only save main window information to preferences.
120 PrefService* prefs = browser_->profile()->GetPrefs();
121 if (!prefs || browser_ != chrome::GetLastActiveBrowser())
124 // Save the current work area, in flipped coordinates.
125 gfx::Rect workArea(NSRectToCGRect([windowScreen visibleFrame]));
126 workArea.set_y(monitorFrame.size.height - workArea.y() - workArea.height());
128 DictionaryPrefUpdate update(
130 chrome::GetWindowPlacementKey(browser_.get()).c_str());
131 base::DictionaryValue* windowPreferences = update.Get();
132 windowPreferences->SetInteger("left", bounds.x());
133 windowPreferences->SetInteger("top", bounds.y());
134 windowPreferences->SetInteger("right", bounds.right());
135 windowPreferences->SetInteger("bottom", bounds.bottom());
136 windowPreferences->SetBoolean("maximized", false);
137 windowPreferences->SetBoolean("always_on_top", false);
138 windowPreferences->SetInteger("work_area_left", workArea.x());
139 windowPreferences->SetInteger("work_area_top", workArea.y());
140 windowPreferences->SetInteger("work_area_right", workArea.right());
141 windowPreferences->SetInteger("work_area_bottom", workArea.bottom());
144 - (NSRect)window:(NSWindow*)window
145 willPositionSheet:(NSWindow*)sheet
146 usingRect:(NSRect)defaultSheetRect {
147 // Position the sheet as follows:
148 // - If the bookmark bar is hidden or shown as a bubble (on the NTP when the
149 // bookmark bar is disabled), position the sheet immediately below the
151 // - If the bookmark bar is shown (attached to the normal toolbar), position
152 // the sheet below the bookmark bar.
153 // - If the bookmark bar is currently animating, position the sheet according
154 // to where the bar will be when the animation ends.
155 switch ([bookmarkBarController_ currentState]) {
156 case BookmarkBar::SHOW: {
157 NSRect bookmarkBarFrame = [[bookmarkBarController_ view] frame];
158 defaultSheetRect.origin.y = bookmarkBarFrame.origin.y;
161 case BookmarkBar::HIDDEN:
162 case BookmarkBar::DETACHED: {
163 if ([self hasToolbar]) {
164 NSRect toolbarFrame = [[toolbarController_ view] frame];
165 defaultSheetRect.origin.y = toolbarFrame.origin.y;
167 // The toolbar is not shown in application mode. The sheet should be
168 // located at the top of the window, under the title of the window.
169 defaultSheetRect.origin.y = NSHeight([[window contentView] frame]) -
170 defaultSheetRect.size.height;
175 return defaultSheetRect;
178 - (void)layoutSubviews {
179 // With the exception of the top tab strip, the subviews which we lay out are
180 // subviews of the content view, so we mainly work in the content view's
181 // coordinate system. Note, however, that the content view's coordinate system
182 // and the window's base coordinate system should coincide.
183 NSWindow* window = [self window];
184 NSView* contentView = [window contentView];
185 NSRect contentBounds = [contentView bounds];
186 CGFloat minX = NSMinX(contentBounds);
187 CGFloat minY = NSMinY(contentBounds);
188 CGFloat width = NSWidth(contentBounds);
190 BOOL useSimplifiedFullscreen = CommandLine::ForCurrentProcess()->HasSwitch(
191 switches::kEnableSimplifiedFullscreen);
193 // Suppress title drawing if necessary.
194 if ([window respondsToSelector:@selector(setShouldHideTitle:)])
195 [(id)window setShouldHideTitle:![self hasTitleBar]];
197 // Update z-order. The code below depends on this.
198 [self updateSubviewZOrder:[self inPresentationMode]];
200 BOOL inPresentationMode = [self inPresentationMode];
201 CGFloat floatingBarHeight = [self floatingBarHeight];
202 // In presentation mode, |yOffset| accounts for the sliding position of the
203 // floating bar and the extra offset needed to dodge the menu bar.
204 CGFloat yOffset = inPresentationMode && !useSimplifiedFullscreen ?
205 (std::floor((1 - floatingBarShownFraction_) * floatingBarHeight) -
206 [presentationModeController_ floatingBarVerticalOffset]) : 0;
207 CGFloat maxY = NSMaxY(contentBounds) + yOffset;
209 if ([self hasTabStrip]) {
210 // If we need to lay out the top tab strip, replace |maxY| with a higher
211 // value, and then lay out the tab strip.
212 NSRect windowFrame = [contentView convertRect:[window frame] fromView:nil];
213 maxY = NSHeight(windowFrame) + yOffset;
214 if (useSimplifiedFullscreen && [self isFullscreen]) {
215 CGFloat tabStripHeight = NSHeight([[self tabStripView] frame]);
216 CGFloat revealAmount = (1 - floatingBarShownFraction_) * tabStripHeight;
217 // In simplified fullscreen, only the toolbar is visible by default, and
218 // the tabstrip and menu bar come down (each separately) when the user
219 // mouses near the top of the window. Push the maxY of the toolbar up by
220 // the amount of the tabstrip that is revealed, while removing the amount
221 // of space needed by the menu bar.
223 revealAmount - [fullscreenModeController_ menuBarHeight]);
225 maxY = [self layoutTabStripAtMaxY:maxY
227 fullscreen:[self isFullscreen]];
230 // Sanity-check |maxY|.
231 DCHECK_GE(maxY, minY);
232 DCHECK_LE(maxY, NSMaxY(contentBounds) + yOffset);
234 // Place the toolbar at the top of the reserved area.
235 maxY = [self layoutToolbarAtMinX:minX maxY:maxY width:width];
237 // If we're not displaying the bookmark bar below the info bar, then it goes
238 // immediately below the toolbar.
239 BOOL placeBookmarkBarBelowInfoBar = [self placeBookmarkBarBelowInfoBar];
240 if (!placeBookmarkBarBelowInfoBar)
241 maxY = [self layoutBookmarkBarAtMinX:minX maxY:maxY width:width];
243 // The floating bar backing view doesn't actually add any height.
244 NSRect floatingBarBackingRect =
245 NSMakeRect(minX, maxY, width, floatingBarHeight);
246 [self layoutFloatingBarBackingView:floatingBarBackingRect
247 presentationMode:inPresentationMode];
249 // Place the find bar immediately below the toolbar/attached bookmark bar. In
250 // presentation mode, it hangs off the top of the screen when the bar is
252 [findBarCocoaController_ positionFindBarViewAtMaxY:maxY maxWidth:width];
253 [fullscreenExitBubbleController_ positionInWindowAtTop:maxY width:width];
255 // If in presentation mode, reset |maxY| to top of screen, so that the
256 // floating bar slides over the things which appear to be in the content area.
257 if (inPresentationMode ||
258 (useSimplifiedFullscreen && !fullscreenUrl_.is_empty())) {
259 maxY = NSMaxY(contentBounds);
262 // Also place the info bar container immediate below the toolbar, except in
263 // presentation mode in which case it's at the top of the visual content area.
264 maxY = [self layoutInfoBarAtMinX:minX maxY:maxY width:width];
266 // If the bookmark bar is detached, place it next in the visual content area.
267 if (placeBookmarkBarBelowInfoBar)
268 maxY = [self layoutBookmarkBarAtMinX:minX maxY:maxY width:width];
270 // Place the download shelf, if any, at the bottom of the view.
271 minY = [self layoutDownloadShelfAtMinX:minX minY:minY width:width];
273 // Finally, the content area takes up all of the remaining space.
274 NSRect contentAreaRect = NSMakeRect(minX, minY, width, maxY - minY);
275 [self layoutTabContentArea:contentAreaRect];
277 // Normally, we don't need to tell the toolbar whether or not to show the
278 // divider, but things break down during animation.
279 [toolbarController_ setDividerOpacity:[self toolbarDividerOpacity]];
282 - (CGFloat)floatingBarHeight {
283 if (![self inPresentationMode])
286 CGFloat totalHeight = [presentationModeController_ floatingBarVerticalOffset];
288 if ([self hasTabStrip])
289 totalHeight += NSHeight([[self tabStripView] frame]);
291 if ([self hasToolbar]) {
292 totalHeight += NSHeight([[toolbarController_ view] frame]);
293 } else if ([self hasLocationBar]) {
294 totalHeight += NSHeight([[toolbarController_ view] frame]) +
295 kLocBarTopInset + kLocBarBottomInset;
298 if (![self placeBookmarkBarBelowInfoBar])
299 totalHeight += NSHeight([[bookmarkBarController_ view] frame]);
304 - (CGFloat)layoutTabStripAtMaxY:(CGFloat)maxY
306 fullscreen:(BOOL)fullscreen {
307 // Nothing to do if no tab strip.
308 if (![self hasTabStrip])
311 NSView* tabStripView = [self tabStripView];
312 CGFloat tabStripHeight = NSHeight([tabStripView frame]);
313 maxY -= tabStripHeight;
314 [tabStripView setFrame:NSMakeRect(0, maxY, width, tabStripHeight)];
316 // Set left indentation based on fullscreen mode status.
317 [tabStripController_ setLeftIndentForControls:(fullscreen ? 0 :
318 [[tabStripController_ class] defaultLeftIndentForControls])];
320 // Lay out the icognito/avatar badge because calculating the indentation on
321 // the right depends on it.
322 NSView* avatarButton = [avatarButtonController_ view];
323 if ([self shouldShowAvatar]) {
324 CGFloat badgeXOffset = -kAvatarRightOffset;
325 CGFloat badgeYOffset = 0;
326 CGFloat buttonHeight = NSHeight([avatarButton frame]);
328 if ([self shouldUseNewAvatarButton]) {
329 // The fullscreen icon is displayed to the right of the avatar button.
330 if (![self isFullscreen])
331 badgeXOffset -= kFullscreenIconWidth;
332 // Center the button vertically on the tabstrip.
333 badgeYOffset = (tabStripHeight - buttonHeight) / 2;
335 // Actually place the badge *above* |maxY|, by +2 to miss the divider.
336 badgeYOffset = 2 * [[avatarButton superview] cr_lineWidth];
339 [avatarButton setFrameSize:NSMakeSize(NSWidth([avatarButton frame]),
340 std::min(buttonHeight, tabStripHeight))];
342 NSMakePoint(width - NSWidth([avatarButton frame]) + badgeXOffset,
343 maxY + badgeYOffset);
344 [avatarButton setFrameOrigin:origin];
345 [avatarButton setHidden:NO]; // Make sure it's shown.
348 // Calculate the right indentation. The default indentation built into the
349 // tabstrip leaves enough room for the fullscreen button or presentation mode
350 // toggle button on Lion. On non-Lion systems, the right indent needs to be
351 // adjusted to make room for the new tab button when an avatar is present.
352 CGFloat rightIndent = 0;
353 if (base::mac::IsOSLionOrLater() &&
354 [[self window] isKindOfClass:[FramedBrowserWindow class]]) {
355 FramedBrowserWindow* window =
356 static_cast<FramedBrowserWindow*>([self window]);
357 rightIndent += -[window fullScreenButtonOriginAdjustment].x;
359 // The new avatar is wider than the default indentation, so we need to
360 // account for its width.
361 if ([self shouldUseNewAvatarButton])
362 rightIndent += NSWidth([avatarButton frame]) + kAvatarTabStripShrink;
363 } else if ([self shouldShowAvatar]) {
364 rightIndent += kAvatarTabStripShrink +
365 NSWidth([avatarButton frame]) + kAvatarRightOffset;
367 [tabStripController_ setRightIndentForControls:rightIndent];
369 // Go ahead and layout the tabs.
370 [tabStripController_ layoutTabsWithoutAnimation];
375 - (CGFloat)layoutToolbarAtMinX:(CGFloat)minX
377 width:(CGFloat)width {
378 NSView* toolbarView = [toolbarController_ view];
379 NSRect toolbarFrame = [toolbarView frame];
380 if ([self hasToolbar]) {
381 // The toolbar is present in the window, so we make room for it.
382 DCHECK(![toolbarView isHidden]);
383 toolbarFrame.origin.x = minX;
384 toolbarFrame.origin.y = maxY - NSHeight(toolbarFrame);
385 toolbarFrame.size.width = width;
386 maxY -= NSHeight(toolbarFrame);
388 if ([self hasLocationBar]) {
389 // Location bar is present with no toolbar. Put a border of
390 // |kLocBar...Inset| pixels around the location bar.
391 // TODO(viettrungluu): This is moderately ridiculous. The toolbar should
392 // really be aware of what its height should be (the way the toolbar
393 // compression stuff is currently set up messes things up).
394 DCHECK(![toolbarView isHidden]);
395 toolbarFrame.origin.x = kLocBarLeftRightInset;
396 toolbarFrame.origin.y = maxY - NSHeight(toolbarFrame) - kLocBarTopInset;
397 toolbarFrame.size.width = width - 2 * kLocBarLeftRightInset;
398 maxY -= kLocBarTopInset + NSHeight(toolbarFrame) + kLocBarBottomInset;
400 DCHECK([toolbarView isHidden]);
403 [toolbarView setFrame:toolbarFrame];
407 - (BOOL)placeBookmarkBarBelowInfoBar {
408 // If we are currently displaying the NTP detached bookmark bar or animating
409 // to/from it (from/to anything else), we display the bookmark bar below the
411 return [bookmarkBarController_ isInState:BookmarkBar::DETACHED] ||
412 [bookmarkBarController_ isAnimatingToState:BookmarkBar::DETACHED] ||
413 [bookmarkBarController_ isAnimatingFromState:BookmarkBar::DETACHED];
416 - (CGFloat)layoutBookmarkBarAtMinX:(CGFloat)minX
418 width:(CGFloat)width {
419 [bookmarkBarController_ updateHiddenState];
421 NSView* bookmarkBarView = [bookmarkBarController_ view];
422 NSRect frame = [bookmarkBarView frame];
423 frame.origin.x = minX;
424 frame.origin.y = maxY - NSHeight(frame);
425 frame.size.width = width;
426 [bookmarkBarView setFrame:frame];
427 maxY -= NSHeight(frame);
429 // Pin the bookmark bar to the top of the window and make the width flexible.
430 [bookmarkBarView setAutoresizingMask:NSViewWidthSizable | NSViewMinYMargin];
432 // TODO(viettrungluu): Does this really belong here? Calling it shouldn't be
433 // necessary in the non-NTP case.
434 [bookmarkBarController_ layoutSubviews];
439 - (void)layoutFloatingBarBackingView:(NSRect)frame
440 presentationMode:(BOOL)presentationMode {
441 // Only display when in presentation mode.
442 if (presentationMode) {
443 // For certain window types such as app windows (e.g., the dev tools
444 // window), there's no actual overlay. (Displaying one would result in an
445 // overly sliding in only under the menu, which gives an ugly effect.)
446 if (floatingBarBackingView_.get()) {
448 [floatingBarBackingView_ setFrame:frame];
451 // But we want the logic to work as usual (for show/hide/etc. purposes).
452 [presentationModeController_ overlayFrameChanged:frame];
454 // Okay to call even if |floatingBarBackingView_| is nil.
455 if ([floatingBarBackingView_ superview])
456 [floatingBarBackingView_ removeFromSuperview];
460 - (CGFloat)layoutInfoBarAtMinX:(CGFloat)minX
462 width:(CGFloat)width {
463 NSView* containerView = [infoBarContainerController_ view];
464 NSRect containerFrame = [containerView frame];
465 maxY -= NSHeight(containerFrame);
466 maxY += [infoBarContainerController_ overlappingTipHeight];
467 containerFrame.origin.x = minX;
468 containerFrame.origin.y = maxY;
469 containerFrame.size.width = width;
470 [containerView setFrame:containerFrame];
474 - (CGFloat)layoutDownloadShelfAtMinX:(CGFloat)minX
476 width:(CGFloat)width {
477 if (downloadShelfController_.get()) {
478 NSView* downloadView = [downloadShelfController_ view];
479 NSRect downloadFrame = [downloadView frame];
480 downloadFrame.origin.x = minX;
481 downloadFrame.origin.y = minY;
482 downloadFrame.size.width = width;
483 [downloadView setFrame:downloadFrame];
484 minY += NSHeight(downloadFrame);
489 - (void)layoutTabContentArea:(NSRect)newFrame {
490 NSView* tabContentView = [self tabContentArea];
491 NSRect tabContentFrame = [tabContentView frame];
493 bool contentShifted =
494 NSMaxY(tabContentFrame) != NSMaxY(newFrame) ||
495 NSMinX(tabContentFrame) != NSMinX(newFrame);
497 tabContentFrame = newFrame;
498 [tabContentView setFrame:tabContentFrame];
500 // If the relayout shifts the content area up or down, let the renderer know.
501 if (contentShifted) {
502 if (WebContents* contents =
503 browser_->tab_strip_model()->GetActiveWebContents()) {
504 if (RenderWidgetHostView* rwhv = contents->GetRenderWidgetHostView())
505 rwhv->WindowFrameChanged();
510 - (void)adjustToolbarAndBookmarkBarForCompression:(CGFloat)compression {
512 [toolbarController_ desiredHeightForCompression:compression];
513 NSRect toolbarFrame = [[toolbarController_ view] frame];
514 CGFloat deltaH = newHeight - toolbarFrame.size.height;
519 toolbarFrame.size.height = newHeight;
520 NSRect bookmarkFrame = [[bookmarkBarController_ view] frame];
521 bookmarkFrame.size.height = bookmarkFrame.size.height - deltaH;
522 [[toolbarController_ view] setFrame:toolbarFrame];
523 [[bookmarkBarController_ view] setFrame:bookmarkFrame];
524 [self layoutSubviews];
527 // Fullscreen and presentation mode methods
529 - (BOOL)shouldShowPresentationModeToggle {
530 return chrome::mac::SupportsSystemFullscreen() && [self isFullscreen];
533 - (void)moveViewsForFullscreenForSnowLeopard:(BOOL)fullscreen
534 regularWindow:(NSWindow*)regularWindow
535 fullscreenWindow:(NSWindow*)fullscreenWindow {
536 // This method is only for systems without fullscreen support.
537 DCHECK(!chrome::mac::SupportsSystemFullscreen());
539 NSWindow* sourceWindow = fullscreen ? regularWindow : fullscreenWindow;
540 NSWindow* destWindow = fullscreen ? fullscreenWindow : regularWindow;
542 // Close the bookmark bubble, if it's open. Use |-ok:| instead of |-cancel:|
543 // or |-close| because that matches the behavior when the bubble loses key
545 [bookmarkBubbleController_ ok:self];
547 // Save the current first responder so we can restore after views are moved.
548 base::scoped_nsobject<FocusTracker> focusTracker(
549 [[FocusTracker alloc] initWithWindow:sourceWindow]);
551 // While we move views (and focus) around, disable any bar visibility changes.
552 [self disableBarVisibilityUpdates];
554 // Retain the tab strip view while we remove it from its superview.
555 base::scoped_nsobject<NSView> tabStripView;
556 if ([self hasTabStrip]) {
557 tabStripView.reset([[self tabStripView] retain]);
558 [tabStripView removeFromSuperview];
561 // Ditto for the content view.
562 base::scoped_nsobject<NSView> contentView(
563 [[sourceWindow contentView] retain]);
564 // Disable autoresizing of subviews while we move views around. This prevents
565 // spurious renderer resizes.
566 [contentView setAutoresizesSubviews:NO];
567 [contentView removeFromSuperview];
569 // Have to do this here, otherwise later calls can crash because the window
571 [sourceWindow setDelegate:nil];
572 [destWindow setDelegate:self];
574 // With this call, valgrind complains that a "Conditional jump or move depends
575 // on uninitialised value(s)". The error happens in -[NSThemeFrame
576 // drawOverlayRect:]. I'm pretty convinced this is an Apple bug, but there is
577 // no visual impact. I have been unable to tickle it away with other window
578 // or view manipulation Cocoa calls. Stack added to suppressions_mac.txt.
579 [contentView setAutoresizesSubviews:YES];
580 [destWindow setContentView:contentView];
582 // Move the incognito badge if present.
583 if ([self shouldShowAvatar]) {
584 NSView* avatarButtonView = [avatarButtonController_ view];
586 [avatarButtonView removeFromSuperview];
587 [avatarButtonView setHidden:YES]; // Will be shown in layout.
588 [[[destWindow contentView] superview] addSubview: avatarButtonView];
591 // Add the tab strip after setting the content view and moving the incognito
592 // badge (if any), so that the tab strip will be on top (in the z-order).
593 if ([self hasTabStrip])
594 [[[destWindow contentView] superview] addSubview:tabStripView];
596 [sourceWindow setWindowController:nil];
597 [self setWindow:destWindow];
598 [destWindow setWindowController:self];
600 // Move the status bubble over, if we have one.
602 statusBubble_->SwitchParentWindow(destWindow);
604 // Move the title over.
605 [destWindow setTitle:[sourceWindow title]];
607 // The window needs to be onscreen before we can set its first responder.
608 // Ordering the window to the front can change the active Space (either to
609 // the window's old Space or to the application's assigned Space). To prevent
610 // this by temporarily change the collectionBehavior.
611 NSWindowCollectionBehavior behavior = [sourceWindow collectionBehavior];
612 [destWindow setCollectionBehavior:
613 NSWindowCollectionBehaviorMoveToActiveSpace];
614 [destWindow makeKeyAndOrderFront:self];
615 [destWindow setCollectionBehavior:behavior];
617 [focusTracker restoreFocusInWindow:destWindow];
618 [sourceWindow orderOut:self];
620 // We're done moving focus, so re-enable bar visibility changes.
621 [self enableBarVisibilityUpdates];
624 - (void)setPresentationModeInternal:(BOOL)presentationMode
625 forceDropdown:(BOOL)forceDropdown {
626 if (presentationMode == [self inPresentationMode])
629 if (presentationMode) {
630 BOOL fullscreen_for_tab =
631 browser_->fullscreen_controller()->IsFullscreenForTabOrPending();
633 CommandLine::ForCurrentProcess()->HasSwitch(switches::kKioskMode);
634 BOOL showDropdown = !fullscreen_for_tab &&
636 (forceDropdown || [self floatingBarHasFocus]);
637 NSView* contentView = [[self window] contentView];
638 presentationModeController_.reset(
639 [[PresentationModeController alloc] initWithBrowserController:self]);
640 [presentationModeController_ enterPresentationModeForContentView:contentView
641 showDropdown:showDropdown];
643 [presentationModeController_ exitPresentationMode];
644 presentationModeController_.reset();
647 [self adjustUIForPresentationMode:presentationMode];
648 [self layoutSubviews];
651 // TODO(rohitrao): This method is misnamed now, since there is a flag that
652 // enables 10.6-style fullscreen on newer OSes.
653 - (void)enterFullscreenForSnowLeopard {
655 const CGDisplayReservationInterval kFadeDurationSeconds = 0.6;
656 Boolean didFadeOut = NO;
657 CGDisplayFadeReservationToken token;
658 if (CGAcquireDisplayFadeReservation(kFadeDurationSeconds, &token)
659 == kCGErrorSuccess) {
661 CGDisplayFade(token, kFadeDurationSeconds / 2, kCGDisplayBlendNormal,
662 kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, /*synchronous=*/true);
665 // Create the fullscreen window. After this line, isFullscreen will return
667 fullscreenWindow_.reset([[self createFullscreenWindow] retain]);
668 savedRegularWindow_ = [[self window] retain];
669 savedRegularWindowFrame_ = [savedRegularWindow_ frame];
671 [self moveViewsForFullscreenForSnowLeopard:YES
672 regularWindow:[self window]
673 fullscreenWindow:fullscreenWindow_.get()];
675 // When simplified fullscreen is enabled, do not enter presentation mode.
676 const CommandLine* command_line = CommandLine::ForCurrentProcess();
677 if (command_line->HasSwitch(switches::kEnableSimplifiedFullscreen)) {
678 // TODO(rohitrao): Add code to manage the menubar here.
680 [self adjustUIForPresentationMode:YES];
681 [self setPresentationModeInternal:YES forceDropdown:NO];
684 [self layoutSubviews];
686 [self windowDidEnterFullScreen:nil];
690 CGDisplayFade(token, kFadeDurationSeconds / 2, kCGDisplayBlendSolidColor,
691 kCGDisplayBlendNormal, 0.0, 0.0, 0.0, /*synchronous=*/false);
692 CGReleaseDisplayFadeReservation(token);
696 - (void)exitFullscreenForSnowLeopard {
697 // TODO(rohitrao): This method is misnamed now, since there is a flag that
698 // enables 10.6-style fullscreen on newer OSes.
699 DCHECK(!chrome::mac::SupportsSystemFullscreen());
702 const CGDisplayReservationInterval kFadeDurationSeconds = 0.6;
703 Boolean didFadeOut = NO;
704 CGDisplayFadeReservationToken token;
705 if (CGAcquireDisplayFadeReservation(kFadeDurationSeconds, &token)
706 == kCGErrorSuccess) {
708 CGDisplayFade(token, kFadeDurationSeconds / 2, kCGDisplayBlendNormal,
709 kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, /*synchronous=*/true);
712 // When simplified fullscreen is enabled, the menubar status is managed
714 const CommandLine* command_line = CommandLine::ForCurrentProcess();
715 if (command_line->HasSwitch(switches::kEnableSimplifiedFullscreen)) {
716 // TODO(rohitrao): Add code to manage the menubar here.
719 [self windowWillExitFullScreen:nil];
721 [self moveViewsForFullscreenForSnowLeopard:NO
722 regularWindow:savedRegularWindow_
723 fullscreenWindow:fullscreenWindow_.get()];
725 // When exiting fullscreen mode, we need to call layoutSubviews manually.
726 [savedRegularWindow_ autorelease];
727 savedRegularWindow_ = nil;
728 fullscreenWindow_.reset();
729 [self layoutSubviews];
731 [self windowDidExitFullScreen:nil];
735 CGDisplayFade(token, kFadeDurationSeconds / 2, kCGDisplayBlendSolidColor,
736 kCGDisplayBlendNormal, 0.0, 0.0, 0.0, /*synchronous=*/false);
737 CGReleaseDisplayFadeReservation(token);
741 // TODO(rohitrao): This function has shrunk into uselessness, and
742 // |-setFullscreen:| has grown rather large. Find a good way to break up
743 // |-setFullscreen:| into smaller pieces. http://crbug.com/36449
744 - (void)adjustUIForPresentationMode:(BOOL)fullscreen {
745 // Create the floating bar backing view if necessary.
746 if (fullscreen && !floatingBarBackingView_.get() &&
747 ([self hasTabStrip] || [self hasToolbar] || [self hasLocationBar])) {
748 floatingBarBackingView_.reset(
749 [[FloatingBarBackingView alloc] initWithFrame:NSZeroRect]);
750 [floatingBarBackingView_ setAutoresizingMask:(NSViewWidthSizable |
754 // Force the bookmark bar z-order to update.
755 [[bookmarkBarController_ view] removeFromSuperview];
756 [self updateSubviewZOrder:fullscreen];
757 [self updateAllowOverlappingViews:fullscreen];
760 - (void)showFullscreenExitBubbleIfNecessary {
761 // This method is called in response to
762 // |-updateFullscreenExitBubbleURL:bubbleType:|. If on Lion the system is
763 // transitioning, do not show the bubble because it will cause visual jank
764 // <http://crbug.com/130649>. This will be called again as part of
765 // |-windowDidEnterFullScreen:|, so arrange to do that work then instead.
766 if (enteringFullscreen_)
769 [presentationModeController_ ensureOverlayHiddenWithAnimation:NO delay:NO];
771 if (fullscreenBubbleType_ == FEB_TYPE_NONE ||
772 fullscreenBubbleType_ == FEB_TYPE_BROWSER_FULLSCREEN_EXIT_INSTRUCTION) {
773 // Show no exit instruction bubble on Mac when in Browser Fullscreen.
774 [self destroyFullscreenExitBubbleIfNecessary];
776 [fullscreenExitBubbleController_ closeImmediately];
777 fullscreenExitBubbleController_.reset(
778 [[FullscreenExitBubbleController alloc]
780 browser:browser_.get()
782 bubbleType:fullscreenBubbleType_]);
783 [fullscreenExitBubbleController_ showWindow];
787 - (void)destroyFullscreenExitBubbleIfNecessary {
788 [fullscreenExitBubbleController_ closeImmediately];
789 fullscreenExitBubbleController_.reset();
792 - (void)contentViewDidResize:(NSNotification*)notification {
793 [self layoutSubviews];
796 - (void)registerForContentViewResizeNotifications {
797 [[NSNotificationCenter defaultCenter]
799 selector:@selector(contentViewDidResize:)
800 name:NSViewFrameDidChangeNotification
801 object:[[self window] contentView]];
804 - (void)deregisterForContentViewResizeNotifications {
805 [[NSNotificationCenter defaultCenter]
807 name:NSViewFrameDidChangeNotification
808 object:[[self window] contentView]];
811 - (NSSize)window:(NSWindow*)window
812 willUseFullScreenContentSize:(NSSize)proposedSize {
816 - (NSApplicationPresentationOptions)window:(NSWindow*)window
817 willUseFullScreenPresentationOptions:(NSApplicationPresentationOptions)opt {
819 NSApplicationPresentationAutoHideDock |
820 NSApplicationPresentationAutoHideMenuBar);
823 - (void)windowWillEnterFullScreen:(NSNotification*)notification {
824 [self registerForContentViewResizeNotifications];
826 NSWindow* window = [self window];
827 savedRegularWindowFrame_ = [window frame];
828 BOOL mode = enteringPresentationMode_ ||
829 browser_->fullscreen_controller()->IsFullscreenForTabOrPending();
830 enteringFullscreen_ = YES;
831 [self setPresentationModeInternal:mode forceDropdown:NO];
834 - (void)windowDidEnterFullScreen:(NSNotification*)notification {
835 if (chrome::mac::SupportsSystemFullscreen())
836 [self deregisterForContentViewResizeNotifications];
837 enteringFullscreen_ = NO;
838 enteringPresentationMode_ = NO;
840 const CommandLine* command_line = CommandLine::ForCurrentProcess();
841 if (command_line->HasSwitch(switches::kEnableSimplifiedFullscreen) &&
842 fullscreenUrl_.is_empty()) {
843 fullscreenModeController_.reset([[FullscreenModeController alloc]
844 initWithBrowserWindowController:self]);
847 [self showFullscreenExitBubbleIfNecessary];
848 browser_->WindowFullscreenStateChanged();
851 - (void)windowWillExitFullScreen:(NSNotification*)notification {
852 if (chrome::mac::SupportsSystemFullscreen())
853 [self registerForContentViewResizeNotifications];
854 fullscreenModeController_.reset();
855 [self destroyFullscreenExitBubbleIfNecessary];
856 [self setPresentationModeInternal:NO forceDropdown:NO];
859 - (void)windowDidExitFullScreen:(NSNotification*)notification {
860 if (chrome::mac::SupportsSystemFullscreen())
861 [self deregisterForContentViewResizeNotifications];
862 browser_->WindowFullscreenStateChanged();
865 - (void)windowDidFailToEnterFullScreen:(NSWindow*)window {
866 [self deregisterForContentViewResizeNotifications];
867 enteringFullscreen_ = NO;
868 [self setPresentationModeInternal:NO forceDropdown:NO];
870 // Force a relayout to try and get the window back into a reasonable state.
871 [self layoutSubviews];
874 - (void)windowDidFailToExitFullScreen:(NSWindow*)window {
875 [self deregisterForContentViewResizeNotifications];
877 // Force a relayout to try and get the window back into a reasonable state.
878 [self layoutSubviews];
881 - (void)enableBarVisibilityUpdates {
882 // Early escape if there's nothing to do.
883 if (barVisibilityUpdatesEnabled_)
886 barVisibilityUpdatesEnabled_ = YES;
888 if ([barVisibilityLocks_ count])
889 [presentationModeController_ ensureOverlayShownWithAnimation:NO delay:NO];
891 [presentationModeController_ ensureOverlayHiddenWithAnimation:NO delay:NO];
894 - (void)disableBarVisibilityUpdates {
895 // Early escape if there's nothing to do.
896 if (!barVisibilityUpdatesEnabled_)
899 barVisibilityUpdatesEnabled_ = NO;
900 [presentationModeController_ cancelAnimationAndTimers];
903 - (CGFloat)toolbarDividerOpacity {
904 return [bookmarkBarController_ toolbarDividerOpacity];
907 - (void)updateSubviewZOrder:(BOOL)inPresentationMode {
908 NSView* contentView = [[self window] contentView];
909 NSView* toolbarView = [toolbarController_ view];
911 if (inPresentationMode) {
912 // Toolbar is above tab contents so that it can slide down from top of
914 [contentView cr_ensureSubview:toolbarView
915 isPositioned:NSWindowAbove
916 relativeTo:[self tabContentArea]];
918 // Toolbar is below tab contents so that the info bar arrow can appear above
920 [contentView cr_ensureSubview:toolbarView
921 isPositioned:NSWindowBelow
922 relativeTo:[self tabContentArea]];
925 // The bookmark bar is always below the toolbar.
926 [contentView cr_ensureSubview:[bookmarkBarController_ view]
927 isPositioned:NSWindowBelow
928 relativeTo:toolbarView];
930 if (inPresentationMode) {
931 // In presentation mode the info bar is below all other views.
932 [contentView cr_ensureSubview:[infoBarContainerController_ view]
933 isPositioned:NSWindowBelow
934 relativeTo:[self tabContentArea]];
936 // Above the toolbar but still below tab contents. Similar to the bookmark
937 // bar, this allows Instant results to be above the info bar.
938 [contentView cr_ensureSubview:[infoBarContainerController_ view]
939 isPositioned:NSWindowAbove
940 relativeTo:toolbarView];
943 // The find bar is above everything.
944 if (findBarCocoaController_) {
945 NSView* relativeView = nil;
946 if (inPresentationMode)
947 relativeView = toolbarView;
949 relativeView = [self tabContentArea];
950 [contentView cr_ensureSubview:[findBarCocoaController_ view]
951 isPositioned:NSWindowAbove
952 relativeTo:relativeView];
955 if (floatingBarBackingView_) {
956 if ([floatingBarBackingView_ cr_isBelowView:[self tabContentArea]])
957 [floatingBarBackingView_ removeFromSuperview];
958 if ([self placeBookmarkBarBelowInfoBar]) {
959 [contentView cr_ensureSubview:floatingBarBackingView_
960 isPositioned:NSWindowAbove
961 relativeTo:[bookmarkBarController_ view]];
963 [contentView cr_ensureSubview:floatingBarBackingView_
964 isPositioned:NSWindowBelow
965 relativeTo:[bookmarkBarController_ view]];
970 - (BOOL)shouldAllowOverlappingViews:(BOOL)inPresentationMode {
971 if (inPresentationMode)
974 if (findBarCocoaController_ &&
975 ![[findBarCocoaController_ findBarView] isHidden]) {
979 if (overlappedViewCount_)
985 - (void)updateAllowOverlappingViews:(BOOL)inPresentationMode {
986 WebContents* contents = browser_->tab_strip_model()->GetActiveWebContents();
990 BOOL allowOverlappingViews =
991 [self shouldAllowOverlappingViews:inPresentationMode];
993 // The rendering path with overlapping views disabled causes bugs when
994 // transitioning between composited and non-composited mode.
995 // http://crbug.com/279472
996 allowOverlappingViews = YES;
997 contents->GetView()->SetAllowOverlappingViews(allowOverlappingViews);
999 DevToolsWindow* devToolsWindow =
1000 DevToolsWindow::GetDockedInstanceForInspectedTab(contents);
1001 if (devToolsWindow) {
1002 devToolsWindow->web_contents()->GetView()->
1003 SetAllowOverlappingViews(allowOverlappingViews);
1007 - (void)updateInfoBarTipVisibility {
1008 // If there's no toolbar then hide the infobar tip.
1009 [infoBarContainerController_
1010 setShouldSuppressTopInfoBarTip:![self hasToolbar]];
1013 @end // @implementation BrowserWindowController(Private)