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