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