diff options
Diffstat (limited to 'chrome/browser/cocoa/browser_window_controller.mm')
-rw-r--r-- | chrome/browser/cocoa/browser_window_controller.mm | 991 |
1 files changed, 174 insertions, 817 deletions
diff --git a/chrome/browser/cocoa/browser_window_controller.mm b/chrome/browser/cocoa/browser_window_controller.mm index c646528..4818b52 100644 --- a/chrome/browser/cocoa/browser_window_controller.mm +++ b/chrome/browser/cocoa/browser_window_controller.mm @@ -12,32 +12,25 @@ #import "base/scoped_nsobject.h" #include "base/sys_string_conversions.h" #include "chrome/app/chrome_dll_resource.h" // IDC_* -#include "chrome/browser/bookmarks/bookmark_editor.h" #include "chrome/browser/browser.h" #include "chrome/browser/browser_list.h" -#include "chrome/browser/browser_process.h" -#include "chrome/browser/browser_theme_provider.h" #include "chrome/browser/dock_info.h" #include "chrome/browser/encoding_menu_controller.h" #include "chrome/browser/location_bar.h" #include "chrome/browser/profile.h" #include "chrome/browser/window_sizer.h" -#include "chrome/browser/renderer_host/render_widget_host_view.h" -#include "chrome/browser/tab_contents/tab_contents.h" -#include "chrome/browser/tab_contents/tab_contents_view.h" -#include "chrome/browser/tabs/tab_strip_model.h" +#include "chrome/browser/bookmarks/bookmark_editor.h" #import "chrome/browser/cocoa/autocomplete_text_field_editor.h" #import "chrome/browser/cocoa/background_gradient_view.h" #import "chrome/browser/cocoa/bookmark_bar_controller.h" #import "chrome/browser/cocoa/bookmark_editor_controller.h" #import "chrome/browser/cocoa/browser_window_cocoa.h" -#import "chrome/browser/cocoa/chrome_browser_window.h" +#import "chrome/browser/cocoa/browser_window_controller_private.h" #import "chrome/browser/cocoa/download_shelf_controller.h" #import "chrome/browser/cocoa/event_utils.h" #import "chrome/browser/cocoa/fast_resize_view.h" #import "chrome/browser/cocoa/find_bar_cocoa_controller.h" -#include "chrome/browser/cocoa/find_bar_bridge.h" -#import "chrome/browser/cocoa/floating_bar_backing_view.h" +#import "chrome/browser/cocoa/find_bar_bridge.h" #import "chrome/browser/cocoa/focus_tracker.h" #import "chrome/browser/cocoa/fullscreen_controller.h" #import "chrome/browser/cocoa/fullscreen_window.h" @@ -46,37 +39,51 @@ #import "chrome/browser/cocoa/status_bubble_mac.h" #import "chrome/browser/cocoa/tab_contents_controller.h" #import "chrome/browser/cocoa/tab_strip_model_observer_bridge.h" -#import "chrome/browser/cocoa/tab_strip_view.h" #import "chrome/browser/cocoa/tab_strip_controller.h" +#import "chrome/browser/cocoa/tab_strip_view.h" #import "chrome/browser/cocoa/tab_view.h" #import "chrome/browser/cocoa/toolbar_controller.h" +#include "chrome/browser/renderer_host/render_widget_host_view.h" #include "chrome/browser/sync/profile_sync_service.h" #include "chrome/browser/sync/sync_ui_util_mac.h" -#include "chrome/common/pref_names.h" -#include "chrome/common/pref_service.h" -#include "grit/generated_resources.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/tab_contents/tab_contents_view.h" +#include "chrome/browser/tabs/tab_strip_model.h" #include "grit/locale_settings.h" -#include "grit/theme_resources.h" -#import "third_party/GTM/AppKit/GTMTheme.h" + // ORGANIZATION: This is a big file. It is (in principle) organized as follows // (in order): -// 1. Interfaces, including for our private methods. Very short, one-time-use -// classes may include an implementation immediately after their interface. +// 1. Interfaces. Very short, one-time-use classes may include an implementation +// immediately after their interface. // 2. The general implementation section, ordered as follows: // i. Public methods and overrides. // ii. Overrides/implementations of undocumented methods. // iii. Delegate methods for various protocols, formal and informal, to which // |BrowserWindowController| conforms. -// 3. The private implementation section (|BrowserWindowController (Private)|). -// 4. Implementation for |GTMTheme (BrowserThemeProviderInitialization)|. +// 3. (temporary) Implementation sections for various categories. +// +// Private methods are defined and implemented separately in +// browser_window_controller_private.{h,mm}. // // Not all of the above guidelines are followed and more (re-)organization is // needed. BUT PLEASE TRY TO KEEP THIS FILE ORGANIZED. I'd rather re-organize as // little as possible, since doing so messes up the file's history. // -// TODO(viettrungluu): (re-)organize some more, possibly split into separate -// files? +// TODO(viettrungluu): [crbug.com/35543] on-going re-organization, splitting +// things into multiple files -- the plan is as follows: +// - in general, everything stays in browser_window_controller.h, but is split +// off into categories (see below) +// - core stuff stays in browser_window_controller.mm +// - ... overrides also stay (without going into a category, in particular) +// - private stuff which everyone needs goes into +// browser_window_controller_private.{h,mm}; if no one else needs them, they +// can go in individual files (see below) +// - area/task-specific stuff go in browser_window_controller_<area>.mm +// - ... in categories called "(<Area>)" or "(<PrivateArea>)" +// Plan of action: +// - first re-organize into categories +// - then split into files // Notes on self-inflicted (not user-inflicted) window resizing and moving: // @@ -119,25 +126,8 @@ // longer indicate that the window is shrinking from an apparent zoomed state) // and if it's set we continue to constrain the resize. -namespace { -// Insets for the location bar, used when the full toolbar is hidden. -// TODO(viettrungluu): We can argue about the "correct" insetting; I like the -// following best, though arguably 0 inset is better/more correct. -const CGFloat kLocBarLeftRightInset = 1; -const CGFloat kLocBarTopInset = 0; -const CGFloat kLocBarBottomInset = 1; - -// The amount by which the floating bar is offset downwards (to avoid the menu) -// in fullscreen mode. -const CGFloat kFullscreenVerticalBarOffset = 14; -} // end namespace - -@interface GTMTheme (BrowserThemeProviderInitialization) -+ (GTMTheme*)themeWithBrowserThemeProvider:(BrowserThemeProvider*)provider - isOffTheRecord:(BOOL)offTheRecord; -@end -@interface NSWindow (NSPrivateApis) +@interface NSWindow(NSPrivateApis) // Note: These functions are private, use -[NSObject respondsToSelector:] // before calling them. @@ -147,95 +137,6 @@ const CGFloat kFullscreenVerticalBarOffset = 14; @end -@interface BrowserWindowController(Private) - -// Saves the window's position in the local state preferences. -- (void)saveWindowPositionIfNeeded; - -// Saves the window's position to the given pref service. -- (void)saveWindowPositionToPrefs:(PrefService*)prefs; - -// We need to adjust where sheets come out of the window, as by default they -// erupt from the omnibox, which is rather weird. -- (NSRect)window:(NSWindow*)window -willPositionSheet:(NSWindow*)sheet - usingRect:(NSRect)defaultSheetRect; - -// Assign a theme to the window. -- (void)setTheme; - -// Repositions the window's subviews. From the top down: toolbar, normal -// bookmark bar (if shown), infobar, NTP detached bookmark bar (if shown), -// content area, download shelf (if any). -- (void)layoutSubviews; - -// Find the total height of the floating bar (in fullscreen mode). Safe to call -// even when not in fullscreen mode. -- (CGFloat)floatingBarHeight; - -// Lays out the tab strip at the given maximum y-coordinate, with the given -// width, possibly for fullscreen mode; returns the new maximum y (below the tab -// strip). This is safe to call even when there is no tab strip. -- (CGFloat)layoutTabStripAtMaxY:(CGFloat)maxY - width:(CGFloat)width - fullscreen:(BOOL)fullscreen; - -// Lays out the toolbar (or just location bar for popups) at the given maximum -// y-coordinate, with the given width; returns the new maximum y (below the -// toolbar). -- (CGFloat)layoutToolbarAtMaxY:(CGFloat)maxY width:(CGFloat)width; - -// Returns YES if the bookmark bar should be placed below the infobar, NO -// otherwise. -- (BOOL)placeBookmarkBarBelowInfoBar; - -// Lays out the bookmark bar at the given maximum y-coordinate, with the given -// width; returns the new maximum y (below the bookmark bar). Note that one must -// call it with the appropriate |maxY| which depends on whether or not the -// bookmark bar is shown as the NTP bubble or not (use -// |-placeBookmarkBarBelowInfoBar|). -- (CGFloat)layoutBookmarkBarAtMaxY:(CGFloat)maxY width:(CGFloat)width; - -// Lay out the view which draws the background for the floating bar when in -// fullscreen mode, with the given (minimum) y-coordinate, width, height, and -// fullscreen-mode-status. Should be called even when not in fullscreen mode to -// hide the backing view. -- (void)layoutFloatingBarBackingViewAtY:(CGFloat)y - width:(CGFloat)width - height:(CGFloat)height - fullscreen:(BOOL)fullscreen; - -// Lays out the infobar at the given maximum y-coordinate, with the given width; -// returns the new maximum y (below the infobar). -- (CGFloat)layoutInfoBarAtMaxY:(CGFloat)maxY width:(CGFloat)width; - -// Lays out the download shelf, if there is one, at the given minimum -// y-coordinate, with the given width; returns the new minimum y (above the -// download shelf). This is safe to call even if there is no download shelf. -- (CGFloat)layoutDownloadShelfAtMinY:(CGFloat)minY width:(CGFloat)width; - -// Lays out the tab content area between the given minimum and maximum -// y-coordinates, with the given width. -- (void)layoutTabContentAreaAtMinY:(CGFloat)minY - maxY:(CGFloat)maxY - width:(CGFloat)width; - -// Should we show the normal bookmark bar? -- (BOOL)shouldShowBookmarkBar; - -// Is the current page one for which the bookmark should be shown detached *if* -// the normal bookmark bar is not shown? -- (BOOL)shouldShowDetachedBookmarkBar; - -// Sets the toolbar's height to a value appropriate for the given compression. -// Also adjusts the bookmark bar's height by the opposite amount in order to -// keep the total height of the two views constant. -- (void)adjustToolbarAndBookmarkBarForCompression:(CGFloat)compression; - -// Adjust the UI when entering or leaving fullscreen mode. -- (void)adjustUIForFullscreen:(BOOL)fullscreen; - -@end // IncognitoImageView subclasses NSImageView to allow mouse events to pass // through it so you can drag the window by dragging on the spy guy @@ -248,6 +149,7 @@ willPositionSheet:(NSWindow*)sheet } @end + @implementation BrowserWindowController + (BrowserWindowController*)browserWindowControllerForView:(NSView*)view { @@ -264,7 +166,7 @@ willPositionSheet:(NSWindow*)sheet return [self initWithBrowser:browser takeOwnership:YES]; } -// Private (TestingAPI) init routine with testing options. +// Private(TestingAPI) init routine with testing options. - (id)initWithBrowser:(Browser*)browser takeOwnership:(BOOL)ownIt { // Use initWithWindowNibPath:: instead of initWithWindowNibName: so we // can override it in a unit test. @@ -413,11 +315,26 @@ willPositionSheet:(NSWindow*)sheet [super dealloc]; } -// Access the C++ bridge between the NSWindow and the rest of Chromium - (BrowserWindow*)browserWindow { return windowShim_.get(); } +- (ToolbarController*)toolbarController { + return toolbarController_.get(); +} + +- (TabStripController*)tabStripController { + return tabStripController_.get(); +} + +- (StatusBubbleMac*)statusBubble { + return statusBubble_; +} + +- (LocationBar*)locationBarBridge { + return [toolbarController_ locationBarBridge]; +} + - (void)destroyBrowser { [NSApp removeWindowsItem:[self window]]; @@ -1038,14 +955,6 @@ willPositionSheet:(NSWindow*)sheet return [tabStripController_ sheetController]; } -- (LocationBar*)locationBarBridge { - return [toolbarController_ locationBarBridge]; -} - -- (StatusBubbleMac*)statusBubble { - return statusBubble_; -} - - (void)updateToolbarWithContents:(TabContents*)tab shouldRestoreState:(BOOL)shouldRestore { [toolbarController_ updateToolbarWithContents:tab @@ -1162,14 +1071,6 @@ willPositionSheet:(NSWindow*)sheet return [tabStripController_ selectedTabView]; } -- (TabStripController*)tabStripController { - return tabStripController_.get(); -} - -- (ToolbarController*)toolbarController { - return toolbarController_.get(); -} - - (void)setIsLoading:(BOOL)isLoading { [toolbarController_ setIsLoading:isLoading]; } @@ -1320,146 +1221,6 @@ willPositionSheet:(NSWindow*)sheet autorelease]; } -- (void)setFullscreen:(BOOL)fullscreen { - // The logic in this function is a bit complicated and very carefully - // arranged. See the below comments for more details. - - if (fullscreen == [self isFullscreen]) - return; - - if (![self supportsFullscreen]) - return; - - // Save the current first responder so we can restore after views are moved. - NSWindow* window = [self window]; - scoped_nsobject<FocusTracker> focusTracker( - [[FocusTracker alloc] initWithWindow:window]); - BOOL showDropdown = [self floatingBarHasFocus]; - - // If we're entering fullscreen, create the fullscreen controller. If we're - // exiting fullscreen, kill the controller. - if (fullscreen) { - fullscreenController_.reset([[FullscreenController alloc] - initWithBrowserController:self]); - } else { - [fullscreenController_ exitFullscreen]; - fullscreenController_.reset(nil); - } - - // Retain the tab strip view while we remove it from its superview. - scoped_nsobject<NSView> tabStripView; - if ([self hasTabStrip]) { - tabStripView.reset([[self tabStripView] retain]); - [tabStripView removeFromSuperview]; - } - - // Ditto for the content view. - scoped_nsobject<NSView> contentView([[window contentView] retain]); - // Disable autoresizing of subviews while we move views around. This prevents - // spurious renderer resizes. - [contentView setAutoresizesSubviews:NO]; - [contentView removeFromSuperview]; - - NSWindow* destWindow = nil; - if (fullscreen) { - DCHECK(!savedRegularWindow_); - savedRegularWindow_ = [window retain]; - destWindow = [self createFullscreenWindow]; - } else { - DCHECK(savedRegularWindow_); - destWindow = [savedRegularWindow_ autorelease]; - savedRegularWindow_ = nil; - } - DCHECK(destWindow); - - // Have to do this here, otherwise later calls can crash because the window - // has no delegate. - [window 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. - [contentView setAutoresizesSubviews:YES]; - [destWindow setContentView:contentView]; - - // Add the tabstrip after setting the content view, so that the tab strip will - // be on top (in the z-order). - if ([self hasTabStrip]) - [[[destWindow contentView] superview] addSubview:tabStripView]; - - [window setWindowController:nil]; - [self setWindow:destWindow]; - [destWindow setWindowController:self]; - [self adjustUIForFullscreen:fullscreen]; - - // When entering fullscreen mode, the controller forces a layout for us. When - // exiting, we need to call layoutSubviews manually. - if (fullscreen) { - [fullscreenController_ enterFullscreenForContentView:contentView - showDropdown:showDropdown]; - } else { - [self layoutSubviews]; - } - - // The window needs to be onscreen before we can set its first responder. - [destWindow makeKeyAndOrderFront:self]; - [focusTracker restoreFocusInWindow:destWindow]; - [window orderOut:self]; -} - -- (BOOL)isFullscreen { - return savedRegularWindow_ != nil; -} - -- (CGFloat)floatingBarShownFraction { - return floatingBarShownFraction_; -} - -- (void)setFloatingBarShownFraction:(CGFloat)fraction { - floatingBarShownFraction_ = fraction; - [self layoutSubviews]; -} - -- (BOOL)isBarVisibilityLockedForOwner:(id)owner { - DCHECK(owner); - DCHECK(barVisibilityLocks_); - return [barVisibilityLocks_ containsObject:owner]; -} - -- (void)lockBarVisibilityForOwner:(id)owner - withAnimation:(BOOL)animate - delay:(BOOL)delay { - if (![self isBarVisibilityLockedForOwner:owner]) { - [barVisibilityLocks_ addObject:owner]; - - // Show the overlay if necessary (and if in fullscreen mode). - [fullscreenController_ ensureOverlayShownWithAnimation:animate - delay:delay]; - } -} - -- (void)releaseBarVisibilityForOwner:(id)owner - withAnimation:(BOOL)animate - delay:(BOOL)delay { - if ([self isBarVisibilityLockedForOwner:owner]) { - [barVisibilityLocks_ removeObject:owner]; - - // Hide the overlay if necessary (and if in fullscreen mode). - if (![barVisibilityLocks_ count]) { - [fullscreenController_ ensureOverlayHiddenWithAnimation:animate - delay:delay]; - } - } -} - -- (BOOL)floatingBarHasFocus { - NSResponder* focused = [[self window] firstResponder]; - return [focused isKindOfClass:[AutocompleteTextFieldEditor class]]; -} - - (NSInteger)numberOfTabs { return browser_->tabstrip_model()->count(); } @@ -1469,11 +1230,6 @@ willPositionSheet:(NSWindow*)sheet return base::SysUTF16ToNSString(contents->GetTitle()); } -- (BOOL)supportsWindowFeature:(int)feature { - return browser_->SupportsWindowFeature( - static_cast<Browser::WindowFeature>(feature)); -} - - (NSRect)regularWindowFrame { return [self isFullscreen] ? [savedRegularWindow_ frame] : [[self window] frame]; @@ -1484,26 +1240,6 @@ willPositionSheet:(NSWindow*)sheet return [self supportsWindowFeature:Browser::FEATURE_TABSTRIP]; } -- (BOOL)hasTitleBar { - return [self supportsWindowFeature:Browser::FEATURE_TITLEBAR]; -} - -- (BOOL)hasToolbar { - return [self supportsWindowFeature:Browser::FEATURE_TOOLBAR]; -} - -- (BOOL)hasLocationBar { - return [self supportsWindowFeature:Browser::FEATURE_LOCATIONBAR]; -} - -- (BOOL)supportsBookmarkBar { - return [self supportsWindowFeature:Browser::FEATURE_BOOKMARKBAR]; -} - -- (BOOL)isNormalWindow { - return browser_->type() == Browser::TYPE_NORMAL; -} - - (void)selectTabWithContents:(TabContents*)newContents previousContents:(TabContents*)oldContents atIndex:(NSInteger)index @@ -1844,558 +1580,179 @@ willAnimateFromState:(bookmarks::VisualState)oldState isShrinkingFromZoomed_ = NO; } -@end +@end // @implementation BrowserWindowController -@implementation BrowserWindowController (Private) -- (void)saveWindowPositionIfNeeded { - if (browser_ != BrowserList::GetLastActive()) - return; +@implementation BrowserWindowController(Fullscreen) + +- (void)setFullscreen:(BOOL)fullscreen { + // The logic in this function is a bit complicated and very carefully + // arranged. See the below comments for more details. - if (!g_browser_process || !g_browser_process->local_state() || - !browser_->ShouldSaveWindowPlacement()) + if (fullscreen == [self isFullscreen]) return; - [self saveWindowPositionToPrefs:g_browser_process->local_state()]; -} - -- (void)saveWindowPositionToPrefs:(PrefService*)prefs { - // If we're in fullscreen mode, save the position of the regular window - // instead. - NSWindow* window = [self isFullscreen] ? savedRegularWindow_ : [self window]; - - // Window positions are stored relative to the origin of the primary monitor. - NSRect monitorFrame = [[[NSScreen screens] objectAtIndex:0] frame]; - - // 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()); - - // We also need to save the current work area, in flipped coordinates. - gfx::Rect workArea(NSRectToCGRect([[window screen] visibleFrame])); - workArea.set_y(monitorFrame.size.height - workArea.y() - workArea.height()); - - DictionaryValue* windowPreferences = prefs->GetMutableDictionary( - browser_->GetWindowPlacementKey().c_str()); - windowPreferences->SetInteger(L"left", bounds.x()); - windowPreferences->SetInteger(L"top", bounds.y()); - windowPreferences->SetInteger(L"right", bounds.right()); - windowPreferences->SetInteger(L"bottom", bounds.bottom()); - windowPreferences->SetBoolean(L"maximized", false); - windowPreferences->SetBoolean(L"always_on_top", false); - windowPreferences->SetInteger(L"work_area_left", workArea.x()); - windowPreferences->SetInteger(L"work_area_top", workArea.y()); - windowPreferences->SetInteger(L"work_area_right", workArea.right()); - windowPreferences->SetInteger(L"work_area_bottom", workArea.bottom()); -} - -- (NSRect)window:(NSWindow*)window -willPositionSheet:(NSWindow*)sheet - usingRect:(NSRect)defaultSheetRect { - // Position the sheet as follows: - // - 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 shown (attached to the normal toolbar), position - // the sheet below the bookmark bar. - // - If the bookmark bar is currently animating, position the sheet according - // to where the bar will be when the animation ends. - switch ([bookmarkBarController_ visualState]) { - case bookmarks::kShowingState: { - NSRect bookmarkBarFrame = [[bookmarkBarController_ view] frame]; - defaultSheetRect.origin.y = bookmarkBarFrame.origin.y; - break; - } - case bookmarks::kHiddenState: - case bookmarks::kDetachedState: { - NSRect toolbarFrame = [[toolbarController_ view] frame]; - defaultSheetRect.origin.y = toolbarFrame.origin.y; - break; - } - case bookmarks::kInvalidState: - default: - NOTREACHED(); - } - return defaultSheetRect; -} - -- (void)setTheme { - ThemeProvider* theme_provider = browser_->profile()->GetThemeProvider(); - BrowserThemeProvider* browser_theme_provider = - static_cast<BrowserThemeProvider*>(theme_provider); - if (browser_theme_provider) { - bool offtheRecord = browser_->profile()->IsOffTheRecord(); - GTMTheme* theme = - [GTMTheme themeWithBrowserThemeProvider:browser_theme_provider - isOffTheRecord:offtheRecord]; - theme_.reset([theme retain]); - } -} + if (![self supportsFullscreen]) + return; -- (void)layoutSubviews { - // With the exception of the tab strip, the subviews which we lay out are - // subviews of the content view, so we mainly work in the content view's - // coordinate system. Note, however, that the content view's coordinate system - // and the window's base coordinate system should coincide. + // Save the current first responder so we can restore after views are moved. NSWindow* window = [self window]; - NSView* contentView = [window contentView]; - NSRect contentBounds = [contentView bounds]; - CGFloat minY = NSMinY(contentBounds); - CGFloat width = NSWidth(contentBounds); - - // Suppress title drawing if necessary. - if ([window respondsToSelector:@selector(setShouldHideTitle:)]) - [(id)window setShouldHideTitle:![self hasTitleBar]]; + scoped_nsobject<FocusTracker> focusTracker( + [[FocusTracker alloc] initWithWindow:window]); + BOOL showDropdown = [self floatingBarHasFocus]; - BOOL isFullscreen = [self isFullscreen]; - CGFloat floatingBarHeight = [self floatingBarHeight]; - CGFloat yOffset = floor( - isFullscreen ? (1 - floatingBarShownFraction_) * floatingBarHeight : 0); - CGFloat maxY = NSMaxY(contentBounds) + yOffset; - CGFloat startMaxY = maxY; + // If we're entering fullscreen, create the fullscreen controller. If we're + // exiting fullscreen, kill the controller. + if (fullscreen) { + fullscreenController_.reset([[FullscreenController alloc] + initWithBrowserController:self]); + } else { + [fullscreenController_ exitFullscreen]; + fullscreenController_.reset(nil); + } + // Retain the tab strip view while we remove it from its superview. + scoped_nsobject<NSView> tabStripView; if ([self hasTabStrip]) { - // If we need to lay out the tab strip, replace |maxY| and |startMaxY| with - // higher values, and then lay out the tab strip. - startMaxY = maxY = NSHeight([window frame]) + yOffset; - maxY = [self layoutTabStripAtMaxY:maxY width:width fullscreen:isFullscreen]; + tabStripView.reset([[self tabStripView] retain]); + [tabStripView removeFromSuperview]; } - // Sanity-check |maxY|. - DCHECK_GE(maxY, minY); - DCHECK_LE(maxY, NSMaxY(contentBounds) + yOffset); - - // Place the toolbar at the top of the reserved area. - maxY = [self layoutToolbarAtMaxY:maxY width:width]; - - // If we're not displaying the bookmark bar below the infobar, then it goes - // immediately below the toolbar. - BOOL placeBookmarkBarBelowInfoBar = [self placeBookmarkBarBelowInfoBar]; - if (!placeBookmarkBarBelowInfoBar) - maxY = [self layoutBookmarkBarAtMaxY:maxY width:width]; - - // The floating bar backing view doesn't actually add any height. - [self layoutFloatingBarBackingViewAtY:maxY - width:width - height:floatingBarHeight - fullscreen:isFullscreen]; + // Ditto for the content view. + scoped_nsobject<NSView> contentView([[window contentView] retain]); + // Disable autoresizing of subviews while we move views around. This prevents + // spurious renderer resizes. + [contentView setAutoresizesSubviews:NO]; + [contentView removeFromSuperview]; - [fullscreenController_ overlayFrameChanged:[floatingBarBackingView_ frame]]; + NSWindow* destWindow = nil; + if (fullscreen) { + DCHECK(!savedRegularWindow_); + savedRegularWindow_ = [window retain]; + destWindow = [self createFullscreenWindow]; + } else { + DCHECK(savedRegularWindow_); + destWindow = [savedRegularWindow_ autorelease]; + savedRegularWindow_ = nil; + } + DCHECK(destWindow); - // Place the find bar immediately below the toolbar/attached bookmark bar. In - // fullscreen mode, it hangs off the top of the screen when the bar is hidden. - [findBarCocoaController_ positionFindBarViewAtMaxY:maxY maxWidth:width]; + // Have to do this here, otherwise later calls can crash because the window + // has no delegate. + [window setDelegate:nil]; + [destWindow setDelegate:self]; - // If in fullscreen mode, reset |maxY| to top of screen, so that the floating - // bar slides over the things which appear to be in the content area. - if (isFullscreen) - maxY = NSMaxY(contentBounds); + // 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. + [contentView setAutoresizesSubviews:YES]; + [destWindow setContentView:contentView]; - // Also place the infobar container immediate below the toolbar, except in - // fullscreen mode in which case it's at the top of the visual content area. - maxY = [self layoutInfoBarAtMaxY:maxY width:width]; + // Add the tabstrip after setting the content view, so that the tab strip will + // be on top (in the z-order). + if ([self hasTabStrip]) + [[[destWindow contentView] superview] addSubview:tabStripView]; - // If the bookmark bar is detached, place it next in the visual content area. - if (placeBookmarkBarBelowInfoBar) - maxY = [self layoutBookmarkBarAtMaxY:maxY width:width]; + [window setWindowController:nil]; + [self setWindow:destWindow]; + [destWindow setWindowController:self]; + [self adjustUIForFullscreen:fullscreen]; - // Place the download shelf, if any, at the bottom of the view. - minY = [self layoutDownloadShelfAtMinY:minY width:width]; + // When entering fullscreen mode, the controller forces a layout for us. When + // exiting, we need to call layoutSubviews manually. + if (fullscreen) { + [fullscreenController_ enterFullscreenForContentView:contentView + showDropdown:showDropdown]; + } else { + [self layoutSubviews]; + } - // Finally, the content area takes up all of the remaining space. - [self layoutTabContentAreaAtMinY:minY maxY:maxY width:width]; + // The window needs to be onscreen before we can set its first responder. + [destWindow makeKeyAndOrderFront:self]; + [focusTracker restoreFocusInWindow:destWindow]; + [window orderOut:self]; +} - // Place the status bubble at the bottom of the content area. - verticalOffsetForStatusBubble_ = minY; +- (BOOL)isFullscreen { + return savedRegularWindow_ != nil; +} - // Normally, we don't need to tell the toolbar whether or not to show the - // divider, but things break down during animation. - [toolbarController_ - setDividerOpacity:[bookmarkBarController_ toolbarDividerOpacity]]; +- (CGFloat)floatingBarShownFraction { + return floatingBarShownFraction_; } -- (CGFloat)floatingBarHeight { - if (![self isFullscreen]) - return 0; +- (void)setFloatingBarShownFraction:(CGFloat)fraction { + floatingBarShownFraction_ = fraction; + [self layoutSubviews]; +} - CGFloat totalHeight = kFullscreenVerticalBarOffset; +- (BOOL)isBarVisibilityLockedForOwner:(id)owner { + DCHECK(owner); + DCHECK(barVisibilityLocks_); + return [barVisibilityLocks_ containsObject:owner]; +} - if ([self hasTabStrip]) - totalHeight += NSHeight([[self tabStripView] frame]); +- (void)lockBarVisibilityForOwner:(id)owner + withAnimation:(BOOL)animate + delay:(BOOL)delay { + if (![self isBarVisibilityLockedForOwner:owner]) { + [barVisibilityLocks_ addObject:owner]; - if ([self hasToolbar]) { - totalHeight += NSHeight([[toolbarController_ view] frame]); - } else if ([self hasLocationBar]) { - totalHeight += NSHeight([[toolbarController_ view] frame]) + - kLocBarTopInset + kLocBarBottomInset; + // Show the overlay if necessary (and if in fullscreen mode). + [fullscreenController_ ensureOverlayShownWithAnimation:animate + delay:delay]; } - - if (![self placeBookmarkBarBelowInfoBar]) - totalHeight += NSHeight([[bookmarkBarController_ view] frame]); - - return totalHeight; } -- (CGFloat)layoutTabStripAtMaxY:(CGFloat)maxY - width:(CGFloat)width - fullscreen:(BOOL)fullscreen { - // Nothing to do if no tab strip. - if (![self hasTabStrip]) - return maxY; - - NSView* tabStripView = [self tabStripView]; - CGFloat tabStripHeight = NSHeight([tabStripView frame]); - // In fullscreen mode, push the tab strip down so that the main menu (which - // also slides down) doesn't run it over. - if (fullscreen) - maxY -= kFullscreenVerticalBarOffset; - maxY -= tabStripHeight; - [tabStripView setFrame:NSMakeRect(0, maxY, width, tabStripHeight)]; - - // Set indentation. - [tabStripController_ setIndentForControls:(fullscreen ? 0 : - [[tabStripController_ class] defaultIndentForControls])]; - - // TODO(viettrungluu): Seems kind of bad -- shouldn't |-layoutSubviews| do - // this? Moreover, |-layoutTabs| will try to animate.... - [tabStripController_ layoutTabs]; - - return maxY; -} +- (void)releaseBarVisibilityForOwner:(id)owner + withAnimation:(BOOL)animate + delay:(BOOL)delay { + if ([self isBarVisibilityLockedForOwner:owner]) { + [barVisibilityLocks_ removeObject:owner]; -- (CGFloat)layoutToolbarAtMaxY:(CGFloat)maxY width:(CGFloat)width { - NSView* toolbarView = [toolbarController_ view]; - NSRect toolbarFrame = [toolbarView frame]; - if ([self hasToolbar]) { - // The toolbar is present in the window, so we make room for it. - DCHECK(![toolbarView isHidden]); - toolbarFrame.origin.x = 0; - toolbarFrame.origin.y = maxY - NSHeight(toolbarFrame); - toolbarFrame.size.width = width; - maxY -= NSHeight(toolbarFrame); - } else { - if ([self hasLocationBar]) { - // Location bar is present with no toolbar. Put a border of - // |kLocBar...Inset| pixels around the location bar. - // TODO(viettrungluu): This is moderately ridiculous. The toolbar should - // really be aware of what its height should be (the way the toolbar - // compression stuff is currently set up messes things up). - DCHECK(![toolbarView isHidden]); - toolbarFrame.origin.x = kLocBarLeftRightInset; - toolbarFrame.origin.y = maxY - NSHeight(toolbarFrame) - kLocBarTopInset; - toolbarFrame.size.width = width - 2 * kLocBarLeftRightInset; - maxY -= kLocBarTopInset + NSHeight(toolbarFrame) + kLocBarBottomInset; - } else { - DCHECK([toolbarView isHidden]); - } - } - [toolbarView setFrame:toolbarFrame]; - return maxY; -} - -- (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 - // infobar. - return [bookmarkBarController_ isInState:bookmarks::kDetachedState] || - [bookmarkBarController_ isAnimatingToState:bookmarks::kDetachedState] || - [bookmarkBarController_ isAnimatingFromState:bookmarks::kDetachedState]; -} - -- (CGFloat)layoutBookmarkBarAtMaxY:(CGFloat)maxY width:(CGFloat)width { - NSView* bookmarkBarView = [bookmarkBarController_ view]; - NSRect bookmarkBarFrame = [bookmarkBarView frame]; - BOOL oldHidden = [bookmarkBarView isHidden]; - BOOL newHidden = ![self isBookmarkBarVisible]; - if (oldHidden != newHidden) - [bookmarkBarView setHidden:newHidden]; - bookmarkBarFrame.origin.y = maxY - NSHeight(bookmarkBarFrame); - bookmarkBarFrame.size.width = width; - [bookmarkBarView setFrame:bookmarkBarFrame]; - maxY -= NSHeight(bookmarkBarFrame); - - // TODO(viettrungluu): Does this really belong here? Calling it shouldn't be - // necessary in the non-NTP case. - [bookmarkBarController_ layoutSubviews]; - - return maxY; -} - -- (void)layoutFloatingBarBackingViewAtY:(CGFloat)y - width:(CGFloat)width - height:(CGFloat)height - fullscreen:(BOOL)fullscreen { - // Only display when in fullscreen mode. - if (fullscreen) { - DCHECK(floatingBarBackingView_.get()); - BOOL aboveBookmarkBar = [self placeBookmarkBarBelowInfoBar]; - - // Insert it into the view hierarchy if necessary. - if (![floatingBarBackingView_ superview] || - aboveBookmarkBar != floatingBarAboveBookmarkBar_) { - NSView* contentView = [[self window] contentView]; - // z-order gets messed up unless we explicitly remove the floatingbar view - // and re-add it. - [floatingBarBackingView_ removeFromSuperview]; - [contentView addSubview:floatingBarBackingView_ - positioned:(aboveBookmarkBar ? - NSWindowAbove : NSWindowBelow) - relativeTo:[bookmarkBarController_ view]]; - floatingBarAboveBookmarkBar_ = aboveBookmarkBar; + // Hide the overlay if necessary (and if in fullscreen mode). + if (![barVisibilityLocks_ count]) { + [fullscreenController_ ensureOverlayHiddenWithAnimation:animate + delay:delay]; } - - // Set its frame. - [floatingBarBackingView_ setFrame:NSMakeRect(0, y, width, height)]; - } else { - // Okay to call even if |floatingBarBackingView_| is nil. - if ([floatingBarBackingView_ superview]) - [floatingBarBackingView_ removeFromSuperview]; } } -- (CGFloat)layoutInfoBarAtMaxY:(CGFloat)maxY width:(CGFloat)width { - NSView* infoBarView = [infoBarContainerController_ view]; - NSRect infoBarFrame = [infoBarView frame]; - infoBarFrame.origin.y = maxY - NSHeight(infoBarFrame); - infoBarFrame.size.width = width; - [infoBarView setFrame:infoBarFrame]; - maxY -= NSHeight(infoBarFrame); - return maxY; -} - -- (CGFloat)layoutDownloadShelfAtMinY:(CGFloat)minY width:(CGFloat)width { - if (downloadShelfController_.get()) { - NSView* downloadView = [downloadShelfController_ view]; - NSRect downloadFrame = [downloadView frame]; - downloadFrame.origin.y = minY; - downloadFrame.size.width = width; - [downloadView setFrame:downloadFrame]; - minY += NSHeight(downloadFrame); - } - return minY; +- (BOOL)floatingBarHasFocus { + NSResponder* focused = [[self window] firstResponder]; + return [focused isKindOfClass:[AutocompleteTextFieldEditor class]]; } -- (void)layoutTabContentAreaAtMinY:(CGFloat)minY - maxY:(CGFloat)maxY - width:(CGFloat)width { - NSView* tabContentView = [self tabContentArea]; - NSRect tabContentFrame = [tabContentView frame]; +@end // @implementation BrowserWindowController(Fullscreen) - bool contentShifted = NSMaxY(tabContentFrame) != maxY; - tabContentFrame.origin.y = minY; - tabContentFrame.size.height = maxY - minY; - tabContentFrame.size.width = width; - [tabContentView setFrame:tabContentFrame]; +@implementation BrowserWindowController(WindowType) - // If the relayout shifts the content area up or down, let the renderer know. - if (contentShifted) { - if (TabContents* contents = browser_->GetSelectedTabContents()) { - if (RenderWidgetHostView* rwhv = contents->GetRenderWidgetHostView()) - rwhv->WindowFrameChanged(); - } - } +- (BOOL)supportsWindowFeature:(int)feature { + return browser_->SupportsWindowFeature( + static_cast<Browser::WindowFeature>(feature)); } -- (BOOL)shouldShowBookmarkBar { - DCHECK(browser_.get()); - return browser_->profile()->GetPrefs()->GetBoolean(prefs::kShowBookmarkBar) ? - YES : NO; +- (BOOL)hasTitleBar { + return [self supportsWindowFeature:Browser::FEATURE_TITLEBAR]; } -- (BOOL)shouldShowDetachedBookmarkBar { - DCHECK(browser_.get()); - TabContents* contents = browser_->GetSelectedTabContents(); - return (contents && contents->ShouldShowBookmarkBar()) ? YES : NO; +- (BOOL)hasToolbar { + return [self supportsWindowFeature:Browser::FEATURE_TOOLBAR]; } -- (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]; +- (BOOL)hasLocationBar { + return [self supportsWindowFeature:Browser::FEATURE_LOCATIONBAR]; } -- (void)adjustUIForFullscreen:(BOOL)fullscreen { - if (fullscreen) { - mac_util::RequestFullScreen(); - - // Create the floating bar backing view if necessary. - if (!floatingBarBackingView_.get()) { - floatingBarBackingView_.reset( - [[FloatingBarBackingView alloc] initWithFrame:NSZeroRect]); - } - } else { - mac_util::ReleaseFullScreen(); - } +- (BOOL)supportsBookmarkBar { + return [self supportsWindowFeature:Browser::FEATURE_BOOKMARKBAR]; } -@end // @implementation BrowserWindowController (Private) - -@implementation GTMTheme (BrowserThemeProviderInitialization) -+ (GTMTheme*)themeWithBrowserThemeProvider:(BrowserThemeProvider*)provider - isOffTheRecord:(BOOL)isOffTheRecord { - // First check if it's in the cache. - typedef std::pair<std::string, BOOL> ThemeKey; - static std::map<ThemeKey, GTMTheme*> cache; - ThemeKey key(provider->GetThemeID(), isOffTheRecord); - GTMTheme* theme = cache[key]; - if (theme) - return theme; - - theme = [[GTMTheme alloc] init]; // "Leak" it in the cache. - cache[key] = theme; - - // TODO(pinkerton): Need to be able to theme the entire incognito window - // http://crbug.com/18568 The hardcoding of the colors here will need to be - // removed when that bug is addressed, but are here in order for things to be - // usable in the meantime. - if (isOffTheRecord) { - NSColor* incognitoColor = [NSColor colorWithCalibratedRed:83/255.0 - green:108.0/255.0 - blue:140/255.0 - alpha:1.0]; - [theme setBackgroundColor:incognitoColor]; - [theme setValue:[NSColor blackColor] - forAttribute:@"textColor" - style:GTMThemeStyleTabBarSelected - state:GTMThemeStateActiveWindow]; - [theme setValue:[NSColor blackColor] - forAttribute:@"textColor" - style:GTMThemeStyleTabBarDeselected - state:GTMThemeStateActiveWindow]; - [theme setValue:[NSColor blackColor] - forAttribute:@"textColor" - style:GTMThemeStyleBookmarksBarButton - state:GTMThemeStateActiveWindow]; - [theme setValue:[NSColor blackColor] - forAttribute:@"iconColor" - style:GTMThemeStyleToolBarButton - state:GTMThemeStateActiveWindow]; - return theme; - } - - NSImage* frameImage = provider->GetNSImageNamed(IDR_THEME_FRAME); - NSImage* frameInactiveImage = - provider->GetNSImageNamed(IDR_THEME_FRAME_INACTIVE); - - [theme setValue:frameImage - forAttribute:@"backgroundImage" - style:GTMThemeStyleWindow - state:GTMThemeStateActiveWindow]; - - NSColor* tabTextColor = - provider->GetNSColor(BrowserThemeProvider::COLOR_TAB_TEXT); - [theme setValue:tabTextColor - forAttribute:@"textColor" - style:GTMThemeStyleTabBarSelected - state:GTMThemeStateActiveWindow]; - - NSColor* tabInactiveTextColor = - provider->GetNSColor(BrowserThemeProvider::COLOR_BACKGROUND_TAB_TEXT); - [theme setValue:tabInactiveTextColor - forAttribute:@"textColor" - style:GTMThemeStyleTabBarDeselected - state:GTMThemeStateActiveWindow]; - - NSColor* bookmarkBarTextColor = - provider->GetNSColor(BrowserThemeProvider::COLOR_BOOKMARK_TEXT); - [theme setValue:bookmarkBarTextColor - forAttribute:@"textColor" - style:GTMThemeStyleBookmarksBarButton - state:GTMThemeStateActiveWindow]; - - [theme setValue:frameInactiveImage - forAttribute:@"backgroundImage" - style:GTMThemeStyleWindow - state:0]; - - NSImage* toolbarImage = provider->GetNSImageNamed(IDR_THEME_TOOLBAR); - [theme setValue:toolbarImage - forAttribute:@"backgroundImage" - style:GTMThemeStyleToolBar - state:GTMThemeStateActiveWindow]; - NSImage* toolbarBackgroundImage = - provider->GetNSImageNamed(IDR_THEME_TAB_BACKGROUND); - [theme setValue:toolbarBackgroundImage - forAttribute:@"backgroundImage" - style:GTMThemeStyleTabBarDeselected - state:GTMThemeStateActiveWindow]; - - NSImage* toolbarButtonImage = - provider->GetNSImageNamed(IDR_THEME_BUTTON_BACKGROUND); - if (toolbarButtonImage) { - [theme setValue:toolbarButtonImage - forAttribute:@"backgroundImage" - style:GTMThemeStyleToolBarButton - state:GTMThemeStateActiveWindow]; - } else { - NSColor* startColor = [NSColor colorWithCalibratedWhite:1.0 alpha:0.0]; - NSColor* endColor = [NSColor colorWithCalibratedWhite:1.0 alpha:0.3]; - scoped_nsobject<NSGradient> gradient([[NSGradient alloc] - initWithStartingColor:startColor - endingColor:endColor]); - - [theme setValue:gradient - forAttribute:@"gradient" - style:GTMThemeStyleToolBarButton - state:GTMThemeStateActiveWindow]; - - [theme setValue:gradient - forAttribute:@"gradient" - style:GTMThemeStyleToolBarButton - state:GTMThemeStateActiveWindow]; - } - - NSColor* toolbarButtonIconColor = - provider->GetNSColorTint(BrowserThemeProvider::TINT_BUTTONS); - [theme setValue:toolbarButtonIconColor - forAttribute:@"iconColor" - style:GTMThemeStyleToolBarButton - state:GTMThemeStateActiveWindow]; - - NSColor* toolbarButtonBorderColor = toolbarButtonIconColor; - [theme setValue:toolbarButtonBorderColor - forAttribute:@"borderColor" - style:GTMThemeStyleToolBar - state:GTMThemeStateActiveWindow]; - - NSColor* toolbarBackgroundColor = - provider->GetNSColor(BrowserThemeProvider::COLOR_TOOLBAR); - [theme setValue:toolbarBackgroundColor - forAttribute:@"backgroundColor" - style:GTMThemeStyleToolBar - state:GTMThemeStateActiveWindow]; - - NSImage* frameOverlayImage = - provider->GetNSImageNamed(IDR_THEME_FRAME_OVERLAY); - if (frameOverlayImage) { - [theme setValue:frameOverlayImage - forAttribute:@"overlay" - style:GTMThemeStyleWindow - state:GTMThemeStateActiveWindow]; - } - - NSImage* frameOverlayInactiveImage = - provider->GetNSImageNamed(IDR_THEME_FRAME_OVERLAY_INACTIVE); - if (frameOverlayInactiveImage) { - [theme setValue:frameOverlayInactiveImage - forAttribute:@"overlay" - style:GTMThemeStyleWindow - state:GTMThemeStateInactiveWindow]; - } - - return theme; +- (BOOL)isNormalWindow { + return browser_->type() == Browser::TYPE_NORMAL; } -@end + +@end // @implementation BrowserWindowController(WindowType) |