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