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