diff options
Diffstat (limited to 'chrome/browser/cocoa/browser_window_controller.mm')
-rw-r--r-- | chrome/browser/cocoa/browser_window_controller.mm | 2059 |
1 files changed, 0 insertions, 2059 deletions
diff --git a/chrome/browser/cocoa/browser_window_controller.mm b/chrome/browser/cocoa/browser_window_controller.mm deleted file mode 100644 index 3face17..0000000 --- a/chrome/browser/cocoa/browser_window_controller.mm +++ /dev/null @@ -1,2059 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#import "chrome/browser/cocoa/browser_window_controller.h" - -#include <Carbon/Carbon.h> - -#include "app/l10n_util.h" -#include "app/l10n_util_mac.h" -#include "base/mac_util.h" -#include "app/mac/scoped_nsdisable_screen_updates.h" -#include "base/nsimage_cache_mac.h" -#import "base/scoped_nsobject.h" -#include "base/sys_string_conversions.h" -#include "chrome/app/chrome_command_ids.h" // IDC_* -#include "chrome/browser/bookmarks/bookmark_editor.h" -#import "chrome/browser/cocoa/background_gradient_view.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_bar_controller.h" -#import "chrome/browser/cocoa/bookmarks/bookmark_editor_controller.h" -#import "chrome/browser/cocoa/browser_window_cocoa.h" -#import "chrome/browser/cocoa/browser_window_controller_private.h" -#import "chrome/browser/cocoa/dev_tools_controller.h" -#import "chrome/browser/cocoa/download/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_bridge.h" -#import "chrome/browser/cocoa/find_bar_cocoa_controller.h" -#import "chrome/browser/cocoa/focus_tracker.h" -#import "chrome/browser/cocoa/fullscreen_controller.h" -#import "chrome/browser/cocoa/fullscreen_window.h" -#import "chrome/browser/cocoa/infobar_container_controller.h" -#import "chrome/browser/cocoa/location_bar/autocomplete_text_field_editor.h" -#import "chrome/browser/cocoa/previewable_contents_controller.h" -#import "chrome/browser/cocoa/nswindow_additions.h" -#import "chrome/browser/cocoa/sad_tab_controller.h" -#import "chrome/browser/cocoa/sidebar_controller.h" -#import "chrome/browser/cocoa/status_bubble_mac.h" -#import "chrome/browser/cocoa/tab_contents_controller.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/tabpose_window.h" -#import "chrome/browser/cocoa/toolbar_controller.h" -#include "chrome/browser/dock_info.h" -#include "chrome/browser/encoding_menu_controller.h" -#include "chrome/browser/google/google_util.h" -#include "chrome/browser/location_bar.h" -#include "chrome/browser/profile.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/browser/tab_contents/tab_contents.h" -#include "chrome/browser/tab_contents/tab_contents_view_mac.h" -#include "chrome/browser/tab_contents_wrapper.h" -#include "chrome/browser/tabs/tab_strip_model.h" -#include "chrome/browser/themes/browser_theme_provider.h" -#include "chrome/browser/ui/browser.h" -#include "chrome/browser/ui/browser_list.h" -#include "chrome/browser/window_sizer.h" -#include "chrome/common/url_constants.h" -#include "grit/generated_resources.h" -#include "grit/locale_settings.h" - -// ORGANIZATION: This is a big file. It is (in principle) organized as follows -// (in order): -// 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. (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): [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: -// -// When the bookmark bar goes from hidden to shown (on a non-NTP) page, or when -// the download shelf goes from hidden to shown, we grow the window downwards in -// order to maintain a constant content area size. When either goes from shown -// to hidden, we consequently shrink the window from the bottom, also to keep -// the content area size constant. To keep things simple, if the window is not -// entirely on-screen, we don't grow/shrink the window. -// -// The complications come in when there isn't enough room (on screen) below the -// window to accomodate the growth. In this case, we grow the window first -// downwards, and then upwards. So, when it comes to shrinking, we do the -// opposite: shrink from the top by the amount by which we grew at the top, and -// then from the bottom -- unless the user moved/resized/zoomed the window, in -// which case we "reset state" and just shrink from the bottom. -// -// A further complication arises due to the way in which "zoom" ("maximize") -// works on Mac OS X. Basically, for our purposes, a window is "zoomed" whenever -// it occupies the full available vertical space. (Note that the green zoom -// button does not track zoom/unzoomed state per se, but basically relies on -// this heuristic.) We don't, in general, want to shrink the window if the -// window is zoomed (scenario: window is zoomed, download shelf opens -- which -// doesn't cause window growth, download shelf closes -- shouldn't cause the -// window to become unzoomed!). However, if we grew the window -// (upwards/downwards) to become zoomed in the first place, we *should* shrink -// the window by the amounts by which we grew (scenario: window occupies *most* -// of vertical space, download shelf opens causing growth so that window -// occupies all of vertical space -- i.e., window is effectively zoomed, -// download shelf closes -- should return the window to its previous state). -// -// A major complication is caused by the way grows/shrinks are handled and -// animated. Basically, the BWC doesn't see the global picture, but it sees -// grows and shrinks in small increments (as dictated by the animation). Thus -// window growth/shrinkage (at the top/bottom) have to be tracked incrementally. -// Allowing shrinking from the zoomed state also requires tracking: We check on -// any shrink whether we're both zoomed and have previously grown -- if so, we -// set a flag, and constrain any resize by the allowed amounts. On further -// shrinks, we check the flag (since the size/position of the window will no -// longer indicate that the window is shrinking from an apparent zoomed state) -// and if it's set we continue to constrain the resize. - - -@interface NSWindow(NSPrivateApis) -// Note: These functions are private, use -[NSObject respondsToSelector:] -// before calling them. - -- (void)setBottomCornerRounded:(BOOL)rounded; - -- (NSRect)_growBoxRect; - -@end - - -// IncognitoImageView subclasses NSImageView to allow mouse events to pass -// through it so you can drag the window by dragging on the spy guy -@interface IncognitoImageView : NSImageView -@end - -@implementation IncognitoImageView -- (BOOL)mouseDownCanMoveWindow { - return YES; -} -@end - - -@implementation BrowserWindowController - -+ (BrowserWindowController*)browserWindowControllerForWindow:(NSWindow*)window { - while (window) { - id controller = [window windowController]; - if ([controller isKindOfClass:[BrowserWindowController class]]) - return (BrowserWindowController*)controller; - window = [window parentWindow]; - } - return nil; -} - -+ (BrowserWindowController*)browserWindowControllerForView:(NSView*)view { - NSWindow* window = [view window]; - return [BrowserWindowController browserWindowControllerForWindow:window]; -} - -// Load the browser window nib and do any Cocoa-specific initialization. -// Takes ownership of |browser|. Note that the nib also sets this controller -// up as the window's delegate. -- (id)initWithBrowser:(Browser*)browser { - return [self initWithBrowser:browser takeOwnership:YES]; -} - -// 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. - NSString* nibpath = [mac_util::MainAppBundle() - pathForResource:@"BrowserWindow" - ofType:@"nib"]; - if ((self = [super initWithWindowNibPath:nibpath owner:self])) { - DCHECK(browser); - initializing_ = YES; - browser_.reset(browser); - ownsBrowser_ = ownIt; - NSWindow* window = [self window]; - windowShim_.reset(new BrowserWindowCocoa(browser, self, window)); - - // Create the bar visibility lock set; 10 is arbitrary, but should hopefully - // be big enough to hold all locks that'll ever be needed. - barVisibilityLocks_.reset([[NSMutableSet setWithCapacity:10] retain]); - - // Sets the window to not have rounded corners, which prevents - // the resize control from being inset slightly and looking ugly. - if ([window respondsToSelector:@selector(setBottomCornerRounded:)]) - [window setBottomCornerRounded:NO]; - - // Get the most appropriate size for the window, then enforce the - // minimum width and height. The window shim will handle flipping - // the coordinates for us so we can use it to save some code. - // Note that this may leave a significant portion of the window - // offscreen, but there will always be enough window onscreen to - // drag the whole window back into view. - NSSize minSize = [[self window] minSize]; - gfx::Rect desiredContentRect = browser_->GetSavedWindowBounds(); - gfx::Rect windowRect = desiredContentRect; - if (windowRect.width() < minSize.width) - windowRect.set_width(minSize.width); - if (windowRect.height() < minSize.height) - windowRect.set_height(minSize.height); - - // When we are given x/y coordinates of 0 on a created popup window, assume - // none were given by the window.open() command. - if (browser_->type() & Browser::TYPE_POPUP && - windowRect.x() == 0 && windowRect.y() == 0) { - gfx::Size size = windowRect.size(); - windowRect.set_origin(WindowSizer::GetDefaultPopupOrigin(size)); - } - - // Size and position the window. Note that it is not yet onscreen. Popup - // windows may get resized later on in this function, once the actual size - // of the toolbar/tabstrip is known. - windowShim_->SetBounds(windowRect); - - // Puts the incognito badge on the window frame, if necessary. - [self installIncognitoBadge]; - - // Create a sub-controller for the docked devTools and add its view to the - // hierarchy. This must happen before the sidebar controller is - // instantiated. - devToolsController_.reset( - [[DevToolsController alloc] initWithDelegate:self]); - [[devToolsController_ view] setFrame:[[self tabContentArea] bounds]]; - [[self tabContentArea] addSubview:[devToolsController_ view]]; - - // Create a sub-controller for the docked sidebar and add its view to the - // hierarchy. This must happen before the previewable contents controller - // is instantiated. - sidebarController_.reset([[SidebarController alloc] initWithDelegate:self]); - [[sidebarController_ view] setFrame:[[devToolsController_ view] bounds]]; - [[devToolsController_ view] addSubview:[sidebarController_ view]]; - - // Create the previewable contents controller. This provides the switch - // view that TabStripController needs. - previewableContentsController_.reset( - [[PreviewableContentsController alloc] init]); - [[previewableContentsController_ view] - setFrame:[[sidebarController_ view] bounds]]; - [[sidebarController_ view] - addSubview:[previewableContentsController_ view]]; - - // Create a controller for the tab strip, giving it the model object for - // this window's Browser and the tab strip view. The controller will handle - // registering for the appropriate tab notifications from the back-end and - // managing the creation of new tabs. - [self createTabStripController]; - - // Create the infobar container view, so we can pass it to the - // ToolbarController. - infoBarContainerController_.reset( - [[InfoBarContainerController alloc] initWithResizeDelegate:self]); - [[[self window] contentView] addSubview:[infoBarContainerController_ view]]; - - // Create a controller for the toolbar, giving it the toolbar model object - // and the toolbar view from the nib. The controller will handle - // registering for the appropriate command state changes from the back-end. - // Adds the toolbar to the content area. - toolbarController_.reset([[ToolbarController alloc] - initWithModel:browser->toolbar_model() - commands:browser->command_updater() - profile:browser->profile() - browser:browser - resizeDelegate:self]); - [toolbarController_ setHasToolbar:[self hasToolbar] - hasLocationBar:[self hasLocationBar]]; - [[[self window] contentView] addSubview:[toolbarController_ view]]; - - // Create a sub-controller for the bookmark bar. - bookmarkBarController_.reset( - [[BookmarkBarController alloc] - initWithBrowser:browser_.get() - initialWidth:NSWidth([[[self window] contentView] frame]) - delegate:self - resizeDelegate:self]); - - // Add bookmark bar to the view hierarchy, which also triggers the nib load. - // The bookmark bar is defined (in the nib) to be bottom-aligned to its - // parent view (among other things), so position and resize properties don't - // need to be set. - [[[self window] contentView] addSubview:[bookmarkBarController_ view] - positioned:NSWindowBelow - relativeTo:[toolbarController_ view]]; - [bookmarkBarController_ setBookmarkBarEnabled:[self supportsBookmarkBar]]; - - // We don't want to try and show the bar before it gets placed in its parent - // view, so this step shoudn't be inside the bookmark bar controller's - // |-awakeFromNib|. - [self updateBookmarkBarVisibilityWithAnimation:NO]; - - // Allow bar visibility to be changed. - [self enableBarVisibilityUpdates]; - - // Force a relayout of all the various bars. - [self layoutSubviews]; - - // For a popup window, |desiredContentRect| contains the desired height of - // the content, not of the whole window. Now that all the views are laid - // out, measure the current content area size and grow if needed. The - // window has not been placed onscreen yet, so this extra resize will not - // cause visible jank. - if (browser_->type() & Browser::TYPE_POPUP) { - CGFloat deltaH = desiredContentRect.height() - - NSHeight([[self tabContentArea] frame]); - // Do not shrink the window, as that may break minimum size invariants. - if (deltaH > 0) { - // Convert from tabContentArea coordinates to window coordinates. - NSSize convertedSize = - [[self tabContentArea] convertSize:NSMakeSize(0, deltaH) - toView:nil]; - NSRect frame = [[self window] frame]; - frame.size.height += convertedSize.height; - frame.origin.y -= convertedSize.height; - [[self window] setFrame:frame display:NO]; - } - } - - // Create the bridge for the status bubble. - statusBubble_ = new StatusBubbleMac([self window], self); - - // Register for application hide/unhide notifications. - [[NSNotificationCenter defaultCenter] - addObserver:self - selector:@selector(applicationDidHide:) - name:NSApplicationDidHideNotification - object:nil]; - [[NSNotificationCenter defaultCenter] - addObserver:self - selector:@selector(applicationDidUnhide:) - name:NSApplicationDidUnhideNotification - object:nil]; - - // This must be done after the view is added to the window since it relies - // on the window bounds to determine whether to show buttons or not. - if ([self hasToolbar]) // Do not create the buttons in popups. - [toolbarController_ createBrowserActionButtons]; - - // We are done initializing now. - initializing_ = NO; - } - return self; -} - -- (void)dealloc { - browser_->CloseAllTabs(); - [downloadShelfController_ exiting]; - - // Explicitly release |fullscreenController_| here, as it may call back to - // this BWC in |-dealloc|. We are required to call |-exitFullscreen| before - // releasing the controller. - [fullscreenController_ exitFullscreen]; - fullscreenController_.reset(); - - // Under certain testing configurations we may not actually own the browser. - if (ownsBrowser_ == NO) - ignore_result(browser_.release()); - - [[NSNotificationCenter defaultCenter] removeObserver:self]; - - [super dealloc]; -} - -- (BrowserWindow*)browserWindow { - return windowShim_.get(); -} - -- (ToolbarController*)toolbarController { - return toolbarController_.get(); -} - -- (TabStripController*)tabStripController { - return tabStripController_.get(); -} - -- (StatusBubbleMac*)statusBubble { - return statusBubble_; -} - -- (LocationBarViewMac*)locationBarBridge { - return [toolbarController_ locationBarBridge]; -} - -- (void)destroyBrowser { - [NSApp removeWindowsItem:[self window]]; - - // We need the window to go away now. - // We can't actually use |-autorelease| here because there's an embedded - // run loop in the |-performClose:| which contains its own autorelease pool. - // Instead call it after a zero-length delay, which gets us back to the main - // event loop. - [self performSelector:@selector(autorelease) - withObject:nil - afterDelay:0]; -} - -// Called when the window meets the criteria to be closed (ie, -// |-windowShouldClose:| returns YES). We must be careful to preserve the -// semantics of BrowserWindow::Close() and not call the Browser's dtor directly -// from this method. -- (void)windowWillClose:(NSNotification*)notification { - DCHECK_EQ([notification object], [self window]); - DCHECK(browser_->tabstrip_model()->empty()); - [savedRegularWindow_ close]; - // We delete statusBubble here because we need to kill off the dependency - // that its window has on our window before our window goes away. - delete statusBubble_; - statusBubble_ = NULL; - // We can't actually use |-autorelease| here because there's an embedded - // run loop in the |-performClose:| which contains its own autorelease pool. - // Instead call it after a zero-length delay, which gets us back to the main - // event loop. - [self performSelector:@selector(autorelease) - withObject:nil - afterDelay:0]; -} - -- (void)attachConstrainedWindow:(ConstrainedWindowMac*)window { - [tabStripController_ attachConstrainedWindow:window]; -} - -- (void)removeConstrainedWindow:(ConstrainedWindowMac*)window { - [tabStripController_ removeConstrainedWindow:window]; -} - -- (BOOL)canAttachConstrainedWindow { - return ![previewableContentsController_ isShowingPreview]; -} - -- (void)updateDevToolsForContents:(TabContents*)contents { - [devToolsController_ updateDevToolsForTabContents:contents]; - [devToolsController_ ensureContentsVisible]; -} - -- (void)updateSidebarForContents:(TabContents*)contents { - [sidebarController_ updateSidebarForTabContents:contents]; - [sidebarController_ ensureContentsVisible]; -} - -// Called when the user wants to close a window or from the shutdown process. -// The Browser object is in control of whether or not we're allowed to close. It -// may defer closing due to several states, such as onUnload handlers needing to -// be fired. If closing is deferred, the Browser will handle the processing -// required to get us to the closing state and (by watching for all the tabs -// going away) will again call to close the window when it's finally ready. -- (BOOL)windowShouldClose:(id)sender { - // Disable updates while closing all tabs to avoid flickering. - app::mac::ScopedNSDisableScreenUpdates disabler; - // Give beforeunload handlers the chance to cancel the close before we hide - // the window below. - if (!browser_->ShouldCloseWindow()) - return NO; - - // saveWindowPositionIfNeeded: only works if we are the last active - // window, but orderOut: ends up activating another window, so we - // have to save the window position before we call orderOut:. - [self saveWindowPositionIfNeeded]; - - if (!browser_->tabstrip_model()->empty()) { - // Tab strip isn't empty. Hide the frame (so it appears to have closed - // immediately) and close all the tabs, allowing the renderers to shut - // down. When the tab strip is empty we'll be called back again. - [[self window] orderOut:self]; - browser_->OnWindowClosing(); - return NO; - } - - // the tab strip is empty, it's ok to close the window - return YES; -} - -// Called right after our window became the main window. -- (void)windowDidBecomeMain:(NSNotification*)notification { - BrowserList::SetLastActive(browser_.get()); - [self saveWindowPositionIfNeeded]; - - // TODO(dmaclach): Instead of redrawing the whole window, views that care - // about the active window state should be registering for notifications. - [[self window] setViewsNeedDisplay:YES]; - - // TODO(viettrungluu): For some reason, the above doesn't suffice. - if ([self isFullscreen]) - [floatingBarBackingView_ setNeedsDisplay:YES]; // Okay even if nil. -} - -- (void)windowDidResignMain:(NSNotification*)notification { - // TODO(dmaclach): Instead of redrawing the whole window, views that care - // about the active window state should be registering for notifications. - [[self window] setViewsNeedDisplay:YES]; - - // TODO(viettrungluu): For some reason, the above doesn't suffice. - if ([self isFullscreen]) - [floatingBarBackingView_ setNeedsDisplay:YES]; // Okay even if nil. -} - -// Called when we are activated (when we gain focus). -- (void)windowDidBecomeKey:(NSNotification*)notification { - // We need to activate the controls (in the "WebView"). To do this, get the - // selected TabContents's RenderWidgetHostViewMac and tell it to activate. - if (TabContents* contents = browser_->GetSelectedTabContents()) { - if (RenderWidgetHostView* rwhv = contents->GetRenderWidgetHostView()) - rwhv->SetActive(true); - } -} - -// Called when we are deactivated (when we lose focus). -- (void)windowDidResignKey:(NSNotification*)notification { - // If our app is still active and we're still the key window, ignore this - // message, since it just means that a menu extra (on the "system status bar") - // was activated; we'll get another |-windowDidResignKey| if we ever really - // lose key window status. - if ([NSApp isActive] && ([NSApp keyWindow] == [self window])) - return; - - // We need to deactivate the controls (in the "WebView"). To do this, get the - // selected TabContents's RenderWidgetHostView and tell it to deactivate. - if (TabContents* contents = browser_->GetSelectedTabContents()) { - if (RenderWidgetHostView* rwhv = contents->GetRenderWidgetHostView()) - rwhv->SetActive(false); - } -} - -// Called when we have been minimized. -- (void)windowDidMiniaturize:(NSNotification *)notification { - // Let the selected RenderWidgetHostView know, so that it can tell plugins. - if (TabContents* contents = browser_->GetSelectedTabContents()) { - if (RenderWidgetHostView* rwhv = contents->GetRenderWidgetHostView()) - rwhv->SetWindowVisibility(false); - } -} - -// Called when we have been unminimized. -- (void)windowDidDeminiaturize:(NSNotification *)notification { - // Let the selected RenderWidgetHostView know, so that it can tell plugins. - if (TabContents* contents = browser_->GetSelectedTabContents()) { - if (RenderWidgetHostView* rwhv = contents->GetRenderWidgetHostView()) - rwhv->SetWindowVisibility(true); - } -} - -// Called when the application has been hidden. -- (void)applicationDidHide:(NSNotification *)notification { - // Let the selected RenderWidgetHostView know, so that it can tell plugins - // (unless we are minimized, in which case nothing has really changed). - if (![[self window] isMiniaturized]) { - if (TabContents* contents = browser_->GetSelectedTabContents()) { - if (RenderWidgetHostView* rwhv = contents->GetRenderWidgetHostView()) - rwhv->SetWindowVisibility(false); - } - } -} - -// Called when the application has been unhidden. -- (void)applicationDidUnhide:(NSNotification *)notification { - // Let the selected RenderWidgetHostView know, so that it can tell plugins - // (unless we are minimized, in which case nothing has really changed). - if (![[self window] isMiniaturized]) { - if (TabContents* contents = browser_->GetSelectedTabContents()) { - if (RenderWidgetHostView* rwhv = contents->GetRenderWidgetHostView()) - rwhv->SetWindowVisibility(true); - } - } -} - -// Called when the user clicks the zoom button (or selects it from the Window -// menu) to determine the "standard size" of the window, based on the content -// and other factors. If the current size/location differs nontrivally from the -// standard size, Cocoa resizes the window to the standard size, and saves the -// current size as the "user size". If the current size/location is the same (up -// to a fudge factor) as the standard size, Cocoa resizes the window to the -// saved user size. (It is possible for the two to coincide.) In this way, the -// zoom button acts as a toggle. We determine the standard size based on the -// content, but enforce a minimum width (calculated using the dimensions of the -// screen) to ensure websites with small intrinsic width (such as google.com) -// don't end up with a wee window. Moreover, we always declare the standard -// width to be at least as big as the current width, i.e., we never want zooming -// to the standard width to shrink the window. This is consistent with other -// browsers' behaviour, and is desirable in multi-tab situations. Note, however, -// that the "toggle" behaviour means that the window can still be "unzoomed" to -// the user size. -- (NSRect)windowWillUseStandardFrame:(NSWindow*)window - defaultFrame:(NSRect)frame { - // Forget that we grew the window up (if we in fact did). - [self resetWindowGrowthState]; - - // |frame| already fills the current screen. Never touch y and height since we - // always want to fill vertically. - - // If the shift key is down, maximize. Hopefully this should make the - // "switchers" happy. - if ([[NSApp currentEvent] modifierFlags] & NSShiftKeyMask) { - return frame; - } - - // To prevent strange results on portrait displays, the basic minimum zoomed - // width is the larger of: 60% of available width, 60% of available height - // (bounded by available width). - const CGFloat kProportion = 0.6; - CGFloat zoomedWidth = - std::max(kProportion * frame.size.width, - std::min(kProportion * frame.size.height, frame.size.width)); - - TabContents* contents = browser_->GetSelectedTabContents(); - if (contents) { - // If the intrinsic width is bigger, then make it the zoomed width. - const int kScrollbarWidth = 16; // TODO(viettrungluu): ugh. - TabContentsViewMac* tab_contents_view = - static_cast<TabContentsViewMac*>(contents->view()); - CGFloat intrinsicWidth = static_cast<CGFloat>( - tab_contents_view->preferred_width() + kScrollbarWidth); - zoomedWidth = std::max(zoomedWidth, - std::min(intrinsicWidth, frame.size.width)); - } - - // Never shrink from the current size on zoom (see above). - NSRect currentFrame = [[self window] frame]; - zoomedWidth = std::max(zoomedWidth, currentFrame.size.width); - - // |frame| determines our maximum extents. We need to set the origin of the - // frame -- and only move it left if necessary. - if (currentFrame.origin.x + zoomedWidth > frame.origin.x + frame.size.width) - frame.origin.x = frame.origin.x + frame.size.width - zoomedWidth; - else - frame.origin.x = currentFrame.origin.x; - - // Set the width. Don't touch y or height. - frame.size.width = zoomedWidth; - - return frame; -} - -- (void)activate { - [[self window] makeKeyAndOrderFront:self]; -} - -// Determine whether we should let a window zoom/unzoom to the given |newFrame|. -// We avoid letting unzoom move windows between screens, because it's really -// strange and unintuitive. -- (BOOL)windowShouldZoom:(NSWindow*)window toFrame:(NSRect)newFrame { - // Figure out which screen |newFrame| is on. - NSScreen* newScreen = nil; - CGFloat newScreenOverlapArea = 0.0; - for (NSScreen* screen in [NSScreen screens]) { - NSRect overlap = NSIntersectionRect(newFrame, [screen frame]); - CGFloat overlapArea = overlap.size.width * overlap.size.height; - if (overlapArea > newScreenOverlapArea) { - newScreen = screen; - newScreenOverlapArea = overlapArea; - } - } - // If we're somehow not on any screen, allow the zoom. - if (!newScreen) - return YES; - - // If the new screen is the current screen, we can return a definitive YES. - // Note: This check is not strictly necessary, but just short-circuits in the - // "no-brainer" case. To test the complicated logic below, comment this out! - NSScreen* curScreen = [window screen]; - if (newScreen == curScreen) - return YES; - - // Worry a little: What happens when a window is on two (or more) screens? - // E.g., what happens in a 50-50 scenario? Cocoa may reasonably elect to zoom - // to the other screen rather than staying on the officially current one. So - // we compare overlaps with the current window frame, and see if Cocoa's - // choice was reasonable (allowing a small rounding error). This should - // hopefully avoid us ever erroneously denying a zoom when a window is on - // multiple screens. - NSRect curFrame = [window frame]; - NSRect newScrIntersectCurFr = NSIntersectionRect([newScreen frame], curFrame); - NSRect curScrIntersectCurFr = NSIntersectionRect([curScreen frame], curFrame); - if (newScrIntersectCurFr.size.width*newScrIntersectCurFr.size.height >= - (curScrIntersectCurFr.size.width*curScrIntersectCurFr.size.height - 1.0)) - return YES; - - // If it wasn't reasonable, return NO. - return NO; -} - -// Adjusts the window height by the given amount. -- (void)adjustWindowHeightBy:(CGFloat)deltaH { - // By not adjusting the window height when initializing, we can ensure that - // the window opens with the same size that was saved on close. - if (initializing_ || [self isFullscreen] || deltaH == 0) - return; - - NSWindow* window = [self window]; - NSRect windowFrame = [window frame]; - NSRect workarea = [[window screen] visibleFrame]; - - // If the window is not already fully in the workarea, do not adjust its frame - // at all. - if (!NSContainsRect(workarea, windowFrame)) - return; - - // Record the position of the top/bottom of the window, so we can easily check - // whether we grew the window upwards/downwards. - CGFloat oldWindowMaxY = NSMaxY(windowFrame); - CGFloat oldWindowMinY = NSMinY(windowFrame); - - // We are "zoomed" if we occupy the full vertical space. - bool isZoomed = (windowFrame.origin.y == workarea.origin.y && - windowFrame.size.height == workarea.size.height); - - // If we're shrinking the window.... - if (deltaH < 0) { - bool didChange = false; - - // Don't reset if not currently zoomed since shrinking can take several - // steps! - if (isZoomed) - isShrinkingFromZoomed_ = YES; - - // If we previously grew at the top, shrink as much as allowed at the top - // first. - if (windowTopGrowth_ > 0) { - CGFloat shrinkAtTopBy = MIN(-deltaH, windowTopGrowth_); - windowFrame.size.height -= shrinkAtTopBy; // Shrink the window. - deltaH += shrinkAtTopBy; // Update the amount left to shrink. - windowTopGrowth_ -= shrinkAtTopBy; // Update the growth state. - didChange = true; - } - - // Similarly for the bottom (not an "else if" since we may have to - // simultaneously shrink at both the top and at the bottom). Note that - // |deltaH| may no longer be nonzero due to the above. - if (deltaH < 0 && windowBottomGrowth_ > 0) { - CGFloat shrinkAtBottomBy = MIN(-deltaH, windowBottomGrowth_); - windowFrame.origin.y += shrinkAtBottomBy; // Move the window up. - windowFrame.size.height -= shrinkAtBottomBy; // Shrink the window. - deltaH += shrinkAtBottomBy; // Update the amount left.... - windowBottomGrowth_ -= shrinkAtBottomBy; // Update the growth state. - didChange = true; - } - - // If we're shrinking from zoomed but we didn't change the top or bottom - // (since we've reached the limits imposed by |window...Growth_|), then stop - // here. Don't reset |isShrinkingFromZoomed_| since we might get called - // again for the same shrink. - if (isShrinkingFromZoomed_ && !didChange) - return; - } else { - isShrinkingFromZoomed_ = NO; - - // Don't bother with anything else. - if (isZoomed) - return; - } - - // Shrinking from zoomed is handled above (and is constrained by - // |window...Growth_|). - if (!isShrinkingFromZoomed_) { - // Resize the window down until it hits the bottom of the workarea, then if - // needed continue resizing upwards. Do not resize the window to be taller - // than the current workarea. - // Resize the window as requested, keeping the top left corner fixed. - windowFrame.origin.y -= deltaH; - windowFrame.size.height += deltaH; - - // If the bottom left corner is now outside the visible frame, move the - // window up to make it fit, but make sure not to move the top left corner - // out of the visible frame. - if (windowFrame.origin.y < workarea.origin.y) { - windowFrame.origin.y = workarea.origin.y; - windowFrame.size.height = - std::min(windowFrame.size.height, workarea.size.height); - } - - // Record (if applicable) how much we grew the window in either direction. - // (N.B.: These only record growth, not shrinkage.) - if (NSMaxY(windowFrame) > oldWindowMaxY) - windowTopGrowth_ += NSMaxY(windowFrame) - oldWindowMaxY; - if (NSMinY(windowFrame) < oldWindowMinY) - windowBottomGrowth_ += oldWindowMinY - NSMinY(windowFrame); - } - - // Disable subview resizing while resizing the window, or else we will get - // unwanted renderer resizes. The calling code must call layoutSubviews to - // make things right again. - NSView* contentView = [window contentView]; - [contentView setAutoresizesSubviews:NO]; - [window setFrame:windowFrame display:NO]; - [contentView setAutoresizesSubviews:YES]; -} - -// Main method to resize browser window subviews. This method should be called -// when resizing any child of the content view, rather than resizing the views -// directly. If the view is already the correct height, does not force a -// relayout. -- (void)resizeView:(NSView*)view newHeight:(CGFloat)height { - // We should only ever be called for one of the following four views. - // |downloadShelfController_| may be nil. If we are asked to size the bookmark - // bar directly, its superview must be this controller's content view. - DCHECK(view); - DCHECK(view == [toolbarController_ view] || - view == [infoBarContainerController_ view] || - view == [downloadShelfController_ view] || - view == [bookmarkBarController_ view]); - - // Change the height of the view and call |-layoutSubViews|. We set the height - // here without regard to where the view is on the screen or whether it needs - // to "grow up" or "grow down." The below call to |-layoutSubviews| will - // position each view correctly. - NSRect frame = [view frame]; - if (NSHeight(frame) == height) - return; - - // Grow or shrink the window by the amount of the height change. We adjust - // the window height only in two cases: - // 1) We are adjusting the height of the bookmark bar and it is currently - // animating either open or closed. - // 2) We are adjusting the height of the download shelf. - // - // We do not adjust the window height for bookmark bar changes on the NTP. - BOOL shouldAdjustBookmarkHeight = - [bookmarkBarController_ isAnimatingBetweenState:bookmarks::kHiddenState - andState:bookmarks::kShowingState]; - if ((shouldAdjustBookmarkHeight && view == [bookmarkBarController_ view]) || - view == [downloadShelfController_ view]) { - [[self window] disableScreenUpdatesUntilFlush]; - CGFloat deltaH = height - frame.size.height; - [self adjustWindowHeightBy:deltaH]; - } - - frame.size.height = height; - // TODO(rohitrao): Determine if calling setFrame: twice is bad. - [view setFrame:frame]; - [self layoutSubviews]; -} - -- (void)setAnimationInProgress:(BOOL)inProgress { - [[self tabContentArea] setFastResizeMode:inProgress]; -} - -// Update a toggle state for an NSMenuItem if modified. -// Take care to ensure |item| looks like a NSMenuItem. -// Called by validateUserInterfaceItem:. -- (void)updateToggleStateWithTag:(NSInteger)tag forItem:(id)item { - if (![item respondsToSelector:@selector(state)] || - ![item respondsToSelector:@selector(setState:)]) - return; - - // On Windows this logic happens in bookmark_bar_view.cc. On the - // Mac we're a lot more MVC happy so we've moved it into a - // controller. To be clear, this simply updates the menu item; it - // does not display the bookmark bar itself. - if (tag == IDC_SHOW_BOOKMARK_BAR) { - bool toggled = windowShim_->IsBookmarkBarVisible(); - NSInteger oldState = [item state]; - NSInteger newState = toggled ? NSOnState : NSOffState; - if (oldState != newState) - [item setState:newState]; - } - - // Update the checked/Unchecked state of items in the encoding menu. - // On Windows, this logic is part of |EncodingMenuModel| in - // browser/views/toolbar_view.h. - EncodingMenuController encoding_controller; - if (encoding_controller.DoesCommandBelongToEncodingMenu(tag)) { - DCHECK(browser_.get()); - Profile* profile = browser_->profile(); - DCHECK(profile); - TabContents* current_tab = browser_->GetSelectedTabContents(); - if (!current_tab) { - return; - } - const std::string encoding = current_tab->encoding(); - - bool toggled = encoding_controller.IsItemChecked(profile, encoding, tag); - NSInteger oldState = [item state]; - NSInteger newState = toggled ? NSOnState : NSOffState; - if (oldState != newState) - [item setState:newState]; - } -} - -- (BOOL)supportsFullscreen { - // TODO(avi, thakis): GTMWindowSheetController has no api to move - // tabsheets between windows. Until then, we have to prevent having to - // move a tabsheet between windows, e.g. no fullscreen toggling - NSArray* a = [[tabStripController_ sheetController] viewsWithAttachedSheets]; - return [a count] == 0; -} - -// Called to validate menu and toolbar items when this window is key. All the -// items we care about have been set with the |-commandDispatch:| or -// |-commandDispatchUsingKeyModifiers:| actions and a target of FirstResponder -// in IB. If it's not one of those, let it continue up the responder chain to be -// handled elsewhere. We pull out the tag as the cross-platform constant to -// differentiate and dispatch the various commands. -// NOTE: we might have to handle state for app-wide menu items, -// although we could cheat and directly ask the app controller if our -// command_updater doesn't support the command. This may or may not be an issue, -// too early to tell. -- (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item { - SEL action = [item action]; - BOOL enable = NO; - if (action == @selector(commandDispatch:) || - action == @selector(commandDispatchUsingKeyModifiers:)) { - NSInteger tag = [item tag]; - if (browser_->command_updater()->SupportsCommand(tag)) { - // Generate return value (enabled state) - enable = browser_->command_updater()->IsCommandEnabled(tag); - switch (tag) { - case IDC_CLOSE_TAB: - // Disable "close tab" if we're not the key window or if there's only - // one tab. - enable &= [self numberOfTabs] > 1 && [[self window] isKeyWindow]; - break; - case IDC_FULLSCREEN: { - enable &= [self supportsFullscreen]; - if ([static_cast<NSObject*>(item) isKindOfClass:[NSMenuItem class]]) { - NSString* menuTitle = l10n_util::GetNSString( - [self isFullscreen] ? IDS_EXIT_FULLSCREEN_MAC : - IDS_ENTER_FULLSCREEN_MAC); - [static_cast<NSMenuItem*>(item) setTitle:menuTitle]; - } - break; - } - case IDC_SYNC_BOOKMARKS: - enable &= browser_->profile()->IsSyncAccessible(); - sync_ui_util::UpdateSyncItem(item, enable, browser_->profile()); - break; - default: - // Special handling for the contents of the Text Encoding submenu. On - // Mac OS, instead of enabling/disabling the top-level menu item, we - // enable/disable the submenu's contents (per Apple's HIG). - EncodingMenuController encoding_controller; - if (encoding_controller.DoesCommandBelongToEncodingMenu(tag)) { - enable &= browser_->command_updater()->IsCommandEnabled( - IDC_ENCODING_MENU) ? YES : NO; - } - } - - // If the item is toggleable, find its toggle state and - // try to update it. This is a little awkward, but the alternative is - // to check after a commandDispatch, which seems worse. - [self updateToggleStateWithTag:tag forItem:item]; - } - } - return enable; -} - -// Called when the user picks a menu or toolbar item when this window is key. -// Calls through to the browser object to execute the command. This assumes that -// the command is supported and doesn't check, otherwise it would have been -// disabled in the UI in validateUserInterfaceItem:. -- (void)commandDispatch:(id)sender { - DCHECK(sender); - // Identify the actual BWC to which the command should be dispatched. It might - // belong to a background window, yet this controller gets it because it is - // the foreground window's controller and thus in the responder chain. Some - // senders don't have this problem (for example, menus only operate on the - // foreground window), so this is only an issue for senders that are part of - // windows. - BrowserWindowController* targetController = self; - if ([sender respondsToSelector:@selector(window)]) - targetController = [[sender window] windowController]; - DCHECK([targetController isKindOfClass:[BrowserWindowController class]]); - DCHECK(targetController->browser_.get()); - targetController->browser_->ExecuteCommand([sender tag]); -} - -// Same as |-commandDispatch:|, but executes commands using a disposition -// determined by the key flags. If the window is in the background and the -// command key is down, ignore the command key, but process any other modifiers. -- (void)commandDispatchUsingKeyModifiers:(id)sender { - DCHECK(sender); - // See comment above for why we do this. - BrowserWindowController* targetController = self; - if ([sender respondsToSelector:@selector(window)]) - targetController = [[sender window] windowController]; - DCHECK([targetController isKindOfClass:[BrowserWindowController class]]); - NSInteger command = [sender tag]; - NSUInteger modifierFlags = [[NSApp currentEvent] modifierFlags]; - if ((command == IDC_RELOAD) && - (modifierFlags & (NSShiftKeyMask | NSControlKeyMask))) { - command = IDC_RELOAD_IGNORING_CACHE; - // Mask off Shift and Control so they don't affect the disposition below. - modifierFlags &= ~(NSShiftKeyMask | NSControlKeyMask); - } - if (![[sender window] isMainWindow]) { - // Remove the command key from the flags, it means "keep the window in - // the background" in this case. - modifierFlags &= ~NSCommandKeyMask; - } - WindowOpenDisposition disposition = - event_utils::WindowOpenDispositionFromNSEventWithFlags( - [NSApp currentEvent], modifierFlags); - switch (command) { - case IDC_BACK: - case IDC_FORWARD: - case IDC_RELOAD: - case IDC_RELOAD_IGNORING_CACHE: - if (disposition == CURRENT_TAB) { - // Forcibly reset the location bar, since otherwise it won't discard any - // ongoing user edits, since it doesn't realize this is a user-initiated - // action. - [targetController locationBarBridge]->Revert(); - } - } - DCHECK(targetController->browser_.get()); - targetController->browser_->ExecuteCommandWithDisposition(command, - disposition); -} - -// Called when another part of the internal codebase needs to execute a -// command. -- (void)executeCommand:(int)command { - if (browser_->command_updater()->IsCommandEnabled(command)) - browser_->ExecuteCommand(command); -} - -// StatusBubble delegate method: tell the status bubble the frame it should -// position itself in. -- (NSRect)statusBubbleBaseFrame { - NSView* view = [previewableContentsController_ view]; - return [view convertRect:[view bounds] toView:nil]; -} - -- (GTMWindowSheetController*)sheetController { - return [tabStripController_ sheetController]; -} - -- (void)updateToolbarWithContents:(TabContents*)tab - shouldRestoreState:(BOOL)shouldRestore { - [toolbarController_ updateToolbarWithContents:tab - shouldRestoreState:shouldRestore]; -} - -- (void)setStarredState:(BOOL)isStarred { - [toolbarController_ setStarredState:isStarred]; -} - -// Accept tabs from a BrowserWindowController with the same Profile. -- (BOOL)canReceiveFrom:(TabWindowController*)source { - if (![source isKindOfClass:[BrowserWindowController class]]) { - return NO; - } - - BrowserWindowController* realSource = - static_cast<BrowserWindowController*>(source); - if (browser_->profile() != realSource->browser_->profile()) { - return NO; - } - - // Can't drag a tab from a normal browser to a pop-up - if (browser_->type() != realSource->browser_->type()) { - return NO; - } - - return YES; -} - -// Move a given tab view to the location of the current placeholder. If there is -// no placeholder, it will go at the end. |controller| is the window controller -// of a tab being dropped from a different window. It will be nil if the drag is -// within the window, otherwise the tab is removed from that window before being -// placed into this one. The implementation will call |-removePlaceholder| since -// the drag is now complete. This also calls |-layoutTabs| internally so -// clients do not need to call it again. -- (void)moveTabView:(NSView*)view - fromController:(TabWindowController*)dragController { - if (dragController) { - // Moving between windows. Figure out the TabContents to drop into our tab - // model from the source window's model. - BOOL isBrowser = - [dragController isKindOfClass:[BrowserWindowController class]]; - DCHECK(isBrowser); - if (!isBrowser) return; - BrowserWindowController* dragBWC = (BrowserWindowController*)dragController; - int index = [dragBWC->tabStripController_ modelIndexForTabView:view]; - TabContentsWrapper* contents = - dragBWC->browser_->GetTabContentsWrapperAt(index); - // The tab contents may have gone away if given a window.close() while it - // is being dragged. If so, bail, we've got nothing to drop. - if (!contents) - return; - - // Convert |view|'s frame (which starts in the source tab strip's coordinate - // system) to the coordinate system of the destination tab strip. This needs - // to be done before being detached so the window transforms can be - // performed. - NSRect destinationFrame = [view frame]; - NSPoint tabOrigin = destinationFrame.origin; - tabOrigin = [[dragController tabStripView] convertPoint:tabOrigin - toView:nil]; - tabOrigin = [[view window] convertBaseToScreen:tabOrigin]; - tabOrigin = [[self window] convertScreenToBase:tabOrigin]; - tabOrigin = [[self tabStripView] convertPoint:tabOrigin fromView:nil]; - destinationFrame.origin = tabOrigin; - - // Before the tab is detached from its originating tab strip, store the - // pinned state so that it can be maintained between the windows. - bool isPinned = dragBWC->browser_->tabstrip_model()->IsTabPinned(index); - - // Now that we have enough information about the tab, we can remove it from - // the dragging window. We need to do this *before* we add it to the new - // window as this will remove the TabContents' delegate. - [dragController detachTabView:view]; - - // Deposit it into our model at the appropriate location (it already knows - // where it should go from tracking the drag). Doing this sets the tab's - // delegate to be the Browser. - [tabStripController_ dropTabContents:contents - withFrame:destinationFrame - asPinnedTab:isPinned]; - } else { - // Moving within a window. - int index = [tabStripController_ modelIndexForTabView:view]; - [tabStripController_ moveTabFromIndex:index]; - } - - // Remove the placeholder since the drag is now complete. - [self removePlaceholder]; -} - -// Tells the tab strip to forget about this tab in preparation for it being -// put into a different tab strip, such as during a drop on another window. -- (void)detachTabView:(NSView*)view { - int index = [tabStripController_ modelIndexForTabView:view]; - browser_->tabstrip_model()->DetachTabContentsAt(index); -} - -- (NSView*)selectedTabView { - return [tabStripController_ selectedTabView]; -} - -- (void)setIsLoading:(BOOL)isLoading force:(BOOL)force { - [toolbarController_ setIsLoading:isLoading force:force]; -} - -// Make the location bar the first responder, if possible. -- (void)focusLocationBar:(BOOL)selectAll { - [toolbarController_ focusLocationBar:selectAll]; -} - -- (void)focusTabContents { - [[self window] makeFirstResponder:[tabStripController_ selectedTabView]]; -} - -- (void)layoutTabs { - [tabStripController_ layoutTabs]; -} - -- (TabWindowController*)detachTabToNewWindow:(TabView*)tabView { - // Disable screen updates so that this appears as a single visual change. - app::mac::ScopedNSDisableScreenUpdates disabler; - - // Fetch the tab contents for the tab being dragged. - int index = [tabStripController_ modelIndexForTabView:tabView]; - TabContentsWrapper* contents = browser_->GetTabContentsWrapperAt(index); - - // Set the window size. Need to do this before we detach the tab so it's - // still in the window. We have to flip the coordinates as that's what - // is expected by the Browser code. - NSWindow* sourceWindow = [tabView window]; - NSRect windowRect = [sourceWindow frame]; - NSScreen* screen = [sourceWindow screen]; - windowRect.origin.y = - [screen frame].size.height - windowRect.size.height - - windowRect.origin.y; - gfx::Rect browserRect(windowRect.origin.x, windowRect.origin.y, - windowRect.size.width, windowRect.size.height); - - NSRect sourceTabRect = [tabView frame]; - NSView* tabStrip = [self tabStripView]; - - // Pushes tabView's frame back inside the tabstrip. - NSSize tabOverflow = - [self overflowFrom:[tabStrip convertRectToBase:sourceTabRect] - to:[tabStrip frame]]; - NSRect tabRect = NSOffsetRect(sourceTabRect, - -tabOverflow.width, -tabOverflow.height); - - // Before detaching the tab, store the pinned state. - bool isPinned = browser_->tabstrip_model()->IsTabPinned(index); - - // Detach it from the source window, which just updates the model without - // deleting the tab contents. This needs to come before creating the new - // Browser because it clears the TabContents' delegate, which gets hooked - // up during creation of the new window. - browser_->tabstrip_model()->DetachTabContentsAt(index); - - // Create the new window with a single tab in its model, the one being - // dragged. - DockInfo dockInfo; - Browser* newBrowser = browser_->tabstrip_model()->delegate()-> - CreateNewStripWithContents(contents, browserRect, dockInfo, false); - - // Propagate the tab pinned state of the new tab (which is the only tab in - // this new window). - newBrowser->tabstrip_model()->SetTabPinned(0, isPinned); - - // Get the new controller by asking the new window for its delegate. - BrowserWindowController* controller = - reinterpret_cast<BrowserWindowController*>( - [newBrowser->window()->GetNativeHandle() delegate]); - DCHECK(controller && [controller isKindOfClass:[TabWindowController class]]); - - // Force the added tab to the right size (remove stretching.) - tabRect.size.height = [TabStripController defaultTabHeight]; - - // And make sure we use the correct frame in the new view. - [[controller tabStripController] setFrameOfSelectedTab:tabRect]; - return controller; -} - -- (void)insertPlaceholderForTab:(TabView*)tab - frame:(NSRect)frame - yStretchiness:(CGFloat)yStretchiness { - [super insertPlaceholderForTab:tab frame:frame yStretchiness:yStretchiness]; - [tabStripController_ insertPlaceholderForTab:tab - frame:frame - yStretchiness:yStretchiness]; -} - -- (void)removePlaceholder { - [super removePlaceholder]; - [tabStripController_ insertPlaceholderForTab:nil - frame:NSZeroRect - yStretchiness:0]; -} - -- (BOOL)tabDraggingAllowed { - return [tabStripController_ tabDraggingAllowed]; -} - -- (BOOL)tabTearingAllowed { - return ![self isFullscreen]; -} - -- (BOOL)windowMovementAllowed { - return ![self isFullscreen]; -} - -- (BOOL)isTabFullyVisible:(TabView*)tab { - return [tabStripController_ isTabFullyVisible:tab]; -} - -- (void)showNewTabButton:(BOOL)show { - [tabStripController_ showNewTabButton:show]; -} - -- (BOOL)isBookmarkBarVisible { - return [bookmarkBarController_ isVisible]; -} - -- (BOOL)isBookmarkBarAnimating { - return [bookmarkBarController_ isAnimationRunning]; -} - -- (void)updateBookmarkBarVisibilityWithAnimation:(BOOL)animate { - [bookmarkBarController_ - updateAndShowNormalBar:[self shouldShowBookmarkBar] - showDetachedBar:[self shouldShowDetachedBookmarkBar] - withAnimation:animate]; -} - -- (BOOL)isDownloadShelfVisible { - return downloadShelfController_ != nil && - [downloadShelfController_ isVisible]; -} - -- (DownloadShelfController*)downloadShelf { - if (!downloadShelfController_.get()) { - downloadShelfController_.reset([[DownloadShelfController alloc] - initWithBrowser:browser_.get() resizeDelegate:self]); - [[[self window] contentView] addSubview:[downloadShelfController_ view]]; - [downloadShelfController_ show:nil]; - } - return downloadShelfController_; -} - -- (void)addFindBar:(FindBarCocoaController*)findBarCocoaController { - // Shouldn't call addFindBar twice. - DCHECK(!findBarCocoaController_.get()); - - // Create a controller for the findbar. - findBarCocoaController_.reset([findBarCocoaController retain]); - NSView *contentView = [[self window] contentView]; - [contentView addSubview:[findBarCocoaController_ view] - positioned:NSWindowAbove - relativeTo:[toolbarController_ view]]; - - // 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. - CGFloat maxY = [self placeBookmarkBarBelowInfoBar] ? - NSMinY([[toolbarController_ view] frame]) : - NSMinY([[bookmarkBarController_ view] frame]); - CGFloat maxWidth = NSWidth([contentView frame]); - [findBarCocoaController_ positionFindBarViewAtMaxY:maxY maxWidth:maxWidth]; -} - -- (NSWindow*)createFullscreenWindow { - return [[[FullscreenWindow alloc] initForScreen:[[self window] screen]] - autorelease]; -} - -- (NSInteger)numberOfTabs { - // count() includes pinned tabs. - return browser_->tabstrip_model()->count(); -} - -- (BOOL)hasLiveTabs { - return !browser_->tabstrip_model()->empty(); -} - -- (NSString*)selectedTabTitle { - TabContents* contents = browser_->GetSelectedTabContents(); - return base::SysUTF16ToNSString(contents->GetTitle()); -} - -- (NSRect)regularWindowFrame { - return [self isFullscreen] ? [savedRegularWindow_ frame] : - [[self window] frame]; -} - -// (Override of |TabWindowController| method.) -- (BOOL)hasTabStrip { - return [self supportsWindowFeature:Browser::FEATURE_TABSTRIP]; -} - -// TabContentsControllerDelegate protocol. -- (void)tabContentsViewFrameWillChange:(TabContentsController*)source - frameRect:(NSRect)frameRect { - TabContents* contents = [source tabContents]; - RenderWidgetHostView* render_widget_host_view = contents ? - contents->GetRenderWidgetHostView() : NULL; - if (!render_widget_host_view) - return; - - gfx::Rect reserved_rect; - - NSWindow* window = [self window]; - if ([window respondsToSelector:@selector(_growBoxRect)]) { - NSView* view = [source view]; - if (view && [view superview]) { - NSRect windowGrowBoxRect = [window _growBoxRect]; - NSRect viewRect = [[view superview] convertRect:frameRect toView:nil]; - NSRect growBoxRect = NSIntersectionRect(windowGrowBoxRect, viewRect); - if (!NSIsEmptyRect(growBoxRect)) { - // Before we return a rect, we need to convert it from window - // coordinates to content area coordinates and flip the coordinate - // system. - // Superview is used here because, first, it's a frame rect, so it is - // specified in the parent's coordinates and, second, view is not - // positioned yet. - growBoxRect = [[view superview] convertRect:growBoxRect fromView:nil]; - growBoxRect.origin.y = - NSHeight(frameRect) - NSHeight(growBoxRect); - growBoxRect = - NSOffsetRect(growBoxRect, -frameRect.origin.x, -frameRect.origin.y); - - reserved_rect = - gfx::Rect(growBoxRect.origin.x, growBoxRect.origin.y, - growBoxRect.size.width, growBoxRect.size.height); - } - } - } - - render_widget_host_view->set_reserved_contents_rect(reserved_rect); -} - -// TabStripControllerDelegate protocol. -- (void)onSelectTabWithContents:(TabContents*)contents { - // Update various elements that are interested in knowing the current - // TabContents. - - // Update all the UI bits. - windowShim_->UpdateTitleBar(); - - [sidebarController_ updateSidebarForTabContents:contents]; - [devToolsController_ updateDevToolsForTabContents:contents]; - - // Update the bookmark bar. - // Must do it after sidebar and devtools update, otherwise bookmark bar might - // call resizeView -> layoutSubviews and cause unnecessary relayout. - // TODO(viettrungluu): perhaps update to not terminate running animations (if - // applicable)? - [self updateBookmarkBarVisibilityWithAnimation:NO]; - - [infoBarContainerController_ changeTabContents:contents]; - - // Update devTools and sidebar contents after size for all views is set. - [sidebarController_ ensureContentsVisible]; - [devToolsController_ ensureContentsVisible]; -} - -- (void)onReplaceTabWithContents:(TabContents*)contents { - // This is only called when instant results are committed. Simply remove the - // preview view; the tab strip controller will reinstall the view as the - // active view. - [previewableContentsController_ hidePreview]; - [self updateBookmarkBarVisibilityWithAnimation:NO]; -} - -- (void)onSelectedTabChange:(TabStripModelObserver::TabChangeType)change { - // Update titles if this is the currently selected tab and if it isn't just - // the loading state which changed. - if (change != TabStripModelObserver::LOADING_ONLY) - windowShim_->UpdateTitleBar(); - - // Update the bookmark bar if this is the currently selected tab and if it - // isn't just the title which changed. This for transitions between the NTP - // (showing its floating bookmark bar) and normal web pages (showing no - // bookmark bar). - // TODO(viettrungluu): perhaps update to not terminate running animations? - if (change != TabStripModelObserver::TITLE_NOT_LOADING) - [self updateBookmarkBarVisibilityWithAnimation:NO]; -} - -- (void)onTabDetachedWithContents:(TabContents*)contents { - [infoBarContainerController_ tabDetachedWithContents:contents]; -} - -- (void)userChangedTheme { - // TODO(dmaclach): Instead of redrawing the whole window, views that care - // about the active window state should be registering for notifications. - [[self window] setViewsNeedDisplay:YES]; -} - -- (ThemeProvider*)themeProvider { - return browser_->profile()->GetThemeProvider(); -} - -- (ThemedWindowStyle)themedWindowStyle { - ThemedWindowStyle style = 0; - if (browser_->profile()->IsOffTheRecord()) - style |= THEMED_INCOGNITO; - - Browser::Type type = browser_->type(); - if (type == Browser::TYPE_POPUP) - style |= THEMED_POPUP; - else if (type == Browser::TYPE_DEVTOOLS) - style |= THEMED_DEVTOOLS; - - return style; -} - -- (NSPoint)themePatternPhase { - // Our patterns want to be drawn from the upper left hand corner of the view. - // Cocoa wants to do it from the lower left of the window. - // - // Rephase our pattern to fit this view. Some other views (Tabs, Toolbar etc.) - // will phase their patterns relative to this so all the views look right. - // - // To line up the background pattern with the pattern in the browser window - // the background pattern for the tabs needs to be moved left by 5 pixels. - const CGFloat kPatternHorizontalOffset = -5; - NSView* tabStripView = [self tabStripView]; - NSRect tabStripViewWindowBounds = [tabStripView bounds]; - NSView* windowChromeView = [[[self window] contentView] superview]; - tabStripViewWindowBounds = - [tabStripView convertRect:tabStripViewWindowBounds - toView:windowChromeView]; - NSPoint phase = NSMakePoint(NSMinX(tabStripViewWindowBounds) - + kPatternHorizontalOffset, - NSMinY(tabStripViewWindowBounds) - + [TabStripController defaultTabHeight]); - return phase; -} - -- (NSPoint)bookmarkBubblePoint { - return [toolbarController_ bookmarkBubblePoint]; -} - -// Show the bookmark bubble (e.g. user just clicked on the STAR). -- (void)showBookmarkBubbleForURL:(const GURL&)url - alreadyBookmarked:(BOOL)alreadyMarked { - if (!bookmarkBubbleController_) { - BookmarkModel* model = browser_->profile()->GetBookmarkModel(); - const BookmarkNode* node = model->GetMostRecentlyAddedNodeForURL(url); - bookmarkBubbleController_ = - [[BookmarkBubbleController alloc] initWithParentWindow:[self window] - model:model - node:node - alreadyBookmarked:alreadyMarked]; - [bookmarkBubbleController_ showWindow:self]; - NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; - [center addObserver:self - selector:@selector(bubbleWindowWillClose:) - name:NSWindowWillCloseNotification - object:[bookmarkBubbleController_ window]]; - } -} - -// Nil out the weak bookmark bubble controller reference. -- (void)bubbleWindowWillClose:(NSNotification*)notification { - DCHECK([notification object] == [bookmarkBubbleController_ window]); - NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; - [center removeObserver:self - name:NSWindowWillCloseNotification - object:[bookmarkBubbleController_ window]]; - bookmarkBubbleController_ = nil; -} - -// Handle the editBookmarkNode: action sent from bookmark bubble controllers. -- (void)editBookmarkNode:(id)sender { - BOOL responds = [sender respondsToSelector:@selector(node)]; - DCHECK(responds); - if (responds) { - const BookmarkNode* node = [sender node]; - if (node) { - // A BookmarkEditorController is a sheet that owns itself, and - // deallocates itself when closed. - [[[BookmarkEditorController alloc] - initWithParentWindow:[self window] - profile:browser_->profile() - parent:node->GetParent() - node:node - configuration:BookmarkEditor::SHOW_TREE] - runAsModalSheet]; - } - } -} - -// If the browser is in incognito mode, install the image view to decorate -// the window at the upper right. Use the same base y coordinate as the -// tab strip. -- (void)installIncognitoBadge { - // Only install if this browser window is OTR and has a tab strip. - if (!browser_->profile()->IsOffTheRecord() || ![self hasTabStrip]) - return; - - // Install the image into the badge view and size the view appropriately. - // Hide it for now; positioning and showing will be done by the layout code. - NSImage* image = nsimage_cache::ImageNamed(@"otr_icon.pdf"); - incognitoBadge_.reset([[IncognitoImageView alloc] init]); - [incognitoBadge_ setImage:image]; - [incognitoBadge_ setFrameSize:[image size]]; - [incognitoBadge_ setAutoresizingMask:NSViewMinXMargin | NSViewMinYMargin]; - [incognitoBadge_ setHidden:YES]; - - // Give it a shadow. - scoped_nsobject<NSShadow> shadow([[NSShadow alloc] init]); - [shadow.get() setShadowColor:[NSColor colorWithCalibratedWhite:0.0 - alpha:0.5]]; - [shadow.get() setShadowOffset:NSMakeSize(0, -1)]; - [shadow setShadowBlurRadius:2.0]; - [incognitoBadge_ setShadow:shadow]; - - // Install the view. - [[[[self window] contentView] superview] addSubview:incognitoBadge_]; -} - -// Documented in 10.6+, but present starting in 10.5. Called when we get a -// three-finger swipe. -- (void)swipeWithEvent:(NSEvent*)event { - // Map forwards and backwards to history; left is positive, right is negative. - unsigned int command = 0; - if ([event deltaX] > 0.5) { - command = IDC_BACK; - } else if ([event deltaX] < -0.5) { - command = IDC_FORWARD; - } else if ([event deltaY] > 0.5) { - // TODO(pinkerton): figure out page-up, http://crbug.com/16305 - } else if ([event deltaY] < -0.5) { - // TODO(pinkerton): figure out page-down, http://crbug.com/16305 - browser_->ExecuteCommand(IDC_TABPOSE); - } - - // Ensure the command is valid first (ExecuteCommand() won't do that) and - // then make it so. - if (browser_->command_updater()->IsCommandEnabled(command)) - browser_->ExecuteCommandWithDisposition(command, - event_utils::WindowOpenDispositionFromNSEvent(event)); -} - -// Documented in 10.6+, but present starting in 10.5. Called repeatedly during -// a pinch gesture, with incremental change values. -- (void)magnifyWithEvent:(NSEvent*)event { - // The deltaZ difference necessary to trigger a zoom action. Derived from - // experimentation to find a value that feels reasonable. - const float kZoomStepValue = 150; - - // Find the (absolute) thresholds on either side of the current zoom factor, - // then convert those to actual numbers to trigger a zoom in or out. - // This logic deliberately makes the range around the starting zoom value for - // the gesture twice as large as the other ranges (i.e., the notches are at - // ..., -3*step, -2*step, -step, step, 2*step, 3*step, ... but not at 0) - // so that it's easier to get back to your starting point than it is to - // overshoot. - float nextStep = (abs(currentZoomStepDelta_) + 1) * kZoomStepValue; - float backStep = abs(currentZoomStepDelta_) * kZoomStepValue; - float zoomInThreshold = (currentZoomStepDelta_ >= 0) ? nextStep : -backStep; - float zoomOutThreshold = (currentZoomStepDelta_ <= 0) ? -nextStep : backStep; - - unsigned int command = 0; - totalMagnifyGestureAmount_ += [event deltaZ]; - if (totalMagnifyGestureAmount_ > zoomInThreshold) { - command = IDC_ZOOM_PLUS; - } else if (totalMagnifyGestureAmount_ < zoomOutThreshold) { - command = IDC_ZOOM_MINUS; - } - - if (command && browser_->command_updater()->IsCommandEnabled(command)) { - currentZoomStepDelta_ += (command == IDC_ZOOM_PLUS) ? 1 : -1; - browser_->ExecuteCommandWithDisposition(command, - event_utils::WindowOpenDispositionFromNSEvent(event)); - } -} - -// Documented in 10.6+, but present starting in 10.5. Called at the beginning -// of a gesture. -- (void)beginGestureWithEvent:(NSEvent*)event { - totalMagnifyGestureAmount_ = 0; - currentZoomStepDelta_ = 0; -} - -// Delegate method called when window is resized. -- (void)windowDidResize:(NSNotification*)notification { - // Resize (and possibly move) the status bubble. Note that we may get called - // when the status bubble does not exist. - if (statusBubble_) { - statusBubble_->UpdateSizeAndPosition(); - } - - // Let the selected RenderWidgetHostView know, so that it can tell plugins. - if (TabContents* contents = browser_->GetSelectedTabContents()) { - if (RenderWidgetHostView* rwhv = contents->GetRenderWidgetHostView()) - rwhv->WindowFrameChanged(); - } -} - -// Handle the openLearnMoreAboutCrashLink: action from SadTabController when -// "Learn more" link in "Aw snap" page (i.e. crash page or sad tab) is -// clicked. Decoupling the action from its target makes unitestting possible. -- (void)openLearnMoreAboutCrashLink:(id)sender { - if ([sender isKindOfClass:[SadTabController class]]) { - SadTabController* sad_tab = static_cast<SadTabController*>(sender); - TabContents* tab_contents = [sad_tab tabContents]; - if (tab_contents) { - GURL helpUrl = - google_util::AppendGoogleLocaleParam(GURL(chrome::kCrashReasonURL)); - tab_contents->OpenURL(helpUrl, GURL(), CURRENT_TAB, PageTransition::LINK); - } - } -} - -// Delegate method called when window did move. (See below for why we don't use -// |-windowWillMove:|, which is called less frequently than |-windowDidMove| -// instead.) -- (void)windowDidMove:(NSNotification*)notification { - NSWindow* window = [self window]; - NSRect windowFrame = [window frame]; - NSRect workarea = [[window screen] visibleFrame]; - - // We reset the window growth state whenever the window is moved out of the - // work area or away (up or down) from the bottom or top of the work area. - // Unfortunately, Cocoa sends |-windowWillMove:| too frequently (including - // when clicking on the title bar to activate), and of course - // |-windowWillMove| is called too early for us to apply our heuristic. (The - // heuristic we use for detecting window movement is that if |windowTopGrowth_ - // > 0|, then we should be at the bottom of the work area -- if we're not, - // we've moved. Similarly for the other side.) - if (!NSContainsRect(workarea, windowFrame) || - (windowTopGrowth_ > 0 && NSMinY(windowFrame) != NSMinY(workarea)) || - (windowBottomGrowth_ > 0 && NSMaxY(windowFrame) != NSMaxY(workarea))) - [self resetWindowGrowthState]; - - // Let the selected RenderWidgetHostView know, so that it can tell plugins. - if (TabContents* contents = browser_->GetSelectedTabContents()) { - if (RenderWidgetHostView* rwhv = contents->GetRenderWidgetHostView()) - rwhv->WindowFrameChanged(); - } -} - -// Delegate method called when window will be resized; not called for -// |-setFrame:display:|. -- (NSSize)windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize { - [self resetWindowGrowthState]; - return frameSize; -} - -// Delegate method: see |NSWindowDelegate| protocol. -- (id)windowWillReturnFieldEditor:(NSWindow*)sender toObject:(id)obj { - // Ask the toolbar controller if it wants to return a custom field editor - // for the specific object. - return [toolbarController_ customFieldEditorForObject:obj]; -} - -// (Needed for |BookmarkBarControllerDelegate| protocol.) -- (void)bookmarkBar:(BookmarkBarController*)controller - didChangeFromState:(bookmarks::VisualState)oldState - toState:(bookmarks::VisualState)newState { - [toolbarController_ - setDividerOpacity:[bookmarkBarController_ toolbarDividerOpacity]]; - [self adjustToolbarAndBookmarkBarForCompression: - [controller getDesiredToolbarHeightCompression]]; -} - -// (Needed for |BookmarkBarControllerDelegate| protocol.) -- (void)bookmarkBar:(BookmarkBarController*)controller -willAnimateFromState:(bookmarks::VisualState)oldState - toState:(bookmarks::VisualState)newState { - [toolbarController_ - setDividerOpacity:[bookmarkBarController_ toolbarDividerOpacity]]; - [self adjustToolbarAndBookmarkBarForCompression: - [controller getDesiredToolbarHeightCompression]]; -} - -// (Private/TestingAPI) -- (void)resetWindowGrowthState { - windowTopGrowth_ = 0; - windowBottomGrowth_ = 0; - isShrinkingFromZoomed_ = NO; -} - -- (NSSize)overflowFrom:(NSRect)source - to:(NSRect)target { - // If |source|'s boundary is outside of |target|'s, set its distance - // to |x|. Note that |source| can overflow to both side, but we - // have nothing to do for such case. - CGFloat x = 0; - if (NSMaxX(target) < NSMaxX(source)) // |source| overflows to right - x = NSMaxX(source) - NSMaxX(target); - else if (NSMinX(source) < NSMinX(target)) // |source| overflows to left - x = NSMinX(source) - NSMinX(target); - - // Same as |x| above. - CGFloat y = 0; - if (NSMaxY(target) < NSMaxY(source)) - y = NSMaxY(source) - NSMaxY(target); - else if (NSMinY(source) < NSMinY(target)) - y = NSMinY(source) - NSMinY(target); - - return NSMakeSize(x, y); -} - -// Override to swap in the correct tab strip controller based on the new -// tab strip mode. -- (void)toggleTabStripDisplayMode { - [super toggleTabStripDisplayMode]; - [self createTabStripController]; -} - -- (BOOL)useVerticalTabs { - return browser_->tabstrip_model()->delegate()->UseVerticalTabs(); -} - -- (void)showInstant:(TabContents*)previewContents { - [previewableContentsController_ showPreview:previewContents]; - [self updateBookmarkBarVisibilityWithAnimation:NO]; -} - -- (void)hideInstant { - // TODO(rohitrao): Revisit whether or not this method should be called when - // instant isn't showing. - if (![previewableContentsController_ isShowingPreview]) - return; - - [previewableContentsController_ hidePreview]; - [self updateBookmarkBarVisibilityWithAnimation:NO]; -} - -- (NSRect)instantFrame { - // The view's bounds are in its own coordinate system. Convert that to the - // window base coordinate system, then translate it into the screen's - // coordinate system. - NSView* view = [previewableContentsController_ view]; - if (!view) - return NSZeroRect; - - NSRect frame = [view convertRect:[view bounds] toView:nil]; - NSPoint originInScreenCoords = - [[view window] convertBaseToScreen:frame.origin]; - frame.origin = originInScreenCoords; - return frame; -} - -- (void)sheetDidEnd:(NSWindow*)sheet - returnCode:(NSInteger)code - context:(void*)context { - [sheet orderOut:self]; -} - -@end // @implementation BrowserWindowController - - -@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 (fullscreen == [self isFullscreen]) - return; - - if (![self supportsFullscreen]) - return; - - // Fade to black. - const CGDisplayReservationInterval kFadeDurationSeconds = 0.6; - Boolean didFadeOut = NO; - CGDisplayFadeReservationToken token; - if (CGAcquireDisplayFadeReservation(kFadeDurationSeconds, &token) - == kCGErrorSuccess) { - didFadeOut = YES; - CGDisplayFade(token, kFadeDurationSeconds / 2, kCGDisplayBlendNormal, - kCGDisplayBlendSolidColor, 0.0, 0.0, 0.0, /*synchronous=*/true); - } - - // Close the bookmark bubble, if it's open. We use |-ok:| instead of - // |-cancel:| or |-close| because that matches the behavior when the bubble - // loses key status. - [bookmarkBubbleController_ ok:self]; - - // Save the current first responder so we can restore after views are moved. - NSWindow* window = [self window]; - scoped_nsobject<FocusTracker> focusTracker( - [[FocusTracker alloc] initWithWindow:window]); - BOOL showDropdown = [self floatingBarHasFocus]; - - // While we move views (and focus) around, disable any bar visibility changes. - [self disableBarVisibilityUpdates]; - - // 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(); - } - - // Destroy the tab strip's sheet controller. We will recreate it in the new - // window when needed. - [tabStripController_ destroySheetController]; - - // Retain the tab strip view while we remove it from its superview. - scoped_nsobject<NSView> tabStripView; - if ([self hasTabStrip] && ![self useVerticalTabs]) { - 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; - - CGSWorkspaceID workspace; - if ([window cr_workspace:&workspace]) { - [destWindow cr_moveToWorkspace:workspace]; - } - } - 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]; - - // Move the incognito badge if present. - if (incognitoBadge_.get()) { - [incognitoBadge_ removeFromSuperview]; - [incognitoBadge_ setHidden:YES]; // Will be shown in layout. - [[[destWindow contentView] superview] addSubview:incognitoBadge_]; - } - - // Add the tab strip after setting the content view and moving the incognito - // badge (if any), so that the tab strip will be on top (in the z-order). - if ([self hasTabStrip] && ![self useVerticalTabs]) - [[[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]; - } - - // Move the status bubble over, if we have one. - if (statusBubble_) - statusBubble_->SwitchParentWindow(destWindow); - - // Move the title over. - [destWindow setTitle:[window title]]; - - // The window needs to be onscreen before we can set its first responder. - [destWindow makeKeyAndOrderFront:self]; - [focusTracker restoreFocusInWindow:destWindow]; - [window orderOut:self]; - - // We're done moving focus, so re-enable bar visibility changes. - [self enableBarVisibilityUpdates]; - - // Fade back in. - if (didFadeOut) { - CGDisplayFade(token, kFadeDurationSeconds / 2, kCGDisplayBlendSolidColor, - kCGDisplayBlendNormal, 0.0, 0.0, 0.0, /*synchronous=*/false); - CGReleaseDisplayFadeReservation(token); - } -} - -- (BOOL)isFullscreen { - return fullscreenController_.get() && [fullscreenController_ isFullscreen]; -} - -- (void)resizeFullscreenWindow { - DCHECK([self isFullscreen]); - if (![self isFullscreen]) - return; - - NSWindow* window = [self window]; - [window setFrame:[[window screen] frame] display:YES]; - [self layoutSubviews]; -} - -- (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]; - - // If enabled, show the overlay if necessary (and if in fullscreen mode). - if (barVisibilityUpdatesEnabled_) { - [fullscreenController_ ensureOverlayShownWithAnimation:animate - delay:delay]; - } - } -} - -- (void)releaseBarVisibilityForOwner:(id)owner - withAnimation:(BOOL)animate - delay:(BOOL)delay { - if ([self isBarVisibilityLockedForOwner:owner]) { - [barVisibilityLocks_ removeObject:owner]; - - // If enabled, hide the overlay if necessary (and if in fullscreen mode). - if (barVisibilityUpdatesEnabled_ && - ![barVisibilityLocks_ count]) { - [fullscreenController_ ensureOverlayHiddenWithAnimation:animate - delay:delay]; - } - } -} - -- (BOOL)floatingBarHasFocus { - NSResponder* focused = [[self window] firstResponder]; - return [focused isKindOfClass:[AutocompleteTextFieldEditor class]]; -} - -- (void)openTabpose { - NSUInteger modifierFlags = [[NSApp currentEvent] modifierFlags]; - BOOL slomo = (modifierFlags & NSShiftKeyMask) != 0; - - // Cover info bars, inspector window, and detached bookmark bar on NTP. - // Do not cover download shelf. - NSRect activeArea = [[self tabContentArea] frame]; - activeArea.size.height += - NSHeight([[infoBarContainerController_ view] frame]); - if ([self isBookmarkBarVisible] && [self placeBookmarkBarBelowInfoBar]) { - NSView* bookmarkBarView = [bookmarkBarController_ view]; - activeArea.size.height += NSHeight([bookmarkBarView frame]); - } - - [TabposeWindow openTabposeFor:[self window] - rect:activeArea - slomo:slomo - tabStripModel:browser_->tabstrip_model()]; -} - -@end // @implementation BrowserWindowController(Fullscreen) - - -@implementation BrowserWindowController(WindowType) - -- (BOOL)supportsWindowFeature:(int)feature { - return browser_->SupportsWindowFeature( - static_cast<Browser::WindowFeature>(feature)); -} - -- (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; -} - -@end // @implementation BrowserWindowController(WindowType) |