Home | History | Annotate | Download | only in cocoa
      1 // Copyright 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #import "chrome/browser/ui/cocoa/browser_window_controller.h"
      6 
      7 #include <cmath>
      8 #include <numeric>
      9 
     10 #include "base/command_line.h"
     11 #include "base/mac/bundle_locations.h"
     12 #include "base/mac/mac_util.h"
     13 #include "base/strings/sys_string_conversions.h"
     14 #include "base/strings/utf_string_conversions.h"
     15 #include "chrome/app/chrome_command_ids.h"  // IDC_*
     16 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
     17 #include "chrome/browser/browser_process.h"
     18 #include "chrome/browser/devtools/devtools_window.h"
     19 #include "chrome/browser/fullscreen.h"
     20 #include "chrome/browser/profiles/avatar_menu_model.h"
     21 #include "chrome/browser/profiles/profile.h"
     22 #include "chrome/browser/profiles/profile_info_cache.h"
     23 #include "chrome/browser/profiles/profile_manager.h"
     24 #include "chrome/browser/signin/signin_ui_util.h"
     25 #include "chrome/browser/themes/theme_service.h"
     26 #include "chrome/browser/themes/theme_service_factory.h"
     27 #include "chrome/browser/ui/bookmarks/bookmark_editor.h"
     28 #include "chrome/browser/ui/browser.h"
     29 #include "chrome/browser/ui/browser_command_controller.h"
     30 #include "chrome/browser/ui/browser_commands.h"
     31 #include "chrome/browser/ui/browser_instant_controller.h"
     32 #include "chrome/browser/ui/browser_list.h"
     33 #include "chrome/browser/ui/browser_window_state.h"
     34 #import "chrome/browser/ui/cocoa/background_gradient_view.h"
     35 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller.h"
     36 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_editor_controller.h"
     37 #import "chrome/browser/ui/cocoa/browser/avatar_button_controller.h"
     38 #import "chrome/browser/ui/cocoa/browser_window_cocoa.h"
     39 #import "chrome/browser/ui/cocoa/browser_window_controller_private.h"
     40 #import "chrome/browser/ui/cocoa/browser_window_utils.h"
     41 #import "chrome/browser/ui/cocoa/dev_tools_controller.h"
     42 #import "chrome/browser/ui/cocoa/download/download_shelf_controller.h"
     43 #include "chrome/browser/ui/cocoa/extensions/extension_keybinding_registry_cocoa.h"
     44 #import "chrome/browser/ui/cocoa/fast_resize_view.h"
     45 #import "chrome/browser/ui/cocoa/find_bar/find_bar_bridge.h"
     46 #import "chrome/browser/ui/cocoa/find_bar/find_bar_cocoa_controller.h"
     47 #import "chrome/browser/ui/cocoa/framed_browser_window.h"
     48 #import "chrome/browser/ui/cocoa/fullscreen_window.h"
     49 #import "chrome/browser/ui/cocoa/infobars/infobar_container_controller.h"
     50 #import "chrome/browser/ui/cocoa/location_bar/autocomplete_text_field_editor.h"
     51 #import "chrome/browser/ui/cocoa/nsview_additions.h"
     52 #import "chrome/browser/ui/cocoa/presentation_mode_controller.h"
     53 #import "chrome/browser/ui/cocoa/status_bubble_mac.h"
     54 #import "chrome/browser/ui/cocoa/tab_contents/overlayable_contents_controller.h"
     55 #import "chrome/browser/ui/cocoa/tab_contents/sad_tab_controller.h"
     56 #import "chrome/browser/ui/cocoa/tab_contents/tab_contents_controller.h"
     57 #import "chrome/browser/ui/cocoa/tabpose_window.h"
     58 #import "chrome/browser/ui/cocoa/tabs/tab_strip_controller.h"
     59 #import "chrome/browser/ui/cocoa/tabs/tab_strip_view.h"
     60 #import "chrome/browser/ui/cocoa/tabs/tab_view.h"
     61 #import "chrome/browser/ui/cocoa/toolbar/toolbar_controller.h"
     62 #include "chrome/browser/ui/fullscreen/fullscreen_controller.h"
     63 #include "chrome/browser/ui/omnibox/location_bar.h"
     64 #include "chrome/browser/ui/tabs/dock_info.h"
     65 #include "chrome/browser/ui/tabs/tab_strip_model.h"
     66 #include "chrome/browser/ui/tabs/tab_strip_model_delegate.h"
     67 #include "chrome/browser/ui/toolbar/encoding_menu_controller.h"
     68 #include "chrome/browser/ui/window_sizer/window_sizer.h"
     69 #include "chrome/common/chrome_switches.h"
     70 #include "chrome/common/url_constants.h"
     71 #include "components/web_modal/web_contents_modal_dialog_manager.h"
     72 #include "content/public/browser/render_view_host.h"
     73 #include "content/public/browser/render_widget_host_view.h"
     74 #include "content/public/browser/web_contents.h"
     75 #include "content/public/browser/web_contents_view.h"
     76 #include "content/public/common/content_switches.h"
     77 #include "grit/chromium_strings.h"
     78 #include "grit/generated_resources.h"
     79 #include "grit/locale_settings.h"
     80 #import "ui/base/cocoa/cocoa_event_utils.h"
     81 #include "ui/base/l10n/l10n_util.h"
     82 #include "ui/base/l10n/l10n_util_mac.h"
     83 #include "ui/gfx/mac/scoped_ns_disable_screen_updates.h"
     84 
     85 using l10n_util::GetStringUTF16;
     86 using l10n_util::GetNSStringWithFixup;
     87 using l10n_util::GetNSStringFWithFixup;
     88 
     89 // ORGANIZATION: This is a big file. It is (in principle) organized as follows
     90 // (in order):
     91 // 1. Interfaces. Very short, one-time-use classes may include an implementation
     92 //    immediately after their interface.
     93 // 2. The general implementation section, ordered as follows:
     94 //      i. Public methods and overrides.
     95 //     ii. Overrides/implementations of undocumented methods.
     96 //    iii. Delegate methods for various protocols, formal and informal, to which
     97 //        |BrowserWindowController| conforms.
     98 // 3. (temporary) Implementation sections for various categories.
     99 //
    100 // Private methods are defined and implemented separately in
    101 // browser_window_controller_private.{h,mm}.
    102 //
    103 // Not all of the above guidelines are followed and more (re-)organization is
    104 // needed. BUT PLEASE TRY TO KEEP THIS FILE ORGANIZED. I'd rather re-organize as
    105 // little as possible, since doing so messes up the file's history.
    106 //
    107 // TODO(viettrungluu): [crbug.com/35543] on-going re-organization, splitting
    108 // things into multiple files -- the plan is as follows:
    109 // - in general, everything stays in browser_window_controller.h, but is split
    110 //   off into categories (see below)
    111 // - core stuff stays in browser_window_controller.mm
    112 // - ... overrides also stay (without going into a category, in particular)
    113 // - private stuff which everyone needs goes into
    114 //   browser_window_controller_private.{h,mm}; if no one else needs them, they
    115 //   can go in individual files (see below)
    116 // - area/task-specific stuff go in browser_window_controller_<area>.mm
    117 // - ... in categories called "(<Area>)" or "(<PrivateArea>)"
    118 // Plan of action:
    119 // - first re-organize into categories
    120 // - then split into files
    121 
    122 // Notes on self-inflicted (not user-inflicted) window resizing and moving:
    123 //
    124 // When the bookmark bar goes from hidden to shown (on a non-NTP) page, or when
    125 // the download shelf goes from hidden to shown, we grow the window downwards in
    126 // order to maintain a constant content area size. When either goes from shown
    127 // to hidden, we consequently shrink the window from the bottom, also to keep
    128 // the content area size constant. To keep things simple, if the window is not
    129 // entirely on-screen, we don't grow/shrink the window.
    130 //
    131 // The complications come in when there isn't enough room (on screen) below the
    132 // window to accomodate the growth. In this case, we grow the window first
    133 // downwards, and then upwards. So, when it comes to shrinking, we do the
    134 // opposite: shrink from the top by the amount by which we grew at the top, and
    135 // then from the bottom -- unless the user moved/resized/zoomed the window, in
    136 // which case we "reset state" and just shrink from the bottom.
    137 //
    138 // A further complication arises due to the way in which "zoom" ("maximize")
    139 // works on Mac OS X. Basically, for our purposes, a window is "zoomed" whenever
    140 // it occupies the full available vertical space. (Note that the green zoom
    141 // button does not track zoom/unzoomed state per se, but basically relies on
    142 // this heuristic.) We don't, in general, want to shrink the window if the
    143 // window is zoomed (scenario: window is zoomed, download shelf opens -- which
    144 // doesn't cause window growth, download shelf closes -- shouldn't cause the
    145 // window to become unzoomed!). However, if we grew the window
    146 // (upwards/downwards) to become zoomed in the first place, we *should* shrink
    147 // the window by the amounts by which we grew (scenario: window occupies *most*
    148 // of vertical space, download shelf opens causing growth so that window
    149 // occupies all of vertical space -- i.e., window is effectively zoomed,
    150 // download shelf closes -- should return the window to its previous state).
    151 //
    152 // A major complication is caused by the way grows/shrinks are handled and
    153 // animated. Basically, the BWC doesn't see the global picture, but it sees
    154 // grows and shrinks in small increments (as dictated by the animation). Thus
    155 // window growth/shrinkage (at the top/bottom) have to be tracked incrementally.
    156 // Allowing shrinking from the zoomed state also requires tracking: We check on
    157 // any shrink whether we're both zoomed and have previously grown -- if so, we
    158 // set a flag, and constrain any resize by the allowed amounts. On further
    159 // shrinks, we check the flag (since the size/position of the window will no
    160 // longer indicate that the window is shrinking from an apparent zoomed state)
    161 // and if it's set we continue to constrain the resize.
    162 
    163 using content::OpenURLParams;
    164 using content::Referrer;
    165 using content::RenderWidgetHostView;
    166 using content::WebContents;
    167 using web_modal::WebContentsModalDialogManager;
    168 
    169 @interface NSWindow (NSPrivateApis)
    170 // Note: These functions are private, use -[NSObject respondsToSelector:]
    171 // before calling them.
    172 
    173 - (void)setBottomCornerRounded:(BOOL)rounded;
    174 
    175 - (NSRect)_growBoxRect;
    176 
    177 @end
    178 
    179 // Replicate specific 10.7 SDK declarations for building with prior SDKs.
    180 #if !defined(MAC_OS_X_VERSION_10_7) || \
    181     MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
    182 
    183 enum {
    184   NSWindowAnimationBehaviorDefault = 0,
    185   NSWindowAnimationBehaviorNone = 2,
    186   NSWindowAnimationBehaviorDocumentWindow = 3,
    187   NSWindowAnimationBehaviorUtilityWindow = 4,
    188   NSWindowAnimationBehaviorAlertPanel = 5
    189 };
    190 typedef NSInteger NSWindowAnimationBehavior;
    191 
    192 enum {
    193   NSWindowCollectionBehaviorFullScreenPrimary = 1 << 7,
    194   NSWindowCollectionBehaviorFullScreenAuxiliary = 1 << 8
    195 };
    196 
    197 enum {
    198   NSFullScreenWindowMask = 1 << 14
    199 };
    200 
    201 @interface NSWindow (LionSDKDeclarations)
    202 - (void)setRestorable:(BOOL)flag;
    203 - (void)setAnimationBehavior:(NSWindowAnimationBehavior)newAnimationBehavior;
    204 @end
    205 
    206 #endif  // MAC_OS_X_VERSION_10_7
    207 
    208 @implementation BrowserWindowController
    209 
    210 + (BrowserWindowController*)browserWindowControllerForWindow:(NSWindow*)window {
    211   while (window) {
    212     id controller = [window windowController];
    213     if ([controller isKindOfClass:[BrowserWindowController class]])
    214       return (BrowserWindowController*)controller;
    215     window = [window parentWindow];
    216   }
    217   return nil;
    218 }
    219 
    220 + (BrowserWindowController*)browserWindowControllerForView:(NSView*)view {
    221   NSWindow* window = [view window];
    222   return [BrowserWindowController browserWindowControllerForWindow:window];
    223 }
    224 
    225 + (void)updateSigninItem:(id)signinItem
    226               shouldShow:(BOOL)showSigninMenuItem
    227           currentProfile:(Profile*)profile {
    228   DCHECK([signinItem isKindOfClass:[NSMenuItem class]]);
    229   NSMenuItem* signinMenuItem = static_cast<NSMenuItem*>(signinItem);
    230 
    231   // Look for a separator immediately after the menu item so it can be hidden
    232   // or shown appropriately along with the signin menu item.
    233   NSMenuItem* followingSeparator = nil;
    234   NSMenu* menu = [signinItem menu];
    235   if (menu) {
    236     NSInteger signinItemIndex = [menu indexOfItem:signinMenuItem];
    237     DCHECK_NE(signinItemIndex, -1);
    238     if ((signinItemIndex + 1) < [menu numberOfItems]) {
    239       NSMenuItem* menuItem = [menu itemAtIndex:(signinItemIndex + 1)];
    240       if ([menuItem isSeparatorItem]) {
    241         followingSeparator = menuItem;
    242       }
    243     }
    244   }
    245 
    246   string16 label = signin_ui_util::GetSigninMenuLabel(profile);
    247   [signinMenuItem setTitle:l10n_util::FixUpWindowsStyleLabel(label)];
    248   [signinMenuItem setHidden:!showSigninMenuItem];
    249   [followingSeparator setHidden:!showSigninMenuItem];
    250 }
    251 
    252 // Load the browser window nib and do any Cocoa-specific initialization.
    253 // Takes ownership of |browser|. Note that the nib also sets this controller
    254 // up as the window's delegate.
    255 - (id)initWithBrowser:(Browser*)browser {
    256   return [self initWithBrowser:browser takeOwnership:YES];
    257 }
    258 
    259 // Private(TestingAPI) init routine with testing options.
    260 - (id)initWithBrowser:(Browser*)browser takeOwnership:(BOOL)ownIt {
    261   bool hasTabStrip = browser->SupportsWindowFeature(Browser::FEATURE_TABSTRIP);
    262   if ((self = [super initTabWindowControllerWithTabStrip:hasTabStrip])) {
    263     DCHECK(browser);
    264     initializing_ = YES;
    265     browser_.reset(browser);
    266     ownsBrowser_ = ownIt;
    267     NSWindow* window = [self window];
    268     windowShim_.reset(new BrowserWindowCocoa(browser, self));
    269 
    270     // Eagerly enable core animation if requested.
    271     if ([self coreAnimationStatus] ==
    272             browser_window_controller::kCoreAnimationEnabledAlways) {
    273       [[[self window] contentView] setWantsLayer:YES];
    274       [[self tabStripView] setWantsLayer:YES];
    275     }
    276 
    277     // Set different minimum sizes on tabbed windows vs non-tabbed, e.g. popups.
    278     // This has to happen before -enforceMinWindowSize: is called further down.
    279     NSSize minSize = [self isTabbedWindow] ?
    280       NSMakeSize(400, 272) : NSMakeSize(100, 122);
    281     [[self window] setMinSize:minSize];
    282 
    283     // Create the bar visibility lock set; 10 is arbitrary, but should hopefully
    284     // be big enough to hold all locks that'll ever be needed.
    285     barVisibilityLocks_.reset([[NSMutableSet setWithCapacity:10] retain]);
    286 
    287     // Set the window to not have rounded corners, which prevents the resize
    288     // control from being inset slightly and looking ugly. Only bother to do
    289     // this on Snow Leopard; on Lion and later all windows have rounded bottom
    290     // corners, and this won't work anyway.
    291     if (base::mac::IsOSSnowLeopard() &&
    292         [window respondsToSelector:@selector(setBottomCornerRounded:)])
    293       [window setBottomCornerRounded:NO];
    294 
    295     // Lion will attempt to automagically save and restore the UI. This
    296     // functionality appears to be leaky (or at least interacts badly with our
    297     // architecture) and thus BrowserWindowController never gets released. This
    298     // prevents the browser from being able to quit <http://crbug.com/79113>.
    299     if ([window respondsToSelector:@selector(setRestorable:)])
    300       [window setRestorable:NO];
    301 
    302     // Get the windows to swish in on Lion.
    303     if ([window respondsToSelector:@selector(setAnimationBehavior:)])
    304       [window setAnimationBehavior:NSWindowAnimationBehaviorDocumentWindow];
    305 
    306     // Get the most appropriate size for the window, then enforce the
    307     // minimum width and height. The window shim will handle flipping
    308     // the coordinates for us so we can use it to save some code.
    309     // Note that this may leave a significant portion of the window
    310     // offscreen, but there will always be enough window onscreen to
    311     // drag the whole window back into view.
    312     ui::WindowShowState show_state = ui::SHOW_STATE_DEFAULT;
    313     gfx::Rect desiredContentRect;
    314     chrome::GetSavedWindowBoundsAndShowState(browser_.get(),
    315                                              &desiredContentRect,
    316                                              &show_state);
    317     gfx::Rect windowRect = desiredContentRect;
    318     windowRect = [self enforceMinWindowSize:windowRect];
    319 
    320     // When we are given x/y coordinates of 0 on a created popup window, assume
    321     // none were given by the window.open() command.
    322     if (browser_->is_type_popup() &&
    323         windowRect.x() == 0 && windowRect.y() == 0) {
    324       gfx::Size size = windowRect.size();
    325       windowRect.set_origin(
    326           WindowSizer::GetDefaultPopupOrigin(size,
    327                                              browser_->host_desktop_type()));
    328     }
    329 
    330     // Size and position the window.  Note that it is not yet onscreen.  Popup
    331     // windows may get resized later on in this function, once the actual size
    332     // of the toolbar/tabstrip is known.
    333     windowShim_->SetBounds(windowRect);
    334 
    335     // Puts the incognito badge on the window frame, if necessary.
    336     [self installAvatar];
    337 
    338     // Create a sub-controller for the docked devTools and add its view to the
    339     // hierarchy.
    340     devToolsController_.reset([[DevToolsController alloc] init]);
    341     [[devToolsController_ view] setFrame:[[self tabContentArea] bounds]];
    342     [[self tabContentArea] addSubview:[devToolsController_ view]];
    343 
    344     // Create the overlayable contents controller.  This provides the switch
    345     // view that TabStripController needs.
    346     overlayableContentsController_.reset(
    347         [[OverlayableContentsController alloc] initWithBrowser:browser]);
    348     [[overlayableContentsController_ view]
    349         setFrame:[[devToolsController_ view] bounds]];
    350     [[devToolsController_ view]
    351         addSubview:[overlayableContentsController_ view]];
    352 
    353     // Create a controller for the tab strip, giving it the model object for
    354     // this window's Browser and the tab strip view. The controller will handle
    355     // registering for the appropriate tab notifications from the back-end and
    356     // managing the creation of new tabs.
    357     [self createTabStripController];
    358 
    359     // Create a controller for the toolbar, giving it the toolbar model object
    360     // and the toolbar view from the nib. The controller will handle
    361     // registering for the appropriate command state changes from the back-end.
    362     // Adds the toolbar to the content area.
    363     toolbarController_.reset([[ToolbarController alloc]
    364               initWithModel:browser->toolbar_model()
    365                    commands:browser->command_controller()->command_updater()
    366                     profile:browser->profile()
    367                     browser:browser
    368              resizeDelegate:self]);
    369     [toolbarController_ setHasToolbar:[self hasToolbar]
    370                        hasLocationBar:[self hasLocationBar]];
    371 
    372     // Create a sub-controller for the bookmark bar.
    373     bookmarkBarController_.reset(
    374         [[BookmarkBarController alloc]
    375             initWithBrowser:browser_.get()
    376                initialWidth:NSWidth([[[self window] contentView] frame])
    377                    delegate:self
    378              resizeDelegate:self]);
    379     [bookmarkBarController_ setBookmarkBarEnabled:[self supportsBookmarkBar]];
    380 
    381     // Create the infobar container view, so we can pass it to the
    382     // ToolbarController.
    383     infoBarContainerController_.reset(
    384         [[InfoBarContainerController alloc] initWithResizeDelegate:self]);
    385     [self updateInfoBarTipVisibility];
    386 
    387     // We don't want to try and show the bar before it gets placed in its parent
    388     // view, so this step shoudn't be inside the bookmark bar controller's
    389     // |-awakeFromNib|.
    390     windowShim_->BookmarkBarStateChanged(
    391         BookmarkBar::DONT_ANIMATE_STATE_CHANGE);
    392 
    393     // Allow bar visibility to be changed.
    394     [self enableBarVisibilityUpdates];
    395 
    396     // Force a relayout of all the various bars.
    397     [self layoutSubviews];
    398 
    399     // Set the window to participate in Lion Fullscreen mode.  Setting this flag
    400     // has no effect on Snow Leopard or earlier.  Panels can share a fullscreen
    401     // space with a tabbed window, but they can not be primary fullscreen
    402     // windows.  Do this after |-layoutSubviews| so that the fullscreen button
    403     // can be adjusted in FramedBrowserWindow.
    404     NSUInteger collectionBehavior = [window collectionBehavior];
    405     collectionBehavior |=
    406        browser_->type() == Browser::TYPE_TABBED ||
    407            browser_->type() == Browser::TYPE_POPUP ?
    408                NSWindowCollectionBehaviorFullScreenPrimary :
    409                NSWindowCollectionBehaviorFullScreenAuxiliary;
    410     [window setCollectionBehavior:collectionBehavior];
    411 
    412     // For a popup window, |desiredContentRect| contains the desired height of
    413     // the content, not of the whole window.  Now that all the views are laid
    414     // out, measure the current content area size and grow if needed.  The
    415     // window has not been placed onscreen yet, so this extra resize will not
    416     // cause visible jank.
    417     if (browser_->is_type_popup()) {
    418       CGFloat deltaH = desiredContentRect.height() -
    419                        NSHeight([[self tabContentArea] frame]);
    420       // Do not shrink the window, as that may break minimum size invariants.
    421       if (deltaH > 0) {
    422         // Convert from tabContentArea coordinates to window coordinates.
    423         NSSize convertedSize =
    424             [[self tabContentArea] convertSize:NSMakeSize(0, deltaH)
    425                                         toView:nil];
    426         NSRect frame = [[self window] frame];
    427         frame.size.height += convertedSize.height;
    428         frame.origin.y -= convertedSize.height;
    429         [[self window] setFrame:frame display:NO];
    430       }
    431     }
    432 
    433     // Create the bridge for the status bubble.
    434     statusBubble_ = new StatusBubbleMac([self window], self);
    435 
    436     // Register for application hide/unhide notifications.
    437     [[NSNotificationCenter defaultCenter]
    438          addObserver:self
    439             selector:@selector(applicationDidHide:)
    440                 name:NSApplicationDidHideNotification
    441               object:nil];
    442     [[NSNotificationCenter defaultCenter]
    443          addObserver:self
    444             selector:@selector(applicationDidUnhide:)
    445                 name:NSApplicationDidUnhideNotification
    446               object:nil];
    447 
    448     // This must be done after the view is added to the window since it relies
    449     // on the window bounds to determine whether to show buttons or not.
    450     if ([self hasToolbar])  // Do not create the buttons in popups.
    451       [toolbarController_ createBrowserActionButtons];
    452 
    453     extension_keybinding_registry_.reset(
    454         new ExtensionKeybindingRegistryCocoa(browser_->profile(),
    455             [self window],
    456             extensions::ExtensionKeybindingRegistry::ALL_EXTENSIONS,
    457             windowShim_.get()));
    458 
    459     // We are done initializing now.
    460     initializing_ = NO;
    461   }
    462   return self;
    463 }
    464 
    465 - (void)dealloc {
    466   browser_->tab_strip_model()->CloseAllTabs();
    467   [downloadShelfController_ exiting];
    468 
    469   // Explicitly release |presentationModeController_| here, as it may call back
    470   // to this BWC in |-dealloc|.  We are required to call |-exitPresentationMode|
    471   // before releasing the controller.
    472   [presentationModeController_ exitPresentationMode];
    473   presentationModeController_.reset();
    474 
    475   // Under certain testing configurations we may not actually own the browser.
    476   if (ownsBrowser_ == NO)
    477     ignore_result(browser_.release());
    478 
    479   [[NSNotificationCenter defaultCenter] removeObserver:self];
    480 
    481   [super dealloc];
    482 }
    483 
    484 - (gfx::Rect)enforceMinWindowSize:(gfx::Rect)bounds {
    485   gfx::Rect checkedBounds = bounds;
    486 
    487   NSSize minSize = [[self window] minSize];
    488   if (bounds.width() < minSize.width)
    489       checkedBounds.set_width(minSize.width);
    490   if (bounds.height() < minSize.height)
    491       checkedBounds.set_height(minSize.height);
    492 
    493   return checkedBounds;
    494 }
    495 
    496 - (BrowserWindow*)browserWindow {
    497   return windowShim_.get();
    498 }
    499 
    500 - (ToolbarController*)toolbarController {
    501   return toolbarController_.get();
    502 }
    503 
    504 - (TabStripController*)tabStripController {
    505   return tabStripController_.get();
    506 }
    507 
    508 - (FindBarCocoaController*)findBarCocoaController {
    509   return findBarCocoaController_.get();
    510 }
    511 
    512 - (InfoBarContainerController*)infoBarContainerController {
    513   return infoBarContainerController_.get();
    514 }
    515 
    516 - (StatusBubbleMac*)statusBubble {
    517   return statusBubble_;
    518 }
    519 
    520 - (LocationBarViewMac*)locationBarBridge {
    521   return [toolbarController_ locationBarBridge];
    522 }
    523 
    524 - (NSView*)floatingBarBackingView {
    525   return floatingBarBackingView_;
    526 }
    527 
    528 - (OverlayableContentsController*)overlayableContentsController {
    529   return overlayableContentsController_;
    530 }
    531 
    532 - (Profile*)profile {
    533   return browser_->profile();
    534 }
    535 
    536 - (AvatarButtonController*)avatarButtonController {
    537   return avatarButtonController_.get();
    538 }
    539 
    540 - (void)destroyBrowser {
    541   [NSApp removeWindowsItem:[self window]];
    542 
    543   // We need the window to go away now.
    544   // We can't actually use |-autorelease| here because there's an embedded
    545   // run loop in the |-performClose:| which contains its own autorelease pool.
    546   // Instead call it after a zero-length delay, which gets us back to the main
    547   // event loop.
    548   [self performSelector:@selector(autorelease)
    549              withObject:nil
    550              afterDelay:0];
    551 }
    552 
    553 // Called when the window meets the criteria to be closed (ie,
    554 // |-windowShouldClose:| returns YES). We must be careful to preserve the
    555 // semantics of BrowserWindow::Close() and not call the Browser's dtor directly
    556 // from this method.
    557 - (void)windowWillClose:(NSNotification*)notification {
    558   DCHECK_EQ([notification object], [self window]);
    559   DCHECK(browser_->tab_strip_model()->empty());
    560   [savedRegularWindow_ close];
    561   // We delete statusBubble here because we need to kill off the dependency
    562   // that its window has on our window before our window goes away.
    563   delete statusBubble_;
    564   statusBubble_ = NULL;
    565   // We can't actually use |-autorelease| here because there's an embedded
    566   // run loop in the |-performClose:| which contains its own autorelease pool.
    567   // Instead call it after a zero-length delay, which gets us back to the main
    568   // event loop.
    569   [self performSelector:@selector(autorelease)
    570              withObject:nil
    571              afterDelay:0];
    572 }
    573 
    574 - (void)updateDevToolsForContents:(WebContents*)contents {
    575   [devToolsController_ updateDevToolsForWebContents:contents
    576                                         withProfile:browser_->profile()];
    577   [self updateAllowOverlappingViews:[self inPresentationMode]];
    578 }
    579 
    580 // Called when the user wants to close a window or from the shutdown process.
    581 // The Browser object is in control of whether or not we're allowed to close. It
    582 // may defer closing due to several states, such as onUnload handlers needing to
    583 // be fired. If closing is deferred, the Browser will handle the processing
    584 // required to get us to the closing state and (by watching for all the tabs
    585 // going away) will again call to close the window when it's finally ready.
    586 - (BOOL)windowShouldClose:(id)sender {
    587   // Disable updates while closing all tabs to avoid flickering.
    588   gfx::ScopedNSDisableScreenUpdates disabler;
    589   // Give beforeunload handlers the chance to cancel the close before we hide
    590   // the window below.
    591   if (!browser_->ShouldCloseWindow())
    592     return NO;
    593 
    594   // saveWindowPositionIfNeeded: only works if we are the last active
    595   // window, but orderOut: ends up activating another window, so we
    596   // have to save the window position before we call orderOut:.
    597   [self saveWindowPositionIfNeeded];
    598 
    599   bool fast_tab_closing_enabled =
    600       CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableFastUnload);
    601 
    602   if (!browser_->tab_strip_model()->empty()) {
    603     // Tab strip isn't empty.  Hide the frame (so it appears to have closed
    604     // immediately) and close all the tabs, allowing the renderers to shut
    605     // down. When the tab strip is empty we'll be called back again.
    606     [[self window] orderOut:self];
    607     browser_->OnWindowClosing();
    608     if (fast_tab_closing_enabled)
    609       browser_->tab_strip_model()->CloseAllTabs();
    610     return NO;
    611   } else if (fast_tab_closing_enabled &&
    612         !browser_->HasCompletedUnloadProcessing()) {
    613     // The browser needs to finish running unload handlers.
    614     // Hide the window (so it appears to have closed immediately), and
    615     // the browser will call us back again when it is ready to close.
    616     [[self window] orderOut:self];
    617     return NO;
    618   }
    619 
    620   // the tab strip is empty, it's ok to close the window
    621   return YES;
    622 }
    623 
    624 // Called right after our window became the main window.
    625 - (void)windowDidBecomeMain:(NSNotification*)notification {
    626   BrowserList::SetLastActive(browser_.get());
    627   [self saveWindowPositionIfNeeded];
    628 
    629   // TODO(dmaclach): Instead of redrawing the whole window, views that care
    630   // about the active window state should be registering for notifications.
    631   [[self window] setViewsNeedDisplay:YES];
    632 
    633   // TODO(viettrungluu): For some reason, the above doesn't suffice.
    634   if ([self isFullscreen])
    635     [floatingBarBackingView_ setNeedsDisplay:YES];  // Okay even if nil.
    636 }
    637 
    638 - (void)windowDidResignMain:(NSNotification*)notification {
    639   // TODO(dmaclach): Instead of redrawing the whole window, views that care
    640   // about the active window state should be registering for notifications.
    641   [[self window] setViewsNeedDisplay:YES];
    642 
    643   // TODO(viettrungluu): For some reason, the above doesn't suffice.
    644   if ([self isFullscreen])
    645     [floatingBarBackingView_ setNeedsDisplay:YES];  // Okay even if nil.
    646 }
    647 
    648 // Called when we are activated (when we gain focus).
    649 - (void)windowDidBecomeKey:(NSNotification*)notification {
    650   // We need to activate the controls (in the "WebView"). To do this, get the
    651   // selected WebContents's RenderWidgetHostView and tell it to activate.
    652   if (WebContents* contents =
    653           browser_->tab_strip_model()->GetActiveWebContents()) {
    654 
    655     DevToolsWindow* devtoolsWindow =
    656         DevToolsWindow::GetDockedInstanceForInspectedTab(contents);
    657     if (devtoolsWindow) {
    658       RenderWidgetHostView* devtoolsView =
    659           devtoolsWindow->web_contents()->GetRenderWidgetHostView();
    660       if (devtoolsView && devtoolsView->HasFocus()) {
    661         devtoolsView->SetActive(true);
    662         return;
    663       }
    664     }
    665 
    666     if (RenderWidgetHostView* rwhv = contents->GetRenderWidgetHostView())
    667       rwhv->SetActive(true);
    668   }
    669 }
    670 
    671 // Called when we are deactivated (when we lose focus).
    672 - (void)windowDidResignKey:(NSNotification*)notification {
    673   // If our app is still active and we're still the key window, ignore this
    674   // message, since it just means that a menu extra (on the "system status bar")
    675   // was activated; we'll get another |-windowDidResignKey| if we ever really
    676   // lose key window status.
    677   if ([NSApp isActive] && ([NSApp keyWindow] == [self window]))
    678     return;
    679 
    680   // We need to deactivate the controls (in the "WebView"). To do this, get the
    681   // selected WebContents's RenderWidgetHostView and tell it to deactivate.
    682   if (WebContents* contents =
    683           browser_->tab_strip_model()->GetActiveWebContents()) {
    684 
    685     DevToolsWindow* devtoolsWindow =
    686         DevToolsWindow::GetDockedInstanceForInspectedTab(contents);
    687     if (devtoolsWindow) {
    688       RenderWidgetHostView* devtoolsView =
    689           devtoolsWindow->web_contents()->GetRenderWidgetHostView();
    690       if (devtoolsView && devtoolsView->HasFocus()) {
    691         devtoolsView->SetActive(false);
    692         return;
    693       }
    694     }
    695 
    696     if (RenderWidgetHostView* rwhv = contents->GetRenderWidgetHostView())
    697       rwhv->SetActive(false);
    698   }
    699 }
    700 
    701 // Called when we have been minimized.
    702 - (void)windowDidMiniaturize:(NSNotification *)notification {
    703   [self saveWindowPositionIfNeeded];
    704 
    705   // Let the selected RenderWidgetHostView know, so that it can tell plugins.
    706   if (WebContents* contents =
    707           browser_->tab_strip_model()->GetActiveWebContents()) {
    708     if (RenderWidgetHostView* rwhv = contents->GetRenderWidgetHostView())
    709       rwhv->SetWindowVisibility(false);
    710   }
    711 }
    712 
    713 // Called when we have been unminimized.
    714 - (void)windowDidDeminiaturize:(NSNotification *)notification {
    715   // Let the selected RenderWidgetHostView know, so that it can tell plugins.
    716   if (WebContents* contents =
    717           browser_->tab_strip_model()->GetActiveWebContents()) {
    718     if (RenderWidgetHostView* rwhv = contents->GetRenderWidgetHostView())
    719       rwhv->SetWindowVisibility(true);
    720   }
    721 }
    722 
    723 // Called when the application has been hidden.
    724 - (void)applicationDidHide:(NSNotification *)notification {
    725   // Let the selected RenderWidgetHostView know, so that it can tell plugins
    726   // (unless we are minimized, in which case nothing has really changed).
    727   if (![[self window] isMiniaturized]) {
    728   if (WebContents* contents =
    729           browser_->tab_strip_model()->GetActiveWebContents()) {
    730       if (RenderWidgetHostView* rwhv = contents->GetRenderWidgetHostView())
    731         rwhv->SetWindowVisibility(false);
    732     }
    733   }
    734 }
    735 
    736 // Called when the application has been unhidden.
    737 - (void)applicationDidUnhide:(NSNotification *)notification {
    738   // Let the selected RenderWidgetHostView know, so that it can tell plugins
    739   // (unless we are minimized, in which case nothing has really changed).
    740   if (![[self window] isMiniaturized]) {
    741   if (WebContents* contents =
    742           browser_->tab_strip_model()->GetActiveWebContents()) {
    743       if (RenderWidgetHostView* rwhv = contents->GetRenderWidgetHostView())
    744         rwhv->SetWindowVisibility(true);
    745     }
    746   }
    747 }
    748 
    749 // Called when the user clicks the zoom button (or selects it from the Window
    750 // menu) to determine the "standard size" of the window, based on the content
    751 // and other factors. If the current size/location differs nontrivally from the
    752 // standard size, Cocoa resizes the window to the standard size, and saves the
    753 // current size as the "user size". If the current size/location is the same (up
    754 // to a fudge factor) as the standard size, Cocoa resizes the window to the
    755 // saved user size. (It is possible for the two to coincide.) In this way, the
    756 // zoom button acts as a toggle. We determine the standard size based on the
    757 // content, but enforce a minimum width (calculated using the dimensions of the
    758 // screen) to ensure websites with small intrinsic width (such as google.com)
    759 // don't end up with a wee window. Moreover, we always declare the standard
    760 // width to be at least as big as the current width, i.e., we never want zooming
    761 // to the standard width to shrink the window. This is consistent with other
    762 // browsers' behaviour, and is desirable in multi-tab situations. Note, however,
    763 // that the "toggle" behaviour means that the window can still be "unzoomed" to
    764 // the user size.
    765 - (NSRect)windowWillUseStandardFrame:(NSWindow*)window
    766                         defaultFrame:(NSRect)frame {
    767   // Forget that we grew the window up (if we in fact did).
    768   [self resetWindowGrowthState];
    769 
    770   // |frame| already fills the current screen. Never touch y and height since we
    771   // always want to fill vertically.
    772 
    773   // If the shift key is down, maximize. Hopefully this should make the
    774   // "switchers" happy.
    775   if ([[NSApp currentEvent] modifierFlags] & NSShiftKeyMask) {
    776     return frame;
    777   }
    778 
    779   // To prevent strange results on portrait displays, the basic minimum zoomed
    780   // width is the larger of: 60% of available width, 60% of available height
    781   // (bounded by available width).
    782   const CGFloat kProportion = 0.6;
    783   CGFloat zoomedWidth =
    784       std::max(kProportion * NSWidth(frame),
    785                std::min(kProportion * NSHeight(frame), NSWidth(frame)));
    786 
    787   WebContents* contents = browser_->tab_strip_model()->GetActiveWebContents();
    788   if (contents) {
    789     // If the intrinsic width is bigger, then make it the zoomed width.
    790     const int kScrollbarWidth = 16;  // TODO(viettrungluu): ugh.
    791     CGFloat intrinsicWidth = static_cast<CGFloat>(
    792         contents->GetPreferredSize().width() + kScrollbarWidth);
    793     zoomedWidth = std::max(zoomedWidth,
    794                            std::min(intrinsicWidth, NSWidth(frame)));
    795   }
    796 
    797   // Never shrink from the current size on zoom (see above).
    798   NSRect currentFrame = [[self window] frame];
    799   zoomedWidth = std::max(zoomedWidth, NSWidth(currentFrame));
    800 
    801   // |frame| determines our maximum extents. We need to set the origin of the
    802   // frame -- and only move it left if necessary.
    803   if (currentFrame.origin.x + zoomedWidth > NSMaxX(frame))
    804     frame.origin.x = NSMaxX(frame) - zoomedWidth;
    805   else
    806     frame.origin.x = currentFrame.origin.x;
    807 
    808   // Set the width. Don't touch y or height.
    809   frame.size.width = zoomedWidth;
    810 
    811   return frame;
    812 }
    813 
    814 - (void)activate {
    815   [BrowserWindowUtils activateWindowForController:self];
    816 }
    817 
    818 // Determine whether we should let a window zoom/unzoom to the given |newFrame|.
    819 // We avoid letting unzoom move windows between screens, because it's really
    820 // strange and unintuitive.
    821 - (BOOL)windowShouldZoom:(NSWindow*)window toFrame:(NSRect)newFrame {
    822   // Figure out which screen |newFrame| is on.
    823   NSScreen* newScreen = nil;
    824   CGFloat newScreenOverlapArea = 0.0;
    825   for (NSScreen* screen in [NSScreen screens]) {
    826     NSRect overlap = NSIntersectionRect(newFrame, [screen frame]);
    827     CGFloat overlapArea = NSWidth(overlap) * NSHeight(overlap);
    828     if (overlapArea > newScreenOverlapArea) {
    829       newScreen = screen;
    830       newScreenOverlapArea = overlapArea;
    831     }
    832   }
    833   // If we're somehow not on any screen, allow the zoom.
    834   if (!newScreen)
    835     return YES;
    836 
    837   // If the new screen is the current screen, we can return a definitive YES.
    838   // Note: This check is not strictly necessary, but just short-circuits in the
    839   // "no-brainer" case. To test the complicated logic below, comment this out!
    840   NSScreen* curScreen = [window screen];
    841   if (newScreen == curScreen)
    842     return YES;
    843 
    844   // Worry a little: What happens when a window is on two (or more) screens?
    845   // E.g., what happens in a 50-50 scenario? Cocoa may reasonably elect to zoom
    846   // to the other screen rather than staying on the officially current one. So
    847   // we compare overlaps with the current window frame, and see if Cocoa's
    848   // choice was reasonable (allowing a small rounding error). This should
    849   // hopefully avoid us ever erroneously denying a zoom when a window is on
    850   // multiple screens.
    851   NSRect curFrame = [window frame];
    852   NSRect newScrIntersectCurFr = NSIntersectionRect([newScreen frame], curFrame);
    853   NSRect curScrIntersectCurFr = NSIntersectionRect([curScreen frame], curFrame);
    854   if (NSWidth(newScrIntersectCurFr) * NSHeight(newScrIntersectCurFr) >=
    855       (NSWidth(curScrIntersectCurFr) * NSHeight(curScrIntersectCurFr) - 1.0)) {
    856     return YES;
    857   }
    858 
    859   // If it wasn't reasonable, return NO.
    860   return NO;
    861 }
    862 
    863 // Adjusts the window height by the given amount.
    864 - (BOOL)adjustWindowHeightBy:(CGFloat)deltaH {
    865   // By not adjusting the window height when initializing, we can ensure that
    866   // the window opens with the same size that was saved on close.
    867   if (initializing_ || [self isFullscreen] || deltaH == 0)
    868     return NO;
    869 
    870   NSWindow* window = [self window];
    871   NSRect windowFrame = [window frame];
    872   NSRect workarea = [[window screen] visibleFrame];
    873 
    874   // If the window is not already fully in the workarea, do not adjust its frame
    875   // at all.
    876   if (!NSContainsRect(workarea, windowFrame))
    877     return NO;
    878 
    879   // Record the position of the top/bottom of the window, so we can easily check
    880   // whether we grew the window upwards/downwards.
    881   CGFloat oldWindowMaxY = NSMaxY(windowFrame);
    882   CGFloat oldWindowMinY = NSMinY(windowFrame);
    883 
    884   // We are "zoomed" if we occupy the full vertical space.
    885   bool isZoomed = (windowFrame.origin.y == workarea.origin.y &&
    886                    NSHeight(windowFrame) == NSHeight(workarea));
    887 
    888   // If we're shrinking the window....
    889   if (deltaH < 0) {
    890     bool didChange = false;
    891 
    892     // Don't reset if not currently zoomed since shrinking can take several
    893     // steps!
    894     if (isZoomed)
    895       isShrinkingFromZoomed_ = YES;
    896 
    897     // If we previously grew at the top, shrink as much as allowed at the top
    898     // first.
    899     if (windowTopGrowth_ > 0) {
    900       CGFloat shrinkAtTopBy = MIN(-deltaH, windowTopGrowth_);
    901       windowFrame.size.height -= shrinkAtTopBy;  // Shrink the window.
    902       deltaH += shrinkAtTopBy;            // Update the amount left to shrink.
    903       windowTopGrowth_ -= shrinkAtTopBy;  // Update the growth state.
    904       didChange = true;
    905     }
    906 
    907     // Similarly for the bottom (not an "else if" since we may have to
    908     // simultaneously shrink at both the top and at the bottom). Note that
    909     // |deltaH| may no longer be nonzero due to the above.
    910     if (deltaH < 0 && windowBottomGrowth_ > 0) {
    911       CGFloat shrinkAtBottomBy = MIN(-deltaH, windowBottomGrowth_);
    912       windowFrame.origin.y += shrinkAtBottomBy;     // Move the window up.
    913       windowFrame.size.height -= shrinkAtBottomBy;  // Shrink the window.
    914       deltaH += shrinkAtBottomBy;               // Update the amount left....
    915       windowBottomGrowth_ -= shrinkAtBottomBy;  // Update the growth state.
    916       didChange = true;
    917     }
    918 
    919     // If we're shrinking from zoomed but we didn't change the top or bottom
    920     // (since we've reached the limits imposed by |window...Growth_|), then stop
    921     // here. Don't reset |isShrinkingFromZoomed_| since we might get called
    922     // again for the same shrink.
    923     if (isShrinkingFromZoomed_ && !didChange)
    924       return NO;
    925   } else {
    926     isShrinkingFromZoomed_ = NO;
    927 
    928     // Don't bother with anything else.
    929     if (isZoomed)
    930       return NO;
    931   }
    932 
    933   // Shrinking from zoomed is handled above (and is constrained by
    934   // |window...Growth_|).
    935   if (!isShrinkingFromZoomed_) {
    936     // Resize the window down until it hits the bottom of the workarea, then if
    937     // needed continue resizing upwards.  Do not resize the window to be taller
    938     // than the current workarea.
    939     // Resize the window as requested, keeping the top left corner fixed.
    940     windowFrame.origin.y -= deltaH;
    941     windowFrame.size.height += deltaH;
    942 
    943     // If the bottom left corner is now outside the visible frame, move the
    944     // window up to make it fit, but make sure not to move the top left corner
    945     // out of the visible frame.
    946     if (windowFrame.origin.y < workarea.origin.y) {
    947       windowFrame.origin.y = workarea.origin.y;
    948       windowFrame.size.height =
    949           std::min(NSHeight(windowFrame), NSHeight(workarea));
    950     }
    951 
    952     // Record (if applicable) how much we grew the window in either direction.
    953     // (N.B.: These only record growth, not shrinkage.)
    954     if (NSMaxY(windowFrame) > oldWindowMaxY)
    955       windowTopGrowth_ += NSMaxY(windowFrame) - oldWindowMaxY;
    956     if (NSMinY(windowFrame) < oldWindowMinY)
    957       windowBottomGrowth_ += oldWindowMinY - NSMinY(windowFrame);
    958   }
    959 
    960   // Disable subview resizing while resizing the window, or else we will get
    961   // unwanted renderer resizes.  The calling code must call layoutSubviews to
    962   // make things right again.
    963   NSView* contentView = [window contentView];
    964   [contentView setAutoresizesSubviews:NO];
    965   [window setFrame:windowFrame display:NO];
    966   [contentView setAutoresizesSubviews:YES];
    967   return YES;
    968 }
    969 
    970 // Main method to resize browser window subviews.  This method should be called
    971 // when resizing any child of the content view, rather than resizing the views
    972 // directly.  If the view is already the correct height, does not force a
    973 // relayout.
    974 - (void)resizeView:(NSView*)view newHeight:(CGFloat)height {
    975   // We should only ever be called for one of the following four views.
    976   // |downloadShelfController_| may be nil. If we are asked to size the bookmark
    977   // bar directly, its superview must be this controller's content view.
    978   DCHECK(view);
    979   DCHECK(view == [toolbarController_ view] ||
    980          view == [infoBarContainerController_ view] ||
    981          view == [downloadShelfController_ view] ||
    982          view == [bookmarkBarController_ view]);
    983 
    984   // Change the height of the view and call |-layoutSubViews|. We set the height
    985   // here without regard to where the view is on the screen or whether it needs
    986   // to "grow up" or "grow down."  The below call to |-layoutSubviews| will
    987   // position each view correctly.
    988   NSRect frame = [view frame];
    989   if (NSHeight(frame) == height)
    990     return;
    991 
    992   // Disable screen updates to prevent flickering.
    993   if (view == [bookmarkBarController_ view] ||
    994       view == [downloadShelfController_ view]) {
    995     [[self window] disableScreenUpdatesUntilFlush];
    996   }
    997 
    998   // Grow or shrink the window by the amount of the height change.  We adjust
    999   // the window height only in two cases:
   1000   // 1) We are adjusting the height of the bookmark bar and it is currently
   1001   // animating either open or closed.
   1002   // 2) We are adjusting the height of the download shelf.
   1003   //
   1004   // We do not adjust the window height for bookmark bar changes on the NTP.
   1005   BOOL shouldAdjustBookmarkHeight =
   1006       [bookmarkBarController_ isAnimatingBetweenState:BookmarkBar::HIDDEN
   1007                                              andState:BookmarkBar::SHOW];
   1008 
   1009   BOOL resizeRectDirty = NO;
   1010   if ((shouldAdjustBookmarkHeight && view == [bookmarkBarController_ view]) ||
   1011       view == [downloadShelfController_ view]) {
   1012     CGFloat deltaH = height - NSHeight(frame);
   1013     if ([self adjustWindowHeightBy:deltaH] &&
   1014         view == [downloadShelfController_ view]) {
   1015       // If the window height didn't change, the download shelf will change the
   1016       // size of the contents. If the contents size doesn't change, send it
   1017       // an explicit grow box invalidation (else, the resize message does that.)
   1018       resizeRectDirty = YES;
   1019     }
   1020   }
   1021 
   1022   frame.size.height = height;
   1023   // TODO(rohitrao): Determine if calling setFrame: twice is bad.
   1024   [view setFrame:frame];
   1025   [self layoutSubviews];
   1026 
   1027   if (resizeRectDirty) {
   1028     // Send new resize rect to foreground tab.
   1029     if (content::WebContents* contents =
   1030             browser_->tab_strip_model()->GetActiveWebContents()) {
   1031       if (content::RenderViewHost* rvh = contents->GetRenderViewHost()) {
   1032         rvh->ResizeRectChanged(windowShim_->GetRootWindowResizerRect());
   1033       }
   1034     }
   1035   }
   1036 }
   1037 
   1038 - (void)setAnimationInProgress:(BOOL)inProgress {
   1039   [[self tabContentArea] setFastResizeMode:inProgress];
   1040 }
   1041 
   1042 // Update a toggle state for an NSMenuItem if modified.
   1043 // Take care to ensure |item| looks like a NSMenuItem.
   1044 // Called by validateUserInterfaceItem:.
   1045 - (void)updateToggleStateWithTag:(NSInteger)tag forItem:(id)item {
   1046   if (![item respondsToSelector:@selector(state)] ||
   1047       ![item respondsToSelector:@selector(setState:)])
   1048     return;
   1049 
   1050   // On Windows this logic happens in bookmark_bar_view.cc.  On the
   1051   // Mac we're a lot more MVC happy so we've moved it into a
   1052   // controller.  To be clear, this simply updates the menu item; it
   1053   // does not display the bookmark bar itself.
   1054   if (tag == IDC_SHOW_BOOKMARK_BAR) {
   1055     bool toggled = windowShim_->IsBookmarkBarVisible();
   1056     NSInteger oldState = [item state];
   1057     NSInteger newState = toggled ? NSOnState : NSOffState;
   1058     if (oldState != newState)
   1059       [item setState:newState];
   1060   }
   1061 
   1062   // Update the checked/Unchecked state of items in the encoding menu.
   1063   // On Windows, this logic is part of |EncodingMenuModel| in
   1064   // browser/ui/views/toolbar_view.h.
   1065   EncodingMenuController encoding_controller;
   1066   if (encoding_controller.DoesCommandBelongToEncodingMenu(tag)) {
   1067     DCHECK(browser_.get());
   1068     Profile* profile = browser_->profile();
   1069     DCHECK(profile);
   1070     WebContents* current_tab =
   1071         browser_->tab_strip_model()->GetActiveWebContents();
   1072     if (!current_tab)
   1073       return;
   1074 
   1075     const std::string encoding = current_tab->GetEncoding();
   1076 
   1077     bool toggled = encoding_controller.IsItemChecked(profile, encoding, tag);
   1078     NSInteger oldState = [item state];
   1079     NSInteger newState = toggled ? NSOnState : NSOffState;
   1080     if (oldState != newState)
   1081       [item setState:newState];
   1082   }
   1083 }
   1084 
   1085 // Called to validate menu and toolbar items when this window is key. All the
   1086 // items we care about have been set with the |-commandDispatch:| or
   1087 // |-commandDispatchUsingKeyModifiers:| actions and a target of FirstResponder
   1088 // in IB. If it's not one of those, let it continue up the responder chain to be
   1089 // handled elsewhere. We pull out the tag as the cross-platform constant to
   1090 // differentiate and dispatch the various commands.
   1091 // NOTE: we might have to handle state for app-wide menu items,
   1092 // although we could cheat and directly ask the app controller if our
   1093 // command_updater doesn't support the command. This may or may not be an issue,
   1094 // too early to tell.
   1095 - (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item {
   1096   SEL action = [item action];
   1097   BOOL enable = NO;
   1098   if (action == @selector(commandDispatch:) ||
   1099       action == @selector(commandDispatchUsingKeyModifiers:)) {
   1100     NSInteger tag = [item tag];
   1101     if (chrome::SupportsCommand(browser_.get(), tag)) {
   1102       // Generate return value (enabled state)
   1103       enable = chrome::IsCommandEnabled(browser_.get(), tag);
   1104       switch (tag) {
   1105         case IDC_CLOSE_TAB:
   1106           // Disable "close tab" if the receiving window is not tabbed.
   1107           // We simply check whether the item has a keyboard shortcut set here;
   1108           // app_controller_mac.mm actually determines whether the item should
   1109           // be enabled.
   1110           if ([static_cast<NSObject*>(item) isKindOfClass:[NSMenuItem class]])
   1111             enable &= !![[static_cast<NSMenuItem*>(item) keyEquivalent] length];
   1112           break;
   1113         case IDC_FULLSCREEN: {
   1114           if ([static_cast<NSObject*>(item) isKindOfClass:[NSMenuItem class]]) {
   1115             NSString* menuTitle = l10n_util::GetNSString(
   1116                 [self isFullscreen] && ![self inPresentationMode] ?
   1117                     IDS_EXIT_FULLSCREEN_MAC :
   1118                     IDS_ENTER_FULLSCREEN_MAC);
   1119             [static_cast<NSMenuItem*>(item) setTitle:menuTitle];
   1120 
   1121             if (!chrome::mac::SupportsSystemFullscreen())
   1122               [static_cast<NSMenuItem*>(item) setHidden:YES];
   1123           }
   1124           break;
   1125         }
   1126         case IDC_PRESENTATION_MODE: {
   1127           if ([static_cast<NSObject*>(item) isKindOfClass:[NSMenuItem class]]) {
   1128             NSString* menuTitle = l10n_util::GetNSString(
   1129                 [self inPresentationMode] ? IDS_EXIT_PRESENTATION_MAC :
   1130                                             IDS_ENTER_PRESENTATION_MAC);
   1131             [static_cast<NSMenuItem*>(item) setTitle:menuTitle];
   1132           }
   1133           break;
   1134         }
   1135         case IDC_SHOW_SIGNIN: {
   1136           Profile* original_profile =
   1137               browser_->profile()->GetOriginalProfile();
   1138           [BrowserWindowController updateSigninItem:item
   1139                                          shouldShow:enable
   1140                                      currentProfile:original_profile];
   1141           break;
   1142         }
   1143         default:
   1144           // Special handling for the contents of the Text Encoding submenu. On
   1145           // Mac OS, instead of enabling/disabling the top-level menu item, we
   1146           // enable/disable the submenu's contents (per Apple's HIG).
   1147           EncodingMenuController encoding_controller;
   1148           if (encoding_controller.DoesCommandBelongToEncodingMenu(tag)) {
   1149             enable &= chrome::IsCommandEnabled(browser_.get(),
   1150                                                IDC_ENCODING_MENU) ? YES : NO;
   1151           }
   1152       }
   1153 
   1154       // If the item is toggleable, find its toggle state and
   1155       // try to update it.  This is a little awkward, but the alternative is
   1156       // to check after a commandDispatch, which seems worse.
   1157       [self updateToggleStateWithTag:tag forItem:item];
   1158     }
   1159   }
   1160   return enable;
   1161 }
   1162 
   1163 // Called when the user picks a menu or toolbar item when this window is key.
   1164 // Calls through to the browser object to execute the command. This assumes that
   1165 // the command is supported and doesn't check, otherwise it would have been
   1166 // disabled in the UI in validateUserInterfaceItem:.
   1167 - (void)commandDispatch:(id)sender {
   1168   DCHECK(sender);
   1169   // Identify the actual BWC to which the command should be dispatched. It might
   1170   // belong to a background window, yet this controller gets it because it is
   1171   // the foreground window's controller and thus in the responder chain. Some
   1172   // senders don't have this problem (for example, menus only operate on the
   1173   // foreground window), so this is only an issue for senders that are part of
   1174   // windows.
   1175   BrowserWindowController* targetController = self;
   1176   if ([sender respondsToSelector:@selector(window)])
   1177     targetController = [[sender window] windowController];
   1178   DCHECK([targetController isKindOfClass:[BrowserWindowController class]]);
   1179   DCHECK(targetController->browser_.get());
   1180   chrome::ExecuteCommand(targetController->browser_.get(), [sender tag]);
   1181 }
   1182 
   1183 // Same as |-commandDispatch:|, but executes commands using a disposition
   1184 // determined by the key flags. If the window is in the background and the
   1185 // command key is down, ignore the command key, but process any other modifiers.
   1186 - (void)commandDispatchUsingKeyModifiers:(id)sender {
   1187   DCHECK(sender);
   1188 
   1189   if (![sender isEnabled]) {
   1190     // This code is reachable e.g. if the user mashes the back button, queuing
   1191     // up a bunch of events before the button's enabled state is updated:
   1192     // http://crbug.com/63254
   1193     return;
   1194   }
   1195 
   1196   // See comment above for why we do this.
   1197   BrowserWindowController* targetController = self;
   1198   if ([sender respondsToSelector:@selector(window)])
   1199     targetController = [[sender window] windowController];
   1200   DCHECK([targetController isKindOfClass:[BrowserWindowController class]]);
   1201   NSInteger command = [sender tag];
   1202   NSUInteger modifierFlags = [[NSApp currentEvent] modifierFlags];
   1203   if ((command == IDC_RELOAD) &&
   1204       (modifierFlags & (NSShiftKeyMask | NSControlKeyMask))) {
   1205     command = IDC_RELOAD_IGNORING_CACHE;
   1206     // Mask off Shift and Control so they don't affect the disposition below.
   1207     modifierFlags &= ~(NSShiftKeyMask | NSControlKeyMask);
   1208   }
   1209   if (![[sender window] isMainWindow]) {
   1210     // Remove the command key from the flags, it means "keep the window in
   1211     // the background" in this case.
   1212     modifierFlags &= ~NSCommandKeyMask;
   1213   }
   1214   WindowOpenDisposition disposition =
   1215       ui::WindowOpenDispositionFromNSEventWithFlags(
   1216           [NSApp currentEvent], modifierFlags);
   1217   switch (command) {
   1218     case IDC_BACK:
   1219     case IDC_FORWARD:
   1220     case IDC_RELOAD:
   1221     case IDC_RELOAD_IGNORING_CACHE:
   1222       if (disposition == CURRENT_TAB) {
   1223         // Forcibly reset the location bar, since otherwise it won't discard any
   1224         // ongoing user edits, since it doesn't realize this is a user-initiated
   1225         // action.
   1226         [targetController locationBarBridge]->Revert();
   1227       }
   1228   }
   1229   DCHECK(targetController->browser_.get());
   1230   chrome::ExecuteCommandWithDisposition(targetController->browser_.get(),
   1231                                         command, disposition);
   1232 }
   1233 
   1234 // Called when another part of the internal codebase needs to execute a
   1235 // command.
   1236 - (void)executeCommand:(int)command {
   1237   chrome::ExecuteCommand(browser_.get(), command);
   1238 }
   1239 
   1240 - (BOOL)handledByExtensionCommand:(NSEvent*)event {
   1241   return extension_keybinding_registry_->ProcessKeyEvent(
   1242       content::NativeWebKeyboardEvent(event));
   1243 }
   1244 
   1245 // StatusBubble delegate method: tell the status bubble the frame it should
   1246 // position itself in.
   1247 - (NSRect)statusBubbleBaseFrame {
   1248   NSView* view = [overlayableContentsController_ view];
   1249   return [view convertRect:[view bounds] toView:nil];
   1250 }
   1251 
   1252 - (void)updateToolbarWithContents:(WebContents*)tab
   1253                shouldRestoreState:(BOOL)shouldRestore {
   1254   [toolbarController_ updateToolbarWithContents:tab
   1255                              shouldRestoreState:shouldRestore];
   1256 }
   1257 
   1258 - (void)setStarredState:(BOOL)isStarred {
   1259   [toolbarController_ setStarredState:isStarred];
   1260 }
   1261 
   1262 - (void)zoomChangedForActiveTab:(BOOL)canShowBubble {
   1263   [toolbarController_ zoomChangedForActiveTab:canShowBubble];
   1264 }
   1265 
   1266 // Return the rect, in WebKit coordinates (flipped), of the window's grow box
   1267 // in the coordinate system of the content area of the currently selected tab.
   1268 // |windowGrowBox| needs to be in the window's coordinate system.
   1269 - (NSRect)selectedTabGrowBoxRect {
   1270   NSWindow* window = [self window];
   1271   if (![window respondsToSelector:@selector(_growBoxRect)])
   1272     return NSZeroRect;
   1273 
   1274   // Before we return a rect, we need to convert it from window coordinates
   1275   // to tab content area coordinates and flip the coordinate system.
   1276   NSRect growBoxRect =
   1277       [[self tabContentArea] convertRect:[window _growBoxRect] fromView:nil];
   1278   growBoxRect.origin.y =
   1279       NSHeight([[self tabContentArea] frame]) - NSMaxY(growBoxRect);
   1280   return growBoxRect;
   1281 }
   1282 
   1283 // Accept tabs from a BrowserWindowController with the same Profile.
   1284 - (BOOL)canReceiveFrom:(TabWindowController*)source {
   1285   if (![source isKindOfClass:[BrowserWindowController class]]) {
   1286     return NO;
   1287   }
   1288 
   1289   BrowserWindowController* realSource =
   1290       static_cast<BrowserWindowController*>(source);
   1291   if (browser_->profile() != realSource->browser_->profile()) {
   1292     return NO;
   1293   }
   1294 
   1295   // Can't drag a tab from a normal browser to a pop-up
   1296   if (browser_->type() != realSource->browser_->type()) {
   1297     return NO;
   1298   }
   1299 
   1300   return YES;
   1301 }
   1302 
   1303 // Move a given tab view to the location of the current placeholder. If there is
   1304 // no placeholder, it will go at the end. |controller| is the window controller
   1305 // of a tab being dropped from a different window. It will be nil if the drag is
   1306 // within the window, otherwise the tab is removed from that window before being
   1307 // placed into this one. The implementation will call |-removePlaceholder| since
   1308 // the drag is now complete.  This also calls |-layoutTabs| internally so
   1309 // clients do not need to call it again.
   1310 - (void)moveTabView:(NSView*)view
   1311      fromController:(TabWindowController*)dragController {
   1312   if (dragController) {
   1313     // Moving between windows. Figure out the WebContents to drop into our tab
   1314     // model from the source window's model.
   1315     BOOL isBrowser =
   1316         [dragController isKindOfClass:[BrowserWindowController class]];
   1317     DCHECK(isBrowser);
   1318     if (!isBrowser) return;
   1319     BrowserWindowController* dragBWC = (BrowserWindowController*)dragController;
   1320     int index = [dragBWC->tabStripController_ modelIndexForTabView:view];
   1321     WebContents* contents =
   1322         dragBWC->browser_->tab_strip_model()->GetWebContentsAt(index);
   1323     // The tab contents may have gone away if given a window.close() while it
   1324     // is being dragged. If so, bail, we've got nothing to drop.
   1325     if (!contents)
   1326       return;
   1327 
   1328     // Convert |view|'s frame (which starts in the source tab strip's coordinate
   1329     // system) to the coordinate system of the destination tab strip. This needs
   1330     // to be done before being detached so the window transforms can be
   1331     // performed.
   1332     NSRect destinationFrame = [view frame];
   1333     NSPoint tabOrigin = destinationFrame.origin;
   1334     tabOrigin = [[dragController tabStripView] convertPoint:tabOrigin
   1335                                                      toView:nil];
   1336     tabOrigin = [[view window] convertBaseToScreen:tabOrigin];
   1337     tabOrigin = [[self window] convertScreenToBase:tabOrigin];
   1338     tabOrigin = [[self tabStripView] convertPoint:tabOrigin fromView:nil];
   1339     destinationFrame.origin = tabOrigin;
   1340 
   1341     // Before the tab is detached from its originating tab strip, store the
   1342     // pinned state so that it can be maintained between the windows.
   1343     bool isPinned = dragBWC->browser_->tab_strip_model()->IsTabPinned(index);
   1344 
   1345     // Now that we have enough information about the tab, we can remove it from
   1346     // the dragging window. We need to do this *before* we add it to the new
   1347     // window as this will remove the WebContents' delegate.
   1348     [dragController detachTabView:view];
   1349 
   1350     // Deposit it into our model at the appropriate location (it already knows
   1351     // where it should go from tracking the drag). Doing this sets the tab's
   1352     // delegate to be the Browser.
   1353     [tabStripController_ dropWebContents:contents
   1354                                withFrame:destinationFrame
   1355                              asPinnedTab:isPinned];
   1356   } else {
   1357     // Moving within a window.
   1358     int index = [tabStripController_ modelIndexForTabView:view];
   1359     [tabStripController_ moveTabFromIndex:index];
   1360   }
   1361 
   1362   // Remove the placeholder since the drag is now complete.
   1363   [self removePlaceholder];
   1364 }
   1365 
   1366 // Tells the tab strip to forget about this tab in preparation for it being
   1367 // put into a different tab strip, such as during a drop on another window.
   1368 - (void)detachTabView:(NSView*)view {
   1369   int index = [tabStripController_ modelIndexForTabView:view];
   1370   browser_->tab_strip_model()->DetachWebContentsAt(index);
   1371 }
   1372 
   1373 - (NSView*)activeTabView {
   1374   return [tabStripController_ activeTabView];
   1375 }
   1376 
   1377 - (void)setIsLoading:(BOOL)isLoading force:(BOOL)force {
   1378   [toolbarController_ setIsLoading:isLoading force:force];
   1379 }
   1380 
   1381 // Make the location bar the first responder, if possible.
   1382 - (void)focusLocationBar:(BOOL)selectAll {
   1383   [toolbarController_ focusLocationBar:selectAll];
   1384 }
   1385 
   1386 - (void)focusTabContents {
   1387   [[self window] makeFirstResponder:[tabStripController_ activeTabView]];
   1388 }
   1389 
   1390 - (void)layoutTabs {
   1391   [tabStripController_ layoutTabs];
   1392 }
   1393 
   1394 - (TabWindowController*)detachTabToNewWindow:(TabView*)tabView {
   1395   // Disable screen updates so that this appears as a single visual change.
   1396   gfx::ScopedNSDisableScreenUpdates disabler;
   1397 
   1398   // Fetch the tab contents for the tab being dragged.
   1399   int index = [tabStripController_ modelIndexForTabView:tabView];
   1400   WebContents* contents = browser_->tab_strip_model()->GetWebContentsAt(index);
   1401 
   1402   // Set the window size. Need to do this before we detach the tab so it's
   1403   // still in the window. We have to flip the coordinates as that's what
   1404   // is expected by the Browser code.
   1405   NSWindow* sourceWindow = [tabView window];
   1406   NSRect windowRect = [sourceWindow frame];
   1407   NSScreen* screen = [sourceWindow screen];
   1408   windowRect.origin.y = NSHeight([screen frame]) - NSMaxY(windowRect);
   1409   gfx::Rect browserRect(windowRect.origin.x, windowRect.origin.y,
   1410                         NSWidth(windowRect), NSHeight(windowRect));
   1411 
   1412   NSRect sourceTabRect = [tabView frame];
   1413   NSView* tabStrip = [self tabStripView];
   1414 
   1415   // Pushes tabView's frame back inside the tabstrip.
   1416   NSSize tabOverflow =
   1417       [self overflowFrom:[tabStrip convertRect:sourceTabRect toView:nil]
   1418                       to:[tabStrip frame]];
   1419   NSRect tabRect = NSOffsetRect(sourceTabRect,
   1420                                 -tabOverflow.width, -tabOverflow.height);
   1421 
   1422   // Before detaching the tab, store the pinned state.
   1423   bool isPinned = browser_->tab_strip_model()->IsTabPinned(index);
   1424 
   1425   // Detach it from the source window, which just updates the model without
   1426   // deleting the tab contents. This needs to come before creating the new
   1427   // Browser because it clears the WebContents' delegate, which gets hooked
   1428   // up during creation of the new window.
   1429   browser_->tab_strip_model()->DetachWebContentsAt(index);
   1430 
   1431   // Create the new window with a single tab in its model, the one being
   1432   // dragged.
   1433   DockInfo dockInfo;
   1434   TabStripModelDelegate::NewStripContents item;
   1435   item.web_contents = contents;
   1436   item.add_types = TabStripModel::ADD_ACTIVE |
   1437                    (isPinned ? TabStripModel::ADD_PINNED
   1438                              : TabStripModel::ADD_NONE);
   1439   std::vector<TabStripModelDelegate::NewStripContents> contentses;
   1440   contentses.push_back(item);
   1441   Browser* newBrowser = browser_->tab_strip_model()->delegate()->
   1442       CreateNewStripWithContents(contentses, browserRect, dockInfo, false);
   1443 
   1444   // Get the new controller by asking the new window for its delegate.
   1445   BrowserWindowController* controller =
   1446       reinterpret_cast<BrowserWindowController*>(
   1447           [newBrowser->window()->GetNativeWindow() delegate]);
   1448   DCHECK(controller && [controller isKindOfClass:[TabWindowController class]]);
   1449 
   1450   // Force the added tab to the right size (remove stretching.)
   1451   tabRect.size.height = [TabStripController defaultTabHeight];
   1452 
   1453   // And make sure we use the correct frame in the new view.
   1454   [[controller tabStripController] setFrameOfActiveTab:tabRect];
   1455   return controller;
   1456 }
   1457 
   1458 - (void)insertPlaceholderForTab:(TabView*)tab
   1459                           frame:(NSRect)frame {
   1460   [super insertPlaceholderForTab:tab frame:frame];
   1461   [tabStripController_ insertPlaceholderForTab:tab frame:frame];
   1462 }
   1463 
   1464 - (void)removePlaceholder {
   1465   [super removePlaceholder];
   1466   [tabStripController_ insertPlaceholderForTab:nil frame:NSZeroRect];
   1467 }
   1468 
   1469 - (BOOL)isDragSessionActive {
   1470   // The tab can be dragged within the existing tab strip or detached
   1471   // into its own window (then the overlay window will be present).
   1472   return [[self tabStripController] isDragSessionActive] ||
   1473          [self overlayWindow] != nil;
   1474 }
   1475 
   1476 - (BOOL)tabDraggingAllowed {
   1477   return [tabStripController_ tabDraggingAllowed];
   1478 }
   1479 
   1480 - (BOOL)tabTearingAllowed {
   1481   return ![self isFullscreen];
   1482 }
   1483 
   1484 - (BOOL)windowMovementAllowed {
   1485   return ![self isFullscreen];
   1486 }
   1487 
   1488 - (BOOL)isTabFullyVisible:(TabView*)tab {
   1489   return [tabStripController_ isTabFullyVisible:tab];
   1490 }
   1491 
   1492 - (void)showNewTabButton:(BOOL)show {
   1493   [tabStripController_ showNewTabButton:show];
   1494 }
   1495 
   1496 - (BOOL)shouldShowAvatar {
   1497   if (![self hasTabStrip])
   1498     return NO;
   1499   if (browser_->profile()->IsOffTheRecord())
   1500     return YES;
   1501 
   1502   ProfileInfoCache& cache =
   1503       g_browser_process->profile_manager()->GetProfileInfoCache();
   1504   if (cache.GetIndexOfProfileWithPath(browser_->profile()->GetPath()) ==
   1505       std::string::npos) {
   1506     return NO;
   1507   }
   1508 
   1509   return AvatarMenuModel::ShouldShowAvatarMenu();
   1510 }
   1511 
   1512 - (BOOL)isBookmarkBarVisible {
   1513   return [bookmarkBarController_ isVisible];
   1514 }
   1515 
   1516 - (BOOL)isBookmarkBarAnimating {
   1517   return [bookmarkBarController_ isAnimationRunning];
   1518 }
   1519 
   1520 - (BookmarkBarController*)bookmarkBarController {
   1521   return bookmarkBarController_;
   1522 }
   1523 
   1524 - (DevToolsController*)devToolsController {
   1525   return devToolsController_;
   1526 }
   1527 
   1528 - (BOOL)isDownloadShelfVisible {
   1529   return downloadShelfController_ != nil &&
   1530       [downloadShelfController_ isVisible];
   1531 }
   1532 
   1533 - (DownloadShelfController*)downloadShelf {
   1534   if (!downloadShelfController_.get()) {
   1535     downloadShelfController_.reset([[DownloadShelfController alloc]
   1536         initWithBrowser:browser_.get() resizeDelegate:self]);
   1537     [[[self window] contentView] addSubview:[downloadShelfController_ view]];
   1538   }
   1539   return downloadShelfController_;
   1540 }
   1541 
   1542 - (void)addFindBar:(FindBarCocoaController*)findBarCocoaController {
   1543   // Shouldn't call addFindBar twice.
   1544   DCHECK(!findBarCocoaController_.get());
   1545 
   1546   // Create a controller for the findbar.
   1547   findBarCocoaController_.reset([findBarCocoaController retain]);
   1548   [self layoutSubviews];
   1549   [self updateSubviewZOrder:[self inPresentationMode]];
   1550 }
   1551 
   1552 - (NSWindow*)createFullscreenWindow {
   1553   return [[[FullscreenWindow alloc] initForScreen:[[self window] screen]]
   1554            autorelease];
   1555 }
   1556 
   1557 - (NSInteger)numberOfTabs {
   1558   // count() includes pinned tabs.
   1559   return browser_->tab_strip_model()->count();
   1560 }
   1561 
   1562 - (BOOL)hasLiveTabs {
   1563   return !browser_->tab_strip_model()->empty();
   1564 }
   1565 
   1566 - (NSString*)activeTabTitle {
   1567   WebContents* contents = browser_->tab_strip_model()->GetActiveWebContents();
   1568   return base::SysUTF16ToNSString(contents->GetTitle());
   1569 }
   1570 
   1571 - (NSRect)regularWindowFrame {
   1572   return [self isFullscreen] ? savedRegularWindowFrame_ :
   1573                                [[self window] frame];
   1574 }
   1575 
   1576 // (Override of |TabWindowController| method.)
   1577 - (BOOL)hasTabStrip {
   1578   return [self supportsWindowFeature:Browser::FEATURE_TABSTRIP];
   1579 }
   1580 
   1581 - (BOOL)isTabDraggable:(NSView*)tabView {
   1582   // TODO(avi, thakis): ConstrainedWindowSheetController has no api to move
   1583   // tabsheets between windows. Until then, we have to prevent having to move a
   1584   // tabsheet between windows, e.g. no tearing off of tabs.
   1585   int index = [tabStripController_ modelIndexForTabView:tabView];
   1586   WebContents* contents = browser_->tab_strip_model()->GetWebContentsAt(index);
   1587   if (!contents)
   1588     return NO;
   1589   return !WebContentsModalDialogManager::FromWebContents(contents)->
   1590       IsShowingDialog();
   1591 }
   1592 
   1593 // TabStripControllerDelegate protocol.
   1594 - (void)onActivateTabWithContents:(WebContents*)contents {
   1595   // Update various elements that are interested in knowing the current
   1596   // WebContents.
   1597 
   1598   // Update all the UI bits.
   1599   windowShim_->UpdateTitleBar();
   1600 
   1601   [devToolsController_ updateDevToolsForWebContents:contents
   1602                                         withProfile:browser_->profile()];
   1603 
   1604   // Update the bookmark bar.
   1605   // Must do it after devtools updates, otherwise bookmark bar might
   1606   // call resizeView -> layoutSubviews and cause unnecessary relayout.
   1607   // TODO(viettrungluu): perhaps update to not terminate running animations (if
   1608   // applicable)?
   1609   windowShim_->BookmarkBarStateChanged(
   1610       BookmarkBar::DONT_ANIMATE_STATE_CHANGE);
   1611 
   1612   [infoBarContainerController_ changeWebContents:contents];
   1613 
   1614   [self updateAllowOverlappingViews:[self inPresentationMode]];
   1615 }
   1616 
   1617 - (void)onTabChanged:(TabStripModelObserver::TabChangeType)change
   1618         withContents:(WebContents*)contents {
   1619   // Update titles if this is the currently selected tab and if it isn't just
   1620   // the loading state which changed.
   1621   if (change != TabStripModelObserver::LOADING_ONLY)
   1622     windowShim_->UpdateTitleBar();
   1623 
   1624   // Update the bookmark bar if this is the currently selected tab and if it
   1625   // isn't just the title which changed. This for transitions between the NTP
   1626   // (showing its floating bookmark bar) and normal web pages (showing no
   1627   // bookmark bar).
   1628   // TODO(viettrungluu): perhaps update to not terminate running animations?
   1629   if (change != TabStripModelObserver::TITLE_NOT_LOADING) {
   1630     windowShim_->BookmarkBarStateChanged(
   1631         BookmarkBar::DONT_ANIMATE_STATE_CHANGE);
   1632   }
   1633 }
   1634 
   1635 - (void)onTabDetachedWithContents:(WebContents*)contents {
   1636   [infoBarContainerController_ tabDetachedWithContents:contents];
   1637 }
   1638 
   1639 - (void)userChangedTheme {
   1640   NSView* contentView = [[self window] contentView];
   1641   [[contentView superview] cr_recursivelySetNeedsDisplay:YES];
   1642 }
   1643 
   1644 - (ui::ThemeProvider*)themeProvider {
   1645   return ThemeServiceFactory::GetForProfile(browser_->profile());
   1646 }
   1647 
   1648 - (ThemedWindowStyle)themedWindowStyle {
   1649   ThemedWindowStyle style = 0;
   1650   if (browser_->profile()->IsOffTheRecord())
   1651     style |= THEMED_INCOGNITO;
   1652 
   1653   if (browser_->is_devtools())
   1654     style |= THEMED_DEVTOOLS;
   1655   if (browser_->is_type_popup())
   1656     style |= THEMED_POPUP;
   1657 
   1658   return style;
   1659 }
   1660 
   1661 - (NSPoint)themePatternPhaseForAlignment:(ThemePatternAlignment)alignment {
   1662   NSView* windowChromeView = [[[self window] contentView] superview];
   1663   NSView* tabStripView = nil;
   1664   if (alignment == THEME_PATTERN_ALIGN_WITH_TAB_STRIP && [self hasTabStrip])
   1665     tabStripView = [self tabStripView];
   1666   return [BrowserWindowUtils themePatternPhaseFor:windowChromeView
   1667                                      withTabStrip:tabStripView];
   1668 }
   1669 
   1670 - (NSPoint)bookmarkBubblePoint {
   1671   return [toolbarController_ bookmarkBubblePoint];
   1672 }
   1673 
   1674 // Show the bookmark bubble (e.g. user just clicked on the STAR).
   1675 - (void)showBookmarkBubbleForURL:(const GURL&)url
   1676                alreadyBookmarked:(BOOL)alreadyMarked {
   1677   if (!bookmarkBubbleController_) {
   1678     BookmarkModel* model =
   1679         BookmarkModelFactory::GetForProfile(browser_->profile());
   1680     const BookmarkNode* node = model->GetMostRecentlyAddedNodeForURL(url);
   1681     bookmarkBubbleController_ =
   1682         [[BookmarkBubbleController alloc] initWithParentWindow:[self window]
   1683                                                          model:model
   1684                                                           node:node
   1685                                              alreadyBookmarked:alreadyMarked];
   1686     [bookmarkBubbleController_ showWindow:self];
   1687     NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
   1688     [center addObserver:self
   1689                selector:@selector(bookmarkBubbleWindowWillClose:)
   1690                    name:NSWindowWillCloseNotification
   1691                  object:[bookmarkBubbleController_ window]];
   1692   }
   1693 }
   1694 
   1695 // Nil out the weak bookmark bubble controller reference.
   1696 - (void)bookmarkBubbleWindowWillClose:(NSNotification*)notification {
   1697   DCHECK_EQ([notification object], [bookmarkBubbleController_ window]);
   1698 
   1699   NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
   1700   [center removeObserver:self
   1701                     name:NSWindowWillCloseNotification
   1702                   object:[bookmarkBubbleController_ window]];
   1703   bookmarkBubbleController_ = nil;
   1704 }
   1705 
   1706 // Handle the editBookmarkNode: action sent from bookmark bubble controllers.
   1707 - (void)editBookmarkNode:(id)sender {
   1708   BOOL responds = [sender respondsToSelector:@selector(node)];
   1709   DCHECK(responds);
   1710   if (responds) {
   1711     const BookmarkNode* node = [sender node];
   1712     if (node)
   1713       BookmarkEditor::Show([self window], browser_->profile(),
   1714           BookmarkEditor::EditDetails::EditNode(node),
   1715           BookmarkEditor::SHOW_TREE);
   1716   }
   1717 }
   1718 
   1719 // If the browser is in incognito mode or has multi-profiles, install the image
   1720 // view to decorate the window at the upper right. Use the same base y
   1721 // coordinate as the tab strip.
   1722 - (void)installAvatar {
   1723   // Install the image into the badge view. Hide it for now; positioning and
   1724   // sizing will be done by the layout code. The AvatarButton will choose which
   1725   // image to display based on the browser.
   1726   avatarButtonController_.reset(
   1727       [[AvatarButtonController alloc] initWithBrowser:browser_.get()]);
   1728 
   1729   NSView* view = [avatarButtonController_ view];
   1730   [view setAutoresizingMask:NSViewMinXMargin | NSViewMinYMargin];
   1731   [view setHidden:![self shouldShowAvatar]];
   1732 
   1733   // Install the view.
   1734   [[[[self window] contentView] superview] addSubview:view];
   1735 }
   1736 
   1737 // Called when we get a three-finger swipe.
   1738 - (void)swipeWithEvent:(NSEvent*)event {
   1739   CGFloat deltaX = [event deltaX];
   1740   CGFloat deltaY = [event deltaY];
   1741 
   1742   // Map forwards and backwards to history; left is positive, right is negative.
   1743   unsigned int command = 0;
   1744   if (deltaX > 0.5) {
   1745     command = IDC_BACK;
   1746   } else if (deltaX < -0.5) {
   1747     command = IDC_FORWARD;
   1748   } else if (deltaY > 0.5) {
   1749     // TODO(pinkerton): figure out page-up, http://crbug.com/16305
   1750   } else if (deltaY < -0.5) {
   1751     // TODO(pinkerton): figure out page-down, http://crbug.com/16305
   1752     chrome::ExecuteCommand(browser_.get(), IDC_TABPOSE);
   1753   }
   1754 
   1755   // Ensure the command is valid first (ExecuteCommand() won't do that) and
   1756   // then make it so.
   1757   if (chrome::IsCommandEnabled(browser_.get(), command)) {
   1758     chrome::ExecuteCommandWithDisposition(
   1759         browser_.get(),
   1760         command,
   1761         ui::WindowOpenDispositionFromNSEvent(event));
   1762   }
   1763 }
   1764 
   1765 // Called repeatedly during a pinch gesture, with incremental change values.
   1766 - (void)magnifyWithEvent:(NSEvent*)event {
   1767   // The deltaZ difference necessary to trigger a zoom action. Derived from
   1768   // experimentation to find a value that feels reasonable.
   1769   const float kZoomStepValue = 0.6;
   1770 
   1771   // Find the (absolute) thresholds on either side of the current zoom factor,
   1772   // then convert those to actual numbers to trigger a zoom in or out.
   1773   // This logic deliberately makes the range around the starting zoom value for
   1774   // the gesture twice as large as the other ranges (i.e., the notches are at
   1775   // ..., -3*step, -2*step, -step, step, 2*step, 3*step, ... but not at 0)
   1776   // so that it's easier to get back to your starting point than it is to
   1777   // overshoot.
   1778   float nextStep = (abs(currentZoomStepDelta_) + 1) * kZoomStepValue;
   1779   float backStep = abs(currentZoomStepDelta_) * kZoomStepValue;
   1780   float zoomInThreshold = (currentZoomStepDelta_ >= 0) ? nextStep : -backStep;
   1781   float zoomOutThreshold = (currentZoomStepDelta_ <= 0) ? -nextStep : backStep;
   1782 
   1783   unsigned int command = 0;
   1784   totalMagnifyGestureAmount_ += [event magnification];
   1785   if (totalMagnifyGestureAmount_ > zoomInThreshold) {
   1786     command = IDC_ZOOM_PLUS;
   1787   } else if (totalMagnifyGestureAmount_ < zoomOutThreshold) {
   1788     command = IDC_ZOOM_MINUS;
   1789   }
   1790 
   1791   if (command && chrome::IsCommandEnabled(browser_.get(), command)) {
   1792     currentZoomStepDelta_ += (command == IDC_ZOOM_PLUS) ? 1 : -1;
   1793     chrome::ExecuteCommandWithDisposition(
   1794         browser_.get(),
   1795         command,
   1796         ui::WindowOpenDispositionFromNSEvent(event));
   1797   }
   1798 }
   1799 
   1800 // Delegate method called when window is resized.
   1801 - (void)windowDidResize:(NSNotification*)notification {
   1802   [self saveWindowPositionIfNeeded];
   1803 
   1804   // Resize (and possibly move) the status bubble. Note that we may get called
   1805   // when the status bubble does not exist.
   1806   if (statusBubble_) {
   1807     statusBubble_->UpdateSizeAndPosition();
   1808   }
   1809 
   1810   // Let the selected RenderWidgetHostView know, so that it can tell plugins.
   1811   if (WebContents* contents =
   1812           browser_->tab_strip_model()->GetActiveWebContents()) {
   1813     if (RenderWidgetHostView* rwhv = contents->GetRenderWidgetHostView())
   1814       rwhv->WindowFrameChanged();
   1815   }
   1816 
   1817   // The FindBar needs to know its own position to properly detect overlaps
   1818   // with find results. The position changes whenever the window is resized,
   1819   // and |layoutSubviews| computes the FindBar's position.
   1820   // TODO: calling |layoutSubviews| here is a waste, find a better way to
   1821   // do this.
   1822   if ([findBarCocoaController_ isFindBarVisible])
   1823     [self layoutSubviews];
   1824 }
   1825 
   1826 // Handle the openLearnMoreAboutCrashLink: action from SadTabController when
   1827 // "Learn more" link in "Aw snap" page (i.e. crash page or sad tab) is
   1828 // clicked. Decoupling the action from its target makes unit testing possible.
   1829 - (void)openLearnMoreAboutCrashLink:(id)sender {
   1830   if ([sender isKindOfClass:[SadTabController class]]) {
   1831     SadTabController* sad_tab = static_cast<SadTabController*>(sender);
   1832     WebContents* web_contents = [sad_tab webContents];
   1833     if (web_contents) {
   1834       OpenURLParams params(
   1835           GURL(chrome::kCrashReasonURL), Referrer(), CURRENT_TAB,
   1836           content::PAGE_TRANSITION_LINK, false);
   1837       web_contents->OpenURL(params);
   1838     }
   1839   }
   1840 }
   1841 
   1842 // Delegate method called when window did move. (See below for why we don't use
   1843 // |-windowWillMove:|, which is called less frequently than |-windowDidMove|
   1844 // instead.)
   1845 - (void)windowDidMove:(NSNotification*)notification {
   1846   [self saveWindowPositionIfNeeded];
   1847 
   1848   NSWindow* window = [self window];
   1849   NSRect windowFrame = [window frame];
   1850   NSRect workarea = [[window screen] visibleFrame];
   1851 
   1852   // We reset the window growth state whenever the window is moved out of the
   1853   // work area or away (up or down) from the bottom or top of the work area.
   1854   // Unfortunately, Cocoa sends |-windowWillMove:| too frequently (including
   1855   // when clicking on the title bar to activate), and of course
   1856   // |-windowWillMove| is called too early for us to apply our heuristic. (The
   1857   // heuristic we use for detecting window movement is that if |windowTopGrowth_
   1858   // > 0|, then we should be at the bottom of the work area -- if we're not,
   1859   // we've moved. Similarly for the other side.)
   1860   if (!NSContainsRect(workarea, windowFrame) ||
   1861       (windowTopGrowth_ > 0 && NSMinY(windowFrame) != NSMinY(workarea)) ||
   1862       (windowBottomGrowth_ > 0 && NSMaxY(windowFrame) != NSMaxY(workarea)))
   1863     [self resetWindowGrowthState];
   1864 
   1865   // Let the selected RenderWidgetHostView know, so that it can tell plugins.
   1866   if (WebContents* contents =
   1867           browser_->tab_strip_model()->GetActiveWebContents()) {
   1868     if (RenderWidgetHostView* rwhv = contents->GetRenderWidgetHostView())
   1869       rwhv->WindowFrameChanged();
   1870   }
   1871 }
   1872 
   1873 // Delegate method called when window will be resized; not called for
   1874 // |-setFrame:display:|.
   1875 - (NSSize)windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize {
   1876   [self resetWindowGrowthState];
   1877   return frameSize;
   1878 }
   1879 
   1880 // Delegate method: see |NSWindowDelegate| protocol.
   1881 - (id)windowWillReturnFieldEditor:(NSWindow*)sender toObject:(id)obj {
   1882   // Ask the toolbar controller if it wants to return a custom field editor
   1883   // for the specific object.
   1884   return [toolbarController_ customFieldEditorForObject:obj];
   1885 }
   1886 
   1887 // (Needed for |BookmarkBarControllerDelegate| protocol.)
   1888 - (void)bookmarkBar:(BookmarkBarController*)controller
   1889  didChangeFromState:(BookmarkBar::State)oldState
   1890             toState:(BookmarkBar::State)newState {
   1891   [toolbarController_ setDividerOpacity:[self toolbarDividerOpacity]];
   1892   [self adjustToolbarAndBookmarkBarForCompression:
   1893           [controller getDesiredToolbarHeightCompression]];
   1894 }
   1895 
   1896 // (Needed for |BookmarkBarControllerDelegate| protocol.)
   1897 - (void)bookmarkBar:(BookmarkBarController*)controller
   1898 willAnimateFromState:(BookmarkBar::State)oldState
   1899             toState:(BookmarkBar::State)newState {
   1900   [toolbarController_ setDividerOpacity:[self toolbarDividerOpacity]];
   1901   [self adjustToolbarAndBookmarkBarForCompression:
   1902           [controller getDesiredToolbarHeightCompression]];
   1903 }
   1904 
   1905 // (Private/TestingAPI)
   1906 - (void)resetWindowGrowthState {
   1907   windowTopGrowth_ = 0;
   1908   windowBottomGrowth_ = 0;
   1909   isShrinkingFromZoomed_ = NO;
   1910 }
   1911 
   1912 - (NSSize)overflowFrom:(NSRect)source
   1913                     to:(NSRect)target {
   1914   // If |source|'s boundary is outside of |target|'s, set its distance
   1915   // to |x|.  Note that |source| can overflow to both side, but we
   1916   // have nothing to do for such case.
   1917   CGFloat x = 0;
   1918   if (NSMaxX(target) < NSMaxX(source)) // |source| overflows to right
   1919     x = NSMaxX(source) - NSMaxX(target);
   1920   else if (NSMinX(source) < NSMinX(target)) // |source| overflows to left
   1921     x = NSMinX(source) - NSMinX(target);
   1922 
   1923   // Same as |x| above.
   1924   CGFloat y = 0;
   1925   if (NSMaxY(target) < NSMaxY(source))
   1926     y = NSMaxY(source) - NSMaxY(target);
   1927   else if (NSMinY(source) < NSMinY(target))
   1928     y = NSMinY(source) - NSMinY(target);
   1929 
   1930   return NSMakeSize(x, y);
   1931 }
   1932 
   1933 // (Private/TestingAPI)
   1934 - (FullscreenExitBubbleController*)fullscreenExitBubbleController {
   1935   return fullscreenExitBubbleController_.get();
   1936 }
   1937 
   1938 - (NSRect)omniboxPopupAnchorRect {
   1939   // Start with toolbar rect.
   1940   NSView* toolbarView = [toolbarController_ view];
   1941   NSRect anchorRect = [toolbarView frame];
   1942 
   1943   // Adjust to account for height and possible bookmark bar. Compress by 1
   1944   // to account for the separator.
   1945   anchorRect.origin.y =
   1946       NSMaxY(anchorRect) - [toolbarController_ desiredHeightForCompression:1];
   1947 
   1948   // Shift to window base coordinates.
   1949   return [[toolbarView superview] convertRect:anchorRect toView:nil];
   1950 }
   1951 
   1952 - (void)sheetDidEnd:(NSWindow*)sheet
   1953          returnCode:(NSInteger)code
   1954             context:(void*)context {
   1955   [sheet orderOut:self];
   1956 }
   1957 
   1958 - (void)onFindBarVisibilityChanged {
   1959   [self updateAllowOverlappingViews:[self inPresentationMode]];
   1960 }
   1961 
   1962 - (void)onOverlappedViewShown {
   1963   ++overlappedViewCount_;
   1964   [self updateAllowOverlappingViews:[self inPresentationMode]];
   1965 }
   1966 
   1967 - (void)onOverlappedViewHidden {
   1968   --overlappedViewCount_;
   1969   [self updateAllowOverlappingViews:[self inPresentationMode]];
   1970 }
   1971 
   1972 @end  // @implementation BrowserWindowController
   1973 
   1974 
   1975 @implementation BrowserWindowController(Fullscreen)
   1976 
   1977 - (void)handleLionToggleFullscreen {
   1978   DCHECK(base::mac::IsOSLionOrLater());
   1979   chrome::ExecuteCommand(browser_.get(), IDC_FULLSCREEN);
   1980 }
   1981 
   1982 // On Lion, this method is called by either the Lion fullscreen button or the
   1983 // "Enter Full Screen" menu item.  On Snow Leopard, this function is never
   1984 // called by the UI directly, but it provides the implementation for
   1985 // |-setPresentationMode:|.
   1986 - (void)setFullscreen:(BOOL)fullscreen {
   1987   if (fullscreen == [self isFullscreen])
   1988     return;
   1989 
   1990   if (!chrome::IsCommandEnabled(browser_.get(), IDC_FULLSCREEN))
   1991     return;
   1992 
   1993   if (chrome::mac::SupportsSystemFullscreen() && !fullscreenWindow_) {
   1994     enteredPresentationModeFromFullscreen_ = YES;
   1995     if ([[self window] isKindOfClass:[FramedBrowserWindow class]])
   1996       [static_cast<FramedBrowserWindow*>([self window]) toggleSystemFullScreen];
   1997   } else {
   1998     if (fullscreen)
   1999       [self enterFullscreenForSnowLeopard];
   2000     else
   2001       [self exitFullscreenForSnowLeopard];
   2002   }
   2003 }
   2004 
   2005 - (void)enterFullscreen {
   2006   [self setFullscreen:YES];
   2007 }
   2008 
   2009 - (void)exitFullscreen {
   2010   [self setFullscreen:NO];
   2011 }
   2012 
   2013 - (void)updateFullscreenExitBubbleURL:(const GURL&)url
   2014                            bubbleType:(FullscreenExitBubbleType)bubbleType {
   2015   fullscreenUrl_ = url;
   2016   fullscreenBubbleType_ = bubbleType;
   2017   [self layoutSubviews];
   2018   [self showFullscreenExitBubbleIfNecessary];
   2019 }
   2020 
   2021 - (BOOL)isFullscreen {
   2022   return (fullscreenWindow_.get() != nil) ||
   2023          ([[self window] styleMask] & NSFullScreenWindowMask) ||
   2024          enteringFullscreen_;
   2025 }
   2026 
   2027 // On Lion, this function is called by either the presentation mode toggle
   2028 // button or the "Enter Presentation Mode" menu item.  In the latter case, this
   2029 // function also triggers the Lion machinery to enter fullscreen mode as well as
   2030 // set presentation mode.  On Snow Leopard, this function is called by the
   2031 // "Enter Presentation Mode" menu item, and triggering presentation mode always
   2032 // moves the user into fullscreen mode.
   2033 - (void)setPresentationMode:(BOOL)presentationMode
   2034                         url:(const GURL&)url
   2035                  bubbleType:(FullscreenExitBubbleType)bubbleType {
   2036   fullscreenUrl_ = url;
   2037   fullscreenBubbleType_ = bubbleType;
   2038 
   2039   // Presentation mode on systems without fullscreen support maps directly to
   2040   // fullscreen mode.
   2041   if (!chrome::mac::SupportsSystemFullscreen()) {
   2042     [self setFullscreen:presentationMode];
   2043     return;
   2044   }
   2045 
   2046   if (presentationMode) {
   2047     BOOL fullscreen = [self isFullscreen];
   2048     enteredPresentationModeFromFullscreen_ = fullscreen;
   2049     enteringPresentationMode_ = YES;
   2050 
   2051     if (fullscreen) {
   2052       // If already in fullscreen mode, just toggle the presentation mode
   2053       // setting.  Go through an elaborate dance to force the overlay to show,
   2054       // then animate out once the mouse moves away.  This helps draw attention
   2055       // to the fact that the UI is in an overlay.  Focus the tab contents
   2056       // because the omnibox is the most likely source of bar visibility locks,
   2057       // and taking focus away from the omnibox releases its lock.
   2058       [self lockBarVisibilityForOwner:self withAnimation:NO delay:NO];
   2059       [self focusTabContents];
   2060       [self setPresentationModeInternal:YES forceDropdown:YES];
   2061       [self releaseBarVisibilityForOwner:self withAnimation:YES delay:YES];
   2062       // Since -windowDidEnterFullScreen: won't be called in the
   2063       // fullscreen --> presentation mode case, manually show the exit bubble
   2064       // and notify the change happened with WindowFullscreenStateChanged().
   2065       [self showFullscreenExitBubbleIfNecessary];
   2066       browser_->WindowFullscreenStateChanged();
   2067     } else {
   2068       // If not in fullscreen mode, trigger the Lion fullscreen mode machinery.
   2069       // Presentation mode will automatically be enabled in
   2070       // |-windowWillEnterFullScreen:|.
   2071       NSWindow* window = [self window];
   2072       if ([window isKindOfClass:[FramedBrowserWindow class]])
   2073         [static_cast<FramedBrowserWindow*>(window) toggleSystemFullScreen];
   2074     }
   2075   } else {
   2076     // Exiting presentation mode does not exit system fullscreen; it merely
   2077     // switches from presentation mode to normal fullscreen.
   2078     [self setPresentationModeInternal:NO forceDropdown:NO];
   2079 
   2080     // Since -windowDidExitFullScreen: won't be called in the
   2081     // presentation mode --> normal fullscreen case, manually show the exit
   2082     // bubble and notify the change happened with
   2083     // WindowFullscreenStateChanged().
   2084     [self showFullscreenExitBubbleIfNecessary];
   2085     browser_->WindowFullscreenStateChanged();
   2086   }
   2087 }
   2088 
   2089 - (void)enterPresentationModeForURL:(const GURL&)url
   2090                          bubbleType:(FullscreenExitBubbleType)bubbleType {
   2091   [self setPresentationMode:YES url:url bubbleType:bubbleType];
   2092 }
   2093 
   2094 - (void)exitPresentationMode {
   2095   // url: and bubbleType: are ignored when leaving presentation mode.
   2096   [self setPresentationMode:NO url:GURL() bubbleType:FEB_TYPE_NONE];
   2097 }
   2098 
   2099 - (void)enterFullscreenForURL:(const GURL&)url
   2100                    bubbleType:(FullscreenExitBubbleType)bubbleType {
   2101   // This method may only be called in simplified fullscreen mode.
   2102   const CommandLine* command_line = CommandLine::ForCurrentProcess();
   2103   DCHECK(command_line->HasSwitch(switches::kEnableSimplifiedFullscreen));
   2104 
   2105   [self enterFullscreenForSnowLeopard];
   2106   [self updateFullscreenExitBubbleURL:url bubbleType:bubbleType];
   2107 }
   2108 
   2109 - (BOOL)inPresentationMode {
   2110   return presentationModeController_.get() &&
   2111       [presentationModeController_ inPresentationMode];
   2112 }
   2113 
   2114 - (void)resizeFullscreenWindow {
   2115   DCHECK([self isFullscreen]);
   2116   if (![self isFullscreen])
   2117     return;
   2118 
   2119   NSWindow* window = [self window];
   2120   [window setFrame:[[window screen] frame] display:YES];
   2121   [self layoutSubviews];
   2122 }
   2123 
   2124 - (CGFloat)floatingBarShownFraction {
   2125   return floatingBarShownFraction_;
   2126 }
   2127 
   2128 - (void)setFloatingBarShownFraction:(CGFloat)fraction {
   2129   floatingBarShownFraction_ = fraction;
   2130   [self layoutSubviews];
   2131 }
   2132 
   2133 - (BOOL)isBarVisibilityLockedForOwner:(id)owner {
   2134   DCHECK(owner);
   2135   DCHECK(barVisibilityLocks_);
   2136   return [barVisibilityLocks_ containsObject:owner];
   2137 }
   2138 
   2139 - (void)lockBarVisibilityForOwner:(id)owner
   2140                     withAnimation:(BOOL)animate
   2141                             delay:(BOOL)delay {
   2142   if (![self isBarVisibilityLockedForOwner:owner]) {
   2143     [barVisibilityLocks_ addObject:owner];
   2144 
   2145     // If enabled, show the overlay if necessary (and if in presentation mode).
   2146     if (barVisibilityUpdatesEnabled_) {
   2147       [presentationModeController_ ensureOverlayShownWithAnimation:animate
   2148                                                              delay:delay];
   2149     }
   2150   }
   2151 }
   2152 
   2153 - (void)releaseBarVisibilityForOwner:(id)owner
   2154                        withAnimation:(BOOL)animate
   2155                                delay:(BOOL)delay {
   2156   if ([self isBarVisibilityLockedForOwner:owner]) {
   2157     [barVisibilityLocks_ removeObject:owner];
   2158 
   2159     // If enabled, hide the overlay if necessary (and if in presentation mode).
   2160     if (barVisibilityUpdatesEnabled_ &&
   2161         ![barVisibilityLocks_ count]) {
   2162       [presentationModeController_ ensureOverlayHiddenWithAnimation:animate
   2163                                                               delay:delay];
   2164     }
   2165   }
   2166 }
   2167 
   2168 - (BOOL)floatingBarHasFocus {
   2169   NSResponder* focused = [[self window] firstResponder];
   2170   return [focused isKindOfClass:[AutocompleteTextFieldEditor class]];
   2171 }
   2172 
   2173 - (void)tabposeWillClose:(NSNotification*)notif {
   2174   // Re-show the container after Tabpose closes.
   2175   [[infoBarContainerController_ view] setHidden:NO];
   2176 }
   2177 
   2178 - (void)openTabpose {
   2179   NSUInteger modifierFlags = [[NSApp currentEvent] modifierFlags];
   2180   BOOL slomo = (modifierFlags & NSShiftKeyMask) != 0;
   2181 
   2182   // Cover info bars, inspector window, and detached bookmark bar on NTP.
   2183   // Do not cover download shelf.
   2184   NSRect activeArea = [[self tabContentArea] frame];
   2185   // Take out the anti-spoof height so that Tabpose doesn't draw on top of the
   2186   // browser chrome.
   2187   activeArea.size.height +=
   2188       NSHeight([[infoBarContainerController_ view] frame]) -
   2189           [infoBarContainerController_ overlappingTipHeight];
   2190   if ([self isBookmarkBarVisible] && [self placeBookmarkBarBelowInfoBar]) {
   2191     NSView* bookmarkBarView = [bookmarkBarController_ view];
   2192     activeArea.size.height += NSHeight([bookmarkBarView frame]);
   2193   }
   2194 
   2195   // Hide the infobar container so that the anti-spoof bulge doesn't show when
   2196   // Tabpose is open.
   2197   [[infoBarContainerController_ view] setHidden:YES];
   2198 
   2199   TabposeWindow* window =
   2200       [TabposeWindow openTabposeFor:[self window]
   2201                                rect:activeArea
   2202                               slomo:slomo
   2203                       tabStripModel:browser_->tab_strip_model()];
   2204 
   2205   // When the Tabpose window closes, the infobar container needs to be made
   2206   // visible again.
   2207   NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
   2208   [center addObserver:self
   2209              selector:@selector(tabposeWillClose:)
   2210                  name:NSWindowWillCloseNotification
   2211                object:window];
   2212 }
   2213 
   2214 @end  // @implementation BrowserWindowController(Fullscreen)
   2215 
   2216 
   2217 @implementation BrowserWindowController(WindowType)
   2218 
   2219 - (BOOL)supportsWindowFeature:(int)feature {
   2220   return browser_->SupportsWindowFeature(
   2221       static_cast<Browser::WindowFeature>(feature));
   2222 }
   2223 
   2224 - (BOOL)hasTitleBar {
   2225   return [self supportsWindowFeature:Browser::FEATURE_TITLEBAR];
   2226 }
   2227 
   2228 - (BOOL)hasToolbar {
   2229   return [self supportsWindowFeature:Browser::FEATURE_TOOLBAR];
   2230 }
   2231 
   2232 - (BOOL)hasLocationBar {
   2233   return [self supportsWindowFeature:Browser::FEATURE_LOCATIONBAR];
   2234 }
   2235 
   2236 - (BOOL)supportsBookmarkBar {
   2237   return [self supportsWindowFeature:Browser::FEATURE_BOOKMARKBAR];
   2238 }
   2239 
   2240 - (BOOL)isTabbedWindow {
   2241   return browser_->is_type_tabbed();
   2242 }
   2243 
   2244 @end  // @implementation BrowserWindowController(WindowType)
   2245