d1bdcb1d968cbd02aa40feec129ef74a493a06fa
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / cocoa / browser_window_controller.mm
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/location_bar/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
99 using l10n_util::GetStringUTF16;
100 using l10n_util::GetNSStringWithFixup;
101 using 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
177 using content::OpenURLParams;
178 using content::Referrer;
179 using content::RenderWidgetHostView;
180 using content::WebContents;
181 using 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] 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 // Update a toggle state for an NSMenuItem if modified.
1018 // Take care to ensure |item| looks like a NSMenuItem.
1019 // Called by validateUserInterfaceItem:.
1020 - (void)updateToggleStateWithTag:(NSInteger)tag forItem:(id)item {
1021   if (![item respondsToSelector:@selector(state)] ||
1022       ![item respondsToSelector:@selector(setState:)])
1023     return;
1024
1025   // On Windows this logic happens in bookmark_bar_view.cc.  On the
1026   // Mac we're a lot more MVC happy so we've moved it into a
1027   // controller.  To be clear, this simply updates the menu item; it
1028   // does not display the bookmark bar itself.
1029   if (tag == IDC_SHOW_BOOKMARK_BAR) {
1030     bool toggled = windowShim_->IsBookmarkBarVisible();
1031     NSInteger oldState = [(NSMenuItem*)item state];
1032     NSInteger newState = toggled ? NSOnState : NSOffState;
1033     if (oldState != newState)
1034       [item setState:newState];
1035   }
1036
1037   // Update the checked/Unchecked state of items in the encoding menu.
1038   // On Windows, this logic is part of |EncodingMenuModel| in
1039   // browser/ui/views/toolbar_view.h.
1040   EncodingMenuController encoding_controller;
1041   if (encoding_controller.DoesCommandBelongToEncodingMenu(tag)) {
1042     DCHECK(browser_.get());
1043     Profile* profile = browser_->profile();
1044     DCHECK(profile);
1045     WebContents* current_tab =
1046         browser_->tab_strip_model()->GetActiveWebContents();
1047     if (!current_tab)
1048       return;
1049
1050     const std::string encoding = current_tab->GetEncoding();
1051
1052     bool toggled = encoding_controller.IsItemChecked(profile, encoding, tag);
1053     NSInteger oldState = [(NSMenuItem*)item state];
1054     NSInteger newState = toggled ? NSOnState : NSOffState;
1055     if (oldState != newState)
1056       [item setState:newState];
1057   }
1058 }
1059
1060 // Called to validate menu and toolbar items when this window is key. All the
1061 // items we care about have been set with the |-commandDispatch:| or
1062 // |-commandDispatchUsingKeyModifiers:| actions and a target of FirstResponder
1063 // in IB. If it's not one of those, let it continue up the responder chain to be
1064 // handled elsewhere. We pull out the tag as the cross-platform constant to
1065 // differentiate and dispatch the various commands.
1066 // NOTE: we might have to handle state for app-wide menu items,
1067 // although we could cheat and directly ask the app controller if our
1068 // command_updater doesn't support the command. This may or may not be an issue,
1069 // too early to tell.
1070 - (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item {
1071   SEL action = [item action];
1072   BOOL enable = NO;
1073   if (action == @selector(commandDispatch:) ||
1074       action == @selector(commandDispatchUsingKeyModifiers:)) {
1075     NSInteger tag = [item tag];
1076     if (chrome::SupportsCommand(browser_.get(), tag)) {
1077       // Generate return value (enabled state)
1078       enable = chrome::IsCommandEnabled(browser_.get(), tag);
1079       switch (tag) {
1080         case IDC_CLOSE_TAB:
1081           // Disable "close tab" if the receiving window is not tabbed.
1082           // We simply check whether the item has a keyboard shortcut set here;
1083           // app_controller_mac.mm actually determines whether the item should
1084           // be enabled.
1085           if (NSMenuItem* menuItem = base::mac::ObjCCast<NSMenuItem>(item))
1086             enable &= !![[menuItem keyEquivalent] length];
1087           break;
1088         case IDC_FULLSCREEN: {
1089           if (NSMenuItem* menuItem = base::mac::ObjCCast<NSMenuItem>(item)) {
1090             NSString* menuTitle = l10n_util::GetNSString(
1091                 [self isFullscreen] && ![self inPresentationMode] ?
1092                     IDS_EXIT_FULLSCREEN_MAC :
1093                     IDS_ENTER_FULLSCREEN_MAC);
1094             [menuItem setTitle:menuTitle];
1095
1096             if (!chrome::mac::SupportsSystemFullscreen())
1097               [menuItem setHidden:YES];
1098           }
1099           break;
1100         }
1101         case IDC_PRESENTATION_MODE: {
1102           if (NSMenuItem* menuItem = base::mac::ObjCCast<NSMenuItem>(item)) {
1103             NSString* menuTitle = l10n_util::GetNSString(
1104                 [self inPresentationMode] ? IDS_EXIT_PRESENTATION_MAC :
1105                                             IDS_ENTER_PRESENTATION_MAC);
1106             [menuItem setTitle:menuTitle];
1107           }
1108           break;
1109         }
1110         case IDC_SHOW_SIGNIN: {
1111           Profile* original_profile =
1112               browser_->profile()->GetOriginalProfile();
1113           [BrowserWindowController updateSigninItem:item
1114                                          shouldShow:enable
1115                                      currentProfile:original_profile];
1116           break;
1117         }
1118         case IDC_BOOKMARK_PAGE: {
1119           // Extensions have the ability to hide the bookmark page menu item.
1120           // This only affects the bookmark page menu item under the main menu.
1121           // The bookmark page menu item under the wrench menu has its
1122           // visibility controlled by WrenchMenuModel.
1123           bool shouldHide =
1124               chrome::ShouldRemoveBookmarkThisPageUI(browser_->profile());
1125           NSMenuItem* menuItem = base::mac::ObjCCast<NSMenuItem>(item);
1126           [menuItem setHidden:shouldHide];
1127           break;
1128         }
1129         case IDC_BOOKMARK_ALL_TABS: {
1130           // Extensions have the ability to hide the bookmark all tabs menu
1131           // item.  This only affects the bookmark page menu item under the main
1132           // menu.  The bookmark page menu item under the wrench menu has its
1133           // visibility controlled by WrenchMenuModel.
1134           bool shouldHide =
1135               chrome::ShouldRemoveBookmarkOpenPagesUI(browser_->profile());
1136           NSMenuItem* menuItem = base::mac::ObjCCast<NSMenuItem>(item);
1137           [menuItem setHidden:shouldHide];
1138           break;
1139         }
1140         default:
1141           // Special handling for the contents of the Text Encoding submenu. On
1142           // Mac OS, instead of enabling/disabling the top-level menu item, we
1143           // enable/disable the submenu's contents (per Apple's HIG).
1144           EncodingMenuController encoding_controller;
1145           if (encoding_controller.DoesCommandBelongToEncodingMenu(tag)) {
1146             enable &= chrome::IsCommandEnabled(browser_.get(),
1147                                                IDC_ENCODING_MENU) ? YES : NO;
1148           }
1149       }
1150
1151       // If the item is toggleable, find its toggle state and
1152       // try to update it.  This is a little awkward, but the alternative is
1153       // to check after a commandDispatch, which seems worse.
1154       [self updateToggleStateWithTag:tag forItem:item];
1155     }
1156   }
1157   return enable;
1158 }
1159
1160 // Called when the user picks a menu or toolbar item when this window is key.
1161 // Calls through to the browser object to execute the command. This assumes that
1162 // the command is supported and doesn't check, otherwise it would have been
1163 // disabled in the UI in validateUserInterfaceItem:.
1164 - (void)commandDispatch:(id)sender {
1165   DCHECK(sender);
1166   // Identify the actual BWC to which the command should be dispatched. It might
1167   // belong to a background window, yet this controller gets it because it is
1168   // the foreground window's controller and thus in the responder chain. Some
1169   // senders don't have this problem (for example, menus only operate on the
1170   // foreground window), so this is only an issue for senders that are part of
1171   // windows.
1172   BrowserWindowController* targetController = self;
1173   if ([sender respondsToSelector:@selector(window)])
1174     targetController = [[sender window] windowController];
1175   DCHECK([targetController isKindOfClass:[BrowserWindowController class]]);
1176   DCHECK(targetController->browser_.get());
1177   chrome::ExecuteCommand(targetController->browser_.get(), [sender tag]);
1178 }
1179
1180 // Same as |-commandDispatch:|, but executes commands using a disposition
1181 // determined by the key flags. If the window is in the background and the
1182 // command key is down, ignore the command key, but process any other modifiers.
1183 - (void)commandDispatchUsingKeyModifiers:(id)sender {
1184   DCHECK(sender);
1185
1186   if (![sender isEnabled]) {
1187     // This code is reachable e.g. if the user mashes the back button, queuing
1188     // up a bunch of events before the button's enabled state is updated:
1189     // http://crbug.com/63254
1190     return;
1191   }
1192
1193   // See comment above for why we do this.
1194   BrowserWindowController* targetController = self;
1195   if ([sender respondsToSelector:@selector(window)])
1196     targetController = [[sender window] windowController];
1197   DCHECK([targetController isKindOfClass:[BrowserWindowController class]]);
1198   DCHECK(targetController->browser_.get());
1199
1200   NSInteger command = [sender tag];
1201   NSUInteger modifierFlags = [[NSApp currentEvent] modifierFlags];
1202   if ((command == IDC_RELOAD) &&
1203       (modifierFlags & (NSShiftKeyMask | NSControlKeyMask))) {
1204     command = IDC_RELOAD_IGNORING_CACHE;
1205     // Mask off Shift and Control so they don't affect the disposition below.
1206     modifierFlags &= ~(NSShiftKeyMask | NSControlKeyMask);
1207   }
1208   if (![[sender window] isMainWindow]) {
1209     // Remove the command key from the flags, it means "keep the window in
1210     // the background" in this case.
1211     modifierFlags &= ~NSCommandKeyMask;
1212   }
1213   chrome::ExecuteCommandWithDisposition(
1214       targetController->browser_.get(), command,
1215       ui::WindowOpenDispositionFromNSEventWithFlags(
1216           [NSApp currentEvent], modifierFlags));
1217 }
1218
1219 // Called when another part of the internal codebase needs to execute a
1220 // command.
1221 - (void)executeCommand:(int)command {
1222   chrome::ExecuteCommand(browser_.get(), command);
1223 }
1224
1225 - (BOOL)handledByExtensionCommand:(NSEvent*)event
1226     priority:(ui::AcceleratorManager::HandlerPriority)priority {
1227   return extension_keybinding_registry_->ProcessKeyEvent(
1228       content::NativeWebKeyboardEvent(event), priority);
1229 }
1230
1231 // StatusBubble delegate method: tell the status bubble the frame it should
1232 // position itself in.
1233 - (NSRect)statusBubbleBaseFrame {
1234   NSView* view = [overlayableContentsController_ view];
1235   return [view convertRect:[view bounds] toView:nil];
1236 }
1237
1238 - (void)updateToolbarWithContents:(WebContents*)tab {
1239   [toolbarController_ updateToolbarWithContents:tab];
1240 }
1241
1242 - (void)setStarredState:(BOOL)isStarred {
1243   [toolbarController_ setStarredState:isStarred];
1244 }
1245
1246 - (void)setCurrentPageIsTranslated:(BOOL)on {
1247   [toolbarController_ setTranslateIconLit:on];
1248 }
1249
1250 - (void)zoomChangedForActiveTab:(BOOL)canShowBubble {
1251   [toolbarController_ zoomChangedForActiveTab:canShowBubble];
1252 }
1253
1254 // Return the rect, in WebKit coordinates (flipped), of the window's grow box
1255 // in the coordinate system of the content area of the currently selected tab.
1256 // |windowGrowBox| needs to be in the window's coordinate system.
1257 - (NSRect)selectedTabGrowBoxRect {
1258   NSWindow* window = [self window];
1259   if (![window respondsToSelector:@selector(_growBoxRect)])
1260     return NSZeroRect;
1261
1262   // Before we return a rect, we need to convert it from window coordinates
1263   // to tab content area coordinates and flip the coordinate system.
1264   NSRect growBoxRect =
1265       [[self tabContentArea] convertRect:[window _growBoxRect] fromView:nil];
1266   growBoxRect.origin.y =
1267       NSHeight([[self tabContentArea] frame]) - NSMaxY(growBoxRect);
1268   return growBoxRect;
1269 }
1270
1271 // Accept tabs from a BrowserWindowController with the same Profile.
1272 - (BOOL)canReceiveFrom:(TabWindowController*)source {
1273   BrowserWindowController* realSource =
1274       base::mac::ObjCCast<BrowserWindowController>(source);
1275   if (!realSource || browser_->profile() != realSource->browser_->profile()) {
1276     return NO;
1277   }
1278
1279   // Can't drag a tab from a normal browser to a pop-up
1280   if (browser_->type() != realSource->browser_->type()) {
1281     return NO;
1282   }
1283
1284   return YES;
1285 }
1286
1287 // Move a given tab view to the location of the current placeholder. If there is
1288 // no placeholder, it will go at the end. |controller| is the window controller
1289 // of a tab being dropped from a different window. It will be nil if the drag is
1290 // within the window, otherwise the tab is removed from that window before being
1291 // placed into this one. The implementation will call |-removePlaceholder| since
1292 // the drag is now complete.  This also calls |-layoutTabs| internally so
1293 // clients do not need to call it again.
1294 - (void)moveTabViews:(NSArray*)views
1295       fromController:(TabWindowController*)dragController {
1296   if (dragController) {
1297     // Moving between windows.
1298     NSView* activeTabView = [dragController activeTabView];
1299     BrowserWindowController* dragBWC =
1300         base::mac::ObjCCastStrict<BrowserWindowController>(dragController);
1301
1302     // We will drop the tabs starting at indexOfPlaceholder, and increment from
1303     // there. We remove the placehoder before dropping the tabs, so that the
1304     // new tab animation's destination frame is correct.
1305     int tabIndex = [tabStripController_ indexOfPlaceholder];
1306     [self removePlaceholder];
1307
1308     for (NSView* view in views) {
1309       // Figure out the WebContents to drop into our tab model from the source
1310       // window's model.
1311       int index = [dragBWC->tabStripController_ modelIndexForTabView:view];
1312       WebContents* contents =
1313           dragBWC->browser_->tab_strip_model()->GetWebContentsAt(index);
1314       // The tab contents may have gone away if given a window.close() while it
1315       // is being dragged. If so, bail, we've got nothing to drop.
1316       if (!contents)
1317         continue;
1318
1319       // Convert |view|'s frame (which starts in the source tab strip's
1320       // coordinate system) to the coordinate system of the destination tab
1321       // strip. This needs to be done before being detached so the window
1322       // transforms can be performed.
1323       NSRect destinationFrame = [view frame];
1324       NSPoint tabOrigin = destinationFrame.origin;
1325       tabOrigin = [[dragController tabStripView] convertPoint:tabOrigin
1326                                                        toView:nil];
1327       tabOrigin = [[dragController window] convertBaseToScreen:tabOrigin];
1328       tabOrigin = [[self window] convertScreenToBase:tabOrigin];
1329       tabOrigin = [[self tabStripView] convertPoint:tabOrigin fromView:nil];
1330       destinationFrame.origin = tabOrigin;
1331
1332       // Before the tab is detached from its originating tab strip, store the
1333       // pinned state so that it can be maintained between the windows.
1334       bool isPinned = dragBWC->browser_->tab_strip_model()->IsTabPinned(index);
1335
1336       // Now that we have enough information about the tab, we can remove it
1337       // from the dragging window. We need to do this *before* we add it to the
1338       // new window as this will remove the WebContents' delegate.
1339       [dragController detachTabView:view];
1340
1341       // Deposit it into our model at the appropriate location (it already knows
1342       // where it should go from tracking the drag). Doing this sets the tab's
1343       // delegate to be the Browser.
1344       [tabStripController_ dropWebContents:contents
1345                                    atIndex:tabIndex++
1346                                  withFrame:destinationFrame
1347                                asPinnedTab:isPinned
1348                                   activate:view == activeTabView];
1349     }
1350   } else {
1351     // Moving within a window.
1352     for (NSView* view in views) {
1353       int index = [tabStripController_ modelIndexForTabView:view];
1354       [tabStripController_ moveTabFromIndex:index];
1355     }
1356     [self removePlaceholder];
1357   }
1358 }
1359
1360 // Tells the tab strip to forget about this tab in preparation for it being
1361 // put into a different tab strip, such as during a drop on another window.
1362 - (void)detachTabView:(NSView*)view {
1363   int index = [tabStripController_ modelIndexForTabView:view];
1364   browser_->tab_strip_model()->DetachWebContentsAt(index);
1365 }
1366
1367 - (NSArray*)tabViews {
1368   return [tabStripController_ tabViews];
1369 }
1370
1371 - (NSView*)activeTabView {
1372   return [tabStripController_ activeTabView];
1373 }
1374
1375 - (void)setIsLoading:(BOOL)isLoading force:(BOOL)force {
1376   [toolbarController_ setIsLoading:isLoading force:force];
1377 }
1378
1379 // Make the location bar the first responder, if possible.
1380 - (void)focusLocationBar:(BOOL)selectAll {
1381   [toolbarController_ focusLocationBar:selectAll];
1382 }
1383
1384 - (void)focusTabContents {
1385   content::WebContents* const activeWebContents =
1386       browser_->tab_strip_model()->GetActiveWebContents();
1387   if (activeWebContents)
1388     activeWebContents->Focus();
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   [[[[self window] contentView] superview] cr_recursivelySetNeedsDisplay:YES];
1668 }
1669
1670 - (ui::ThemeProvider*)themeProvider {
1671   return ThemeServiceFactory::GetForProfile(browser_->profile());
1672 }
1673
1674 - (ThemedWindowStyle)themedWindowStyle {
1675   ThemedWindowStyle style = 0;
1676   if (browser_->profile()->IsOffTheRecord())
1677     style |= THEMED_INCOGNITO;
1678
1679   if (browser_->is_devtools())
1680     style |= THEMED_DEVTOOLS;
1681   if (browser_->is_type_popup())
1682     style |= THEMED_POPUP;
1683
1684   return style;
1685 }
1686
1687 - (NSPoint)themeImagePositionForAlignment:(ThemeImageAlignment)alignment {
1688   NSView* windowChromeView = [[[self window] contentView] superview];
1689   NSView* tabStripView = nil;
1690   if ([self hasTabStrip])
1691     tabStripView = [self tabStripView];
1692   return [BrowserWindowUtils themeImagePositionFor:windowChromeView
1693                                       withTabStrip:tabStripView
1694                                          alignment:alignment];
1695 }
1696
1697 - (NSPoint)bookmarkBubblePoint {
1698   return [toolbarController_ bookmarkBubblePoint];
1699 }
1700
1701 // Show the bookmark bubble (e.g. user just clicked on the STAR).
1702 - (void)showBookmarkBubbleForURL:(const GURL&)url
1703                alreadyBookmarked:(BOOL)alreadyMarked {
1704   if (!bookmarkBubbleController_) {
1705     BookmarkModel* model =
1706         BookmarkModelFactory::GetForProfile(browser_->profile());
1707     ChromeBookmarkClient* client =
1708         ChromeBookmarkClientFactory::GetForProfile(browser_->profile());
1709     const BookmarkNode* node = model->GetMostRecentlyAddedUserNodeForURL(url);
1710     bookmarkBubbleController_ =
1711         [[BookmarkBubbleController alloc] initWithParentWindow:[self window]
1712                                                         client:client
1713                                                          model:model
1714                                                           node:node
1715                                              alreadyBookmarked:alreadyMarked];
1716     [bookmarkBubbleController_ showWindow:self];
1717     NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
1718     [center addObserver:self
1719                selector:@selector(bookmarkBubbleWindowWillClose:)
1720                    name:NSWindowWillCloseNotification
1721                  object:[bookmarkBubbleController_ window]];
1722   }
1723 }
1724
1725 // Nil out the weak bookmark bubble controller reference.
1726 - (void)bookmarkBubbleWindowWillClose:(NSNotification*)notification {
1727   DCHECK_EQ([notification object], [bookmarkBubbleController_ window]);
1728
1729   NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
1730   [center removeObserver:self
1731                     name:NSWindowWillCloseNotification
1732                   object:[bookmarkBubbleController_ window]];
1733   bookmarkBubbleController_ = nil;
1734 }
1735
1736 // Handle the editBookmarkNode: action sent from bookmark bubble controllers.
1737 - (void)editBookmarkNode:(id)sender {
1738   BOOL responds = [sender respondsToSelector:@selector(node)];
1739   DCHECK(responds);
1740   if (responds) {
1741     const BookmarkNode* node = [sender node];
1742     if (node)
1743       BookmarkEditor::Show([self window], browser_->profile(),
1744           BookmarkEditor::EditDetails::EditNode(node),
1745           BookmarkEditor::SHOW_TREE);
1746   }
1747 }
1748
1749 - (void)showTranslateBubbleForWebContents:(content::WebContents*)contents
1750                                      step:(translate::TranslateStep)step
1751                                 errorType:(translate::TranslateErrors::Type)
1752                                 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<translate::TranslateUIDelegate> uiDelegate(
1779       new translate::TranslateUIDelegate(
1780           ChromeTranslateClient::GetManagerFromWebContents(contents)
1781               ->GetWeakPtr(),
1782           sourceLanguage,
1783           targetLanguage));
1784   scoped_ptr<TranslateBubbleModel> model(
1785       new TranslateBubbleModelImpl(step, uiDelegate.Pass()));
1786   translateBubbleController_ = [[TranslateBubbleController alloc]
1787                                  initWithParentWindow:self
1788                                                 model:model.Pass()
1789                                           webContents:contents];
1790   [translateBubbleController_ showWindow:nil];
1791
1792   NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
1793   [center addObserver:self
1794              selector:@selector(translateBubbleWindowWillClose:)
1795                  name:NSWindowWillCloseNotification
1796                object:[translateBubbleController_ window]];
1797 }
1798
1799 // Nil out the weak translate bubble controller reference.
1800 - (void)translateBubbleWindowWillClose:(NSNotification*)notification {
1801   DCHECK_EQ([notification object], [translateBubbleController_ window]);
1802
1803   NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
1804   [center removeObserver:self
1805                     name:NSWindowWillCloseNotification
1806                   object:[translateBubbleController_ window]];
1807   translateBubbleController_ = nil;
1808 }
1809
1810 // If the browser is in incognito mode or has multi-profiles, install the image
1811 // view to decorate the window at the upper right. Use the same base y
1812 // coordinate as the tab strip.
1813 - (void)installAvatar {
1814   // Install the image into the badge view. Hide it for now; positioning and
1815   // sizing will be done by the layout code. The AvatarIcon will choose which
1816   // image to display based on the browser. The AvatarButton will display
1817   // the browser profile's name unless the browser is incognito.
1818   NSView* view;
1819   if ([self shouldUseNewAvatarButton]) {
1820     avatarButtonController_.reset(
1821       [[AvatarButtonController alloc] initWithBrowser:browser_.get()]);
1822   } else {
1823     avatarButtonController_.reset(
1824       [[AvatarIconController alloc] initWithBrowser:browser_.get()]);
1825   }
1826   view = [avatarButtonController_ view];
1827   [view setAutoresizingMask:NSViewMinXMargin | NSViewMinYMargin];
1828   [view setHidden:![self shouldShowAvatar]];
1829
1830   // Install the view.
1831   [[[self window] cr_windowView] addSubview:view];
1832 }
1833
1834 // Called when we get a three-finger swipe.
1835 - (void)swipeWithEvent:(NSEvent*)event {
1836   CGFloat deltaX = [event deltaX];
1837   CGFloat deltaY = [event deltaY];
1838
1839   // Map forwards and backwards to history; left is positive, right is negative.
1840   unsigned int command = 0;
1841   if (deltaX > 0.5) {
1842     command = IDC_BACK;
1843   } else if (deltaX < -0.5) {
1844     command = IDC_FORWARD;
1845   } else if (deltaY > 0.5) {
1846     // TODO(pinkerton): figure out page-up, http://crbug.com/16305
1847   } else if (deltaY < -0.5) {
1848     // TODO(pinkerton): figure out page-down, http://crbug.com/16305
1849   }
1850
1851   // Ensure the command is valid first (ExecuteCommand() won't do that) and
1852   // then make it so.
1853   if (chrome::IsCommandEnabled(browser_.get(), command)) {
1854     chrome::ExecuteCommandWithDisposition(
1855         browser_.get(),
1856         command,
1857         ui::WindowOpenDispositionFromNSEvent(event));
1858   }
1859 }
1860
1861 // Delegate method called when window is resized.
1862 - (void)windowDidResize:(NSNotification*)notification {
1863   [self saveWindowPositionIfNeeded];
1864
1865   // Resize (and possibly move) the status bubble. Note that we may get called
1866   // when the status bubble does not exist.
1867   if (statusBubble_) {
1868     statusBubble_->UpdateSizeAndPosition();
1869   }
1870
1871   // Let the selected RenderWidgetHostView know, so that it can tell plugins.
1872   if (WebContents* contents =
1873           browser_->tab_strip_model()->GetActiveWebContents()) {
1874     if (RenderWidgetHostView* rwhv = contents->GetRenderWidgetHostView())
1875       rwhv->WindowFrameChanged();
1876   }
1877
1878   // The FindBar needs to know its own position to properly detect overlaps
1879   // with find results. The position changes whenever the window is resized,
1880   // and |layoutSubviews| computes the FindBar's position.
1881   // TODO: calling |layoutSubviews| here is a waste, find a better way to
1882   // do this.
1883   if ([findBarCocoaController_ isFindBarVisible])
1884     [self layoutSubviews];
1885 }
1886
1887 // Handle the openLearnMoreAboutCrashLink: action from SadTabController when
1888 // "Learn more" link in "Aw snap" page (i.e. crash page or sad tab) is
1889 // clicked. Decoupling the action from its target makes unit testing possible.
1890 - (void)openLearnMoreAboutCrashLink:(id)sender {
1891   if (SadTabController* sadTab =
1892           base::mac::ObjCCast<SadTabController>(sender)) {
1893     WebContents* webContents = [sadTab webContents];
1894     if (webContents) {
1895       OpenURLParams params(
1896           GURL(chrome::kCrashReasonURL), Referrer(), CURRENT_TAB,
1897           content::PAGE_TRANSITION_LINK, false);
1898       webContents->OpenURL(params);
1899     }
1900   }
1901 }
1902
1903 // Delegate method called when window did move. (See below for why we don't use
1904 // |-windowWillMove:|, which is called less frequently than |-windowDidMove|
1905 // instead.)
1906 - (void)windowDidMove:(NSNotification*)notification {
1907   [self saveWindowPositionIfNeeded];
1908
1909   NSWindow* window = [self window];
1910   NSRect windowFrame = [window frame];
1911   NSRect workarea = [[window screen] visibleFrame];
1912
1913   // We reset the window growth state whenever the window is moved out of the
1914   // work area or away (up or down) from the bottom or top of the work area.
1915   // Unfortunately, Cocoa sends |-windowWillMove:| too frequently (including
1916   // when clicking on the title bar to activate), and of course
1917   // |-windowWillMove| is called too early for us to apply our heuristic. (The
1918   // heuristic we use for detecting window movement is that if |windowTopGrowth_
1919   // > 0|, then we should be at the bottom of the work area -- if we're not,
1920   // we've moved. Similarly for the other side.)
1921   if (!NSContainsRect(workarea, windowFrame) ||
1922       (windowTopGrowth_ > 0 && NSMinY(windowFrame) != NSMinY(workarea)) ||
1923       (windowBottomGrowth_ > 0 && NSMaxY(windowFrame) != NSMaxY(workarea)))
1924     [self resetWindowGrowthState];
1925
1926   // Let the selected RenderWidgetHostView know, so that it can tell plugins.
1927   if (WebContents* contents =
1928           browser_->tab_strip_model()->GetActiveWebContents()) {
1929     if (RenderWidgetHostView* rwhv = contents->GetRenderWidgetHostView())
1930       rwhv->WindowFrameChanged();
1931   }
1932 }
1933
1934 // Delegate method called when window will be resized; not called for
1935 // |-setFrame:display:|.
1936 - (NSSize)windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize {
1937   [self resetWindowGrowthState];
1938   return frameSize;
1939 }
1940
1941 // Delegate method: see |NSWindowDelegate| protocol.
1942 - (id)windowWillReturnFieldEditor:(NSWindow*)sender toObject:(id)obj {
1943   // Ask the toolbar controller if it wants to return a custom field editor
1944   // for the specific object.
1945   return [toolbarController_ customFieldEditorForObject:obj];
1946 }
1947
1948 // (Needed for |BookmarkBarControllerDelegate| protocol.)
1949 - (void)bookmarkBar:(BookmarkBarController*)controller
1950  didChangeFromState:(BookmarkBar::State)oldState
1951             toState:(BookmarkBar::State)newState {
1952   [toolbarController_ setDividerOpacity:[self toolbarDividerOpacity]];
1953   [self adjustToolbarAndBookmarkBarForCompression:
1954           [controller getDesiredToolbarHeightCompression]];
1955 }
1956
1957 // (Needed for |BookmarkBarControllerDelegate| protocol.)
1958 - (void)bookmarkBar:(BookmarkBarController*)controller
1959 willAnimateFromState:(BookmarkBar::State)oldState
1960             toState:(BookmarkBar::State)newState {
1961   [toolbarController_ setDividerOpacity:[self toolbarDividerOpacity]];
1962   [self adjustToolbarAndBookmarkBarForCompression:
1963           [controller getDesiredToolbarHeightCompression]];
1964 }
1965
1966 // (Private/TestingAPI)
1967 - (void)resetWindowGrowthState {
1968   windowTopGrowth_ = 0;
1969   windowBottomGrowth_ = 0;
1970   isShrinkingFromZoomed_ = NO;
1971 }
1972
1973 - (NSSize)overflowFrom:(NSRect)source
1974                     to:(NSRect)target {
1975   // If |source|'s boundary is outside of |target|'s, set its distance
1976   // to |x|.  Note that |source| can overflow to both side, but we
1977   // have nothing to do for such case.
1978   CGFloat x = 0;
1979   if (NSMaxX(target) < NSMaxX(source)) // |source| overflows to right
1980     x = NSMaxX(source) - NSMaxX(target);
1981   else if (NSMinX(source) < NSMinX(target)) // |source| overflows to left
1982     x = NSMinX(source) - NSMinX(target);
1983
1984   // Same as |x| above.
1985   CGFloat y = 0;
1986   if (NSMaxY(target) < NSMaxY(source))
1987     y = NSMaxY(source) - NSMaxY(target);
1988   else if (NSMinY(source) < NSMinY(target))
1989     y = NSMinY(source) - NSMinY(target);
1990
1991   return NSMakeSize(x, y);
1992 }
1993
1994 // (Private/TestingAPI)
1995 - (FullscreenExitBubbleController*)fullscreenExitBubbleController {
1996   return fullscreenExitBubbleController_.get();
1997 }
1998
1999 - (NSRect)omniboxPopupAnchorRect {
2000   // Start with toolbar rect.
2001   NSView* toolbarView = [toolbarController_ view];
2002   NSRect anchorRect = [toolbarView frame];
2003
2004   // Adjust to account for height and possible bookmark bar. Compress by 1
2005   // to account for the separator.
2006   anchorRect.origin.y =
2007       NSMaxY(anchorRect) - [toolbarController_ desiredHeightForCompression:1];
2008
2009   // Shift to window base coordinates.
2010   return [[toolbarView superview] convertRect:anchorRect toView:nil];
2011 }
2012
2013 - (void)layoutInfoBars {
2014   [self layoutSubviews];
2015 }
2016
2017 - (void)sheetDidEnd:(NSWindow*)sheet
2018          returnCode:(NSInteger)code
2019             context:(void*)context {
2020   [sheet orderOut:self];
2021 }
2022
2023 - (void)onFindBarVisibilityChanged {
2024   [self updateAllowOverlappingViews:[self inPresentationMode]];
2025 }
2026
2027 - (void)onOverlappedViewShown {
2028   ++overlappedViewCount_;
2029   [self updateAllowOverlappingViews:[self inPresentationMode]];
2030 }
2031
2032 - (void)onOverlappedViewHidden {
2033   --overlappedViewCount_;
2034   [self updateAllowOverlappingViews:[self inPresentationMode]];
2035 }
2036
2037 - (void)executeExtensionCommand:(const std::string&)extension_id
2038                         command:(const extensions::Command&)command {
2039   // Global commands are handled by the ExtensionCommandsGlobalRegistry
2040   // instance.
2041   DCHECK(!command.global());
2042   extension_keybinding_registry_->ExecuteCommand(extension_id,
2043                                                  command.accelerator());
2044 }
2045
2046 - (void)activatePageAction:(const std::string&)extension_id {
2047   [toolbarController_ activatePageAction:extension_id];
2048 }
2049
2050 - (void)activateBrowserAction:(const std::string&)extension_id {
2051   [toolbarController_ activateBrowserAction:extension_id];
2052 }
2053
2054 @end  // @implementation BrowserWindowController
2055
2056
2057 @implementation BrowserWindowController(Fullscreen)
2058
2059 - (void)handleLionToggleFullscreen {
2060   DCHECK(base::mac::IsOSLionOrLater());
2061   chrome::ExecuteCommand(browser_.get(), IDC_FULLSCREEN);
2062 }
2063
2064 // Called to transition into or out of fullscreen mode. Only use System
2065 // Fullscreen mode if the system supports it and we aren't trying to go
2066 // fullscreen for the renderer-initiated use cases.
2067 // Discussion: http://crbug.com/179181 and http:/crbug.com/351252
2068 - (void)setFullscreen:(BOOL)fullscreen {
2069   if (fullscreen == [self isFullscreen])
2070     return;
2071
2072   if (fullscreen) {
2073     const BOOL shouldUseSystemFullscreen =
2074         chrome::mac::SupportsSystemFullscreen() && !fullscreenWindow_ &&
2075         !browser_->fullscreen_controller()->IsWindowFullscreenForTabOrPending();
2076     if (shouldUseSystemFullscreen) {
2077       if (FramedBrowserWindow* framedBrowserWindow =
2078           base::mac::ObjCCast<FramedBrowserWindow>([self window])) {
2079         [framedBrowserWindow toggleSystemFullScreen];
2080       }
2081     } else {
2082       [self enterImmersiveFullscreen];
2083     }
2084   } else {
2085     if ([self isInSystemFullscreen]) {
2086       if (FramedBrowserWindow* framedBrowserWindow =
2087           base::mac::ObjCCast<FramedBrowserWindow>([self window])) {
2088         [framedBrowserWindow toggleSystemFullScreen];
2089       }
2090     } else {
2091       DCHECK(fullscreenWindow_.get());
2092       [self exitImmersiveFullscreen];
2093     }
2094   }
2095 }
2096
2097 - (void)enterFullscreen {
2098   [self setFullscreen:YES];
2099 }
2100
2101 - (void)exitFullscreen {
2102   [self setFullscreen:NO];
2103 }
2104
2105 - (void)updateFullscreenExitBubbleURL:(const GURL&)url
2106                            bubbleType:(FullscreenExitBubbleType)bubbleType {
2107   fullscreenUrl_ = url;
2108   fullscreenBubbleType_ = bubbleType;
2109   [self layoutSubviews];
2110   [self showFullscreenExitBubbleIfNecessary];
2111 }
2112
2113 - (BOOL)isFullscreen {
2114   return [self isInImmersiveFullscreen] ||
2115          [self isInSystemFullscreen] ||
2116          enteringFullscreen_;
2117 }
2118
2119 - (BOOL)isInImmersiveFullscreen {
2120   return fullscreenWindow_.get() != nil;
2121 }
2122
2123 - (BOOL)isInSystemFullscreen {
2124   return ([[self window] styleMask] & NSFullScreenWindowMask) ==
2125       NSFullScreenWindowMask;
2126 }
2127
2128 // On Lion, this function is called by either the presentation mode toggle
2129 // button or the "Enter Presentation Mode" menu item.  In the latter case, this
2130 // function also triggers the Lion machinery to enter fullscreen mode as well as
2131 // set presentation mode.  On Snow Leopard, this function is called by the
2132 // "Enter Presentation Mode" menu item, and triggering presentation mode always
2133 // moves the user into fullscreen mode.
2134 - (void)setPresentationMode:(BOOL)presentationMode
2135                         url:(const GURL&)url
2136                  bubbleType:(FullscreenExitBubbleType)bubbleType {
2137   fullscreenUrl_ = url;
2138   fullscreenBubbleType_ = bubbleType;
2139
2140   // Presentation mode on systems without fullscreen support maps directly to
2141   // fullscreen mode.
2142   if (!chrome::mac::SupportsSystemFullscreen()) {
2143     [self setFullscreen:presentationMode];
2144     return;
2145   }
2146
2147   if (presentationMode) {
2148     BOOL fullscreen = [self isFullscreen];
2149     enteringPresentationMode_ = YES;
2150
2151     if (fullscreen) {
2152       // If already in fullscreen mode, just toggle the presentation mode
2153       // setting.  Go through an elaborate dance to force the overlay to show,
2154       // then animate out once the mouse moves away.  This helps draw attention
2155       // to the fact that the UI is in an overlay.  Focus the tab contents
2156       // because the omnibox is the most likely source of bar visibility locks,
2157       // and taking focus away from the omnibox releases its lock.
2158       [self lockBarVisibilityForOwner:self withAnimation:NO delay:NO];
2159       [self focusTabContents];
2160       [self setPresentationModeInternal:YES forceDropdown:YES];
2161       [self releaseBarVisibilityForOwner:self withAnimation:YES delay:YES];
2162       // Since -windowDidEnterFullScreen: won't be called in the
2163       // fullscreen --> presentation mode case, manually show the exit bubble
2164       // and notify the change happened with WindowFullscreenStateChanged().
2165       [self showFullscreenExitBubbleIfNecessary];
2166       browser_->WindowFullscreenStateChanged();
2167     } else {
2168       // Need to transition into fullscreen mode.  Presentation mode will
2169       // automatically be enabled in |-windowWillEnterFullScreen:|.
2170       [self setFullscreen:YES];
2171     }
2172   } else {
2173     // Exiting presentation mode does not exit system fullscreen; it merely
2174     // switches from presentation mode to normal fullscreen.
2175     [self setPresentationModeInternal:NO forceDropdown:NO];
2176
2177     // Since -windowDidExitFullScreen: won't be called in the
2178     // presentation mode --> normal fullscreen case, manually show the exit
2179     // bubble and notify the change happened with
2180     // WindowFullscreenStateChanged().
2181     [self showFullscreenExitBubbleIfNecessary];
2182     browser_->WindowFullscreenStateChanged();
2183   }
2184 }
2185
2186 - (void)enterPresentationModeForURL:(const GURL&)url
2187                          bubbleType:(FullscreenExitBubbleType)bubbleType {
2188   [self setPresentationMode:YES url:url bubbleType:bubbleType];
2189 }
2190
2191 - (void)exitPresentationMode {
2192   // url: and bubbleType: are ignored when leaving presentation mode.
2193   [self setPresentationMode:NO url:GURL() bubbleType:FEB_TYPE_NONE];
2194 }
2195
2196 - (void)enterFullscreenForURL:(const GURL&)url
2197                    bubbleType:(FullscreenExitBubbleType)bubbleType {
2198   // This method may only be called in simplified fullscreen mode.
2199   const CommandLine* command_line = CommandLine::ForCurrentProcess();
2200   DCHECK(command_line->HasSwitch(switches::kEnableSimplifiedFullscreen));
2201
2202   [self enterImmersiveFullscreen];
2203   [self updateFullscreenExitBubbleURL:url bubbleType:bubbleType];
2204 }
2205
2206 - (BOOL)inPresentationMode {
2207   return presentationModeController_.get() &&
2208       [presentationModeController_ inPresentationMode];
2209 }
2210
2211 - (void)resizeFullscreenWindow {
2212   DCHECK([self isFullscreen]);
2213   if (![self isFullscreen])
2214     return;
2215
2216   NSWindow* window = [self window];
2217   [window setFrame:[[window screen] frame] display:YES];
2218   [self layoutSubviews];
2219 }
2220
2221 - (CGFloat)floatingBarShownFraction {
2222   return floatingBarShownFraction_;
2223 }
2224
2225 - (void)setFloatingBarShownFraction:(CGFloat)fraction {
2226   floatingBarShownFraction_ = fraction;
2227   [self layoutSubviews];
2228 }
2229
2230 - (BOOL)isBarVisibilityLockedForOwner:(id)owner {
2231   DCHECK(owner);
2232   DCHECK(barVisibilityLocks_);
2233   return [barVisibilityLocks_ containsObject:owner];
2234 }
2235
2236 - (void)lockBarVisibilityForOwner:(id)owner
2237                     withAnimation:(BOOL)animate
2238                             delay:(BOOL)delay {
2239   if (![self isBarVisibilityLockedForOwner:owner]) {
2240     [barVisibilityLocks_ addObject:owner];
2241
2242     // If enabled, show the overlay if necessary (and if in presentation mode).
2243     if (barVisibilityUpdatesEnabled_) {
2244       [presentationModeController_ ensureOverlayShownWithAnimation:animate
2245                                                              delay:delay];
2246     }
2247   }
2248 }
2249
2250 - (void)releaseBarVisibilityForOwner:(id)owner
2251                        withAnimation:(BOOL)animate
2252                                delay:(BOOL)delay {
2253   if ([self isBarVisibilityLockedForOwner:owner]) {
2254     [barVisibilityLocks_ removeObject:owner];
2255
2256     // If enabled, hide the overlay if necessary (and if in presentation mode).
2257     if (barVisibilityUpdatesEnabled_ &&
2258         ![barVisibilityLocks_ count]) {
2259       [presentationModeController_ ensureOverlayHiddenWithAnimation:animate
2260                                                               delay:delay];
2261     }
2262   }
2263 }
2264
2265 - (BOOL)floatingBarHasFocus {
2266   NSResponder* focused = [[self window] firstResponder];
2267   return [focused isKindOfClass:[AutocompleteTextFieldEditor class]];
2268 }
2269
2270 @end  // @implementation BrowserWindowController(Fullscreen)
2271
2272
2273 @implementation BrowserWindowController(WindowType)
2274
2275 - (BOOL)supportsWindowFeature:(int)feature {
2276   return browser_->SupportsWindowFeature(
2277       static_cast<Browser::WindowFeature>(feature));
2278 }
2279
2280 - (BOOL)hasTitleBar {
2281   return [self supportsWindowFeature:Browser::FEATURE_TITLEBAR];
2282 }
2283
2284 - (BOOL)hasToolbar {
2285   return [self supportsWindowFeature:Browser::FEATURE_TOOLBAR];
2286 }
2287
2288 - (BOOL)hasLocationBar {
2289   return [self supportsWindowFeature:Browser::FEATURE_LOCATIONBAR];
2290 }
2291
2292 - (BOOL)supportsBookmarkBar {
2293   return [self supportsWindowFeature:Browser::FEATURE_BOOKMARKBAR];
2294 }
2295
2296 - (BOOL)isTabbedWindow {
2297   return browser_->is_type_tabbed();
2298 }
2299
2300 @end  // @implementation BrowserWindowController(WindowType)