Home | History | Annotate | Download | only in cocoa
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/ui/cocoa/browser_window_cocoa.h"
      6 
      7 #include "base/command_line.h"
      8 #include "base/logging.h"
      9 #include "base/message_loop.h"
     10 #include "base/sys_string_conversions.h"
     11 #include "chrome/app/chrome_command_ids.h"
     12 #include "chrome/browser/bookmarks/bookmark_utils.h"
     13 #include "chrome/browser/download/download_shelf.h"
     14 #include "chrome/browser/global_keyboard_shortcuts_mac.h"
     15 #include "chrome/browser/page_info_window.h"
     16 #include "chrome/browser/prefs/pref_service.h"
     17 #include "chrome/browser/profiles/profile.h"
     18 #include "chrome/browser/sidebar/sidebar_container.h"
     19 #include "chrome/browser/sidebar/sidebar_manager.h"
     20 #include "chrome/browser/ui/browser.h"
     21 #include "chrome/browser/ui/browser_list.h"
     22 #import "chrome/browser/ui/cocoa/browser_window_controller.h"
     23 #import "chrome/browser/ui/cocoa/bug_report_window_controller.h"
     24 #import "chrome/browser/ui/cocoa/chrome_event_processing_window.h"
     25 #import "chrome/browser/ui/cocoa/content_settings/collected_cookies_mac.h"
     26 #import "chrome/browser/ui/cocoa/download/download_shelf_controller.h"
     27 #import "chrome/browser/ui/cocoa/html_dialog_window_controller.h"
     28 #import "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h"
     29 #import "chrome/browser/ui/cocoa/nsmenuitem_additions.h"
     30 #include "chrome/browser/ui/cocoa/repost_form_warning_mac.h"
     31 #include "chrome/browser/ui/cocoa/restart_browser.h"
     32 #include "chrome/browser/ui/cocoa/status_bubble_mac.h"
     33 #include "chrome/browser/ui/cocoa/task_manager_mac.h"
     34 #import "chrome/browser/ui/cocoa/theme_install_bubble_view.h"
     35 #import "chrome/browser/ui/cocoa/toolbar/toolbar_controller.h"
     36 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
     37 #include "chrome/common/pref_names.h"
     38 #include "content/browser/tab_contents/tab_contents.h"
     39 #include "content/common/native_web_keyboard_event.h"
     40 #include "content/common/notification_service.h"
     41 #include "grit/chromium_strings.h"
     42 #include "grit/generated_resources.h"
     43 #include "ui/base/l10n/l10n_util_mac.h"
     44 #include "ui/gfx/rect.h"
     45 
     46 BrowserWindowCocoa::BrowserWindowCocoa(Browser* browser,
     47                                        BrowserWindowController* controller,
     48                                        NSWindow* window)
     49   : browser_(browser),
     50     controller_(controller),
     51     confirm_close_factory_(browser) {
     52   // This pref applies to all windows, so all must watch for it.
     53   registrar_.Add(this, NotificationType::BOOKMARK_BAR_VISIBILITY_PREF_CHANGED,
     54                  NotificationService::AllSources());
     55   registrar_.Add(this, NotificationType::SIDEBAR_CHANGED,
     56                  NotificationService::AllSources());
     57 }
     58 
     59 BrowserWindowCocoa::~BrowserWindowCocoa() {
     60 }
     61 
     62 void BrowserWindowCocoa::Show() {
     63   // The Browser associated with this browser window must become the active
     64   // browser at the time |Show()| is called. This is the natural behaviour under
     65   // Windows, but |-makeKeyAndOrderFront:| won't send |-windowDidBecomeMain:|
     66   // until we return to the runloop. Therefore any calls to
     67   // |BrowserList::GetLastActive()| (for example, in bookmark_util), will return
     68   // the previous browser instead if we don't explicitly set it here.
     69   BrowserList::SetLastActive(browser_);
     70 
     71   [window() makeKeyAndOrderFront:controller_];
     72 }
     73 
     74 void BrowserWindowCocoa::ShowInactive() {
     75     [window() orderFront:controller_];
     76 }
     77 
     78 void BrowserWindowCocoa::SetBounds(const gfx::Rect& bounds) {
     79   SetFullscreen(false);
     80   NSRect cocoa_bounds = NSMakeRect(bounds.x(), 0, bounds.width(),
     81                                    bounds.height());
     82   // Flip coordinates based on the primary screen.
     83   NSScreen* screen = [[NSScreen screens] objectAtIndex:0];
     84   cocoa_bounds.origin.y =
     85       [screen frame].size.height - bounds.height() - bounds.y();
     86 
     87   [window() setFrame:cocoa_bounds display:YES];
     88 }
     89 
     90 // Callers assume that this doesn't immediately delete the Browser object.
     91 // The controller implementing the window delegate methods called from
     92 // |-performClose:| must take precautions to ensure that.
     93 void BrowserWindowCocoa::Close() {
     94   // If there is an overlay window, we contain a tab being dragged between
     95   // windows. Don't hide the window as it makes the UI extra confused. We can
     96   // still close the window, as that will happen when the drag completes.
     97   if ([controller_ overlayWindow]) {
     98     [controller_ deferPerformClose];
     99   } else {
    100     // Make sure we hide the window immediately. Even though performClose:
    101     // calls orderOut: eventually, it leaves the window on-screen long enough
    102     // that we start to see tabs shutting down. http://crbug.com/23959
    103     // TODO(viettrungluu): This is kind of bad, since |-performClose:| calls
    104     // |-windowShouldClose:| (on its delegate, which is probably the
    105     // controller) which may return |NO| causing the window to not be closed,
    106     // thereby leaving a hidden window. In fact, our window-closing procedure
    107     // involves a (indirect) recursion on |-performClose:|, which is also bad.
    108     [window() orderOut:controller_];
    109     [window() performClose:controller_];
    110   }
    111 }
    112 
    113 void BrowserWindowCocoa::Activate() {
    114   [controller_ activate];
    115 }
    116 
    117 void BrowserWindowCocoa::Deactivate() {
    118   // TODO(jcivelli): http://crbug.com/51364 Implement me.
    119   NOTIMPLEMENTED();
    120 }
    121 
    122 void BrowserWindowCocoa::FlashFrame() {
    123   [NSApp requestUserAttention:NSInformationalRequest];
    124 }
    125 
    126 bool BrowserWindowCocoa::IsActive() const {
    127   return [window() isKeyWindow];
    128 }
    129 
    130 gfx::NativeWindow BrowserWindowCocoa::GetNativeHandle() {
    131   return window();
    132 }
    133 
    134 BrowserWindowTesting* BrowserWindowCocoa::GetBrowserWindowTesting() {
    135   return NULL;
    136 }
    137 
    138 StatusBubble* BrowserWindowCocoa::GetStatusBubble() {
    139   return [controller_ statusBubble];
    140 }
    141 
    142 void BrowserWindowCocoa::ToolbarSizeChanged(bool is_animating) {
    143   // According to beng, this is an ugly method that comes from the days when the
    144   // download shelf was a ChromeView attached to the TabContents, and as its
    145   // size changed via animation it notified through TCD/etc to the browser view
    146   // to relayout for each tick of the animation. We don't need anything of the
    147   // sort on Mac.
    148 }
    149 
    150 void BrowserWindowCocoa::UpdateTitleBar() {
    151   NSString* newTitle =
    152       base::SysUTF16ToNSString(browser_->GetWindowTitleForCurrentTab());
    153 
    154   // Work around Cocoa bug: if a window changes title during the tracking of the
    155   // Window menu it doesn't display well and the constant re-sorting of the list
    156   // makes it difficult for the user to pick the desired window. Delay window
    157   // title updates until the default run-loop mode.
    158 
    159   if (pending_window_title_.get())
    160     [[NSRunLoop currentRunLoop]
    161         cancelPerformSelector:@selector(setTitle:)
    162                        target:window()
    163                      argument:pending_window_title_.get()];
    164 
    165   pending_window_title_.reset([newTitle copy]);
    166   [[NSRunLoop currentRunLoop]
    167       performSelector:@selector(setTitle:)
    168                target:window()
    169              argument:newTitle
    170                 order:0
    171                 modes:[NSArray arrayWithObject:NSDefaultRunLoopMode]];
    172 }
    173 
    174 void BrowserWindowCocoa::ShelfVisibilityChanged() {
    175   // Mac doesn't yet support showing the bookmark bar at a different size on
    176   // the new tab page. When it does, this method should attempt to relayout the
    177   // bookmark bar/extension shelf as their preferred height may have changed.
    178   // http://crbug.com/43346
    179 }
    180 
    181 void BrowserWindowCocoa::UpdateDevTools() {
    182   [controller_ updateDevToolsForContents:
    183       browser_->GetSelectedTabContents()];
    184 }
    185 
    186 void BrowserWindowCocoa::UpdateLoadingAnimations(bool should_animate) {
    187   // Do nothing on Mac.
    188 }
    189 
    190 void BrowserWindowCocoa::SetStarredState(bool is_starred) {
    191   [controller_ setStarredState:is_starred ? YES : NO];
    192 }
    193 
    194 gfx::Rect BrowserWindowCocoa::GetRestoredBounds() const {
    195   // Flip coordinates based on the primary screen.
    196   NSScreen* screen = [[NSScreen screens] objectAtIndex:0];
    197   NSRect frame = [controller_ regularWindowFrame];
    198   gfx::Rect bounds(frame.origin.x, 0, frame.size.width, frame.size.height);
    199   bounds.set_y([screen frame].size.height - frame.origin.y - frame.size.height);
    200   return bounds;
    201 }
    202 
    203 gfx::Rect BrowserWindowCocoa::GetBounds() const {
    204   return GetRestoredBounds();
    205 }
    206 
    207 bool BrowserWindowCocoa::IsMaximized() const {
    208   return [window() isZoomed];
    209 }
    210 
    211 void BrowserWindowCocoa::SetFullscreen(bool fullscreen) {
    212   [controller_ setFullscreen:fullscreen];
    213 }
    214 
    215 bool BrowserWindowCocoa::IsFullscreen() const {
    216   return !![controller_ isFullscreen];
    217 }
    218 
    219 bool BrowserWindowCocoa::IsFullscreenBubbleVisible() const {
    220   return false;
    221 }
    222 
    223 void BrowserWindowCocoa::ConfirmAddSearchProvider(
    224     const TemplateURL* template_url,
    225     Profile* profile) {
    226   NOTIMPLEMENTED();
    227 }
    228 
    229 LocationBar* BrowserWindowCocoa::GetLocationBar() const {
    230   return [controller_ locationBarBridge];
    231 }
    232 
    233 void BrowserWindowCocoa::SetFocusToLocationBar(bool select_all) {
    234   [controller_ focusLocationBar:select_all ? YES : NO];
    235 }
    236 
    237 void BrowserWindowCocoa::UpdateReloadStopState(bool is_loading, bool force) {
    238   [controller_ setIsLoading:is_loading force:force];
    239 }
    240 
    241 void BrowserWindowCocoa::UpdateToolbar(TabContentsWrapper* contents,
    242                                        bool should_restore_state) {
    243   [controller_ updateToolbarWithContents:contents->tab_contents()
    244                       shouldRestoreState:should_restore_state ? YES : NO];
    245 }
    246 
    247 void BrowserWindowCocoa::FocusToolbar() {
    248   // Not needed on the Mac.
    249 }
    250 
    251 void BrowserWindowCocoa::FocusAppMenu() {
    252   // Chrome uses the standard Mac OS X menu bar, so this isn't needed.
    253 }
    254 
    255 void BrowserWindowCocoa::RotatePaneFocus(bool forwards) {
    256   // Not needed on the Mac.
    257 }
    258 
    259 void BrowserWindowCocoa::FocusBookmarksToolbar() {
    260   // Not needed on the Mac.
    261 }
    262 
    263 void BrowserWindowCocoa::FocusChromeOSStatus() {
    264   // Not needed on the Mac.
    265 }
    266 
    267 bool BrowserWindowCocoa::IsBookmarkBarVisible() const {
    268   return (browser_->profile()->GetPrefs()->GetBoolean(
    269               prefs::kShowBookmarkBar) &&
    270           browser_->profile()->GetPrefs()->GetBoolean(
    271               prefs::kEnableBookmarkBar));
    272 }
    273 
    274 bool BrowserWindowCocoa::IsBookmarkBarAnimating() const {
    275   return [controller_ isBookmarkBarAnimating];
    276 }
    277 
    278 bool BrowserWindowCocoa::IsTabStripEditable() const {
    279   return ![controller_ isDragSessionActive];
    280 }
    281 
    282 bool BrowserWindowCocoa::IsToolbarVisible() const {
    283   return browser_->SupportsWindowFeature(Browser::FEATURE_TOOLBAR) ||
    284          browser_->SupportsWindowFeature(Browser::FEATURE_LOCATIONBAR);
    285 }
    286 
    287 // This is called from Browser, which in turn is called directly from
    288 // a menu option.  All we do here is set a preference.  The act of
    289 // setting the preference sends notifications to all windows who then
    290 // know what to do.
    291 void BrowserWindowCocoa::ToggleBookmarkBar() {
    292   bookmark_utils::ToggleWhenVisible(browser_->profile());
    293 }
    294 
    295 void BrowserWindowCocoa::AddFindBar(
    296     FindBarCocoaController* find_bar_cocoa_controller) {
    297   return [controller_ addFindBar:find_bar_cocoa_controller];
    298 }
    299 
    300 void BrowserWindowCocoa::ShowAboutChromeDialog() {
    301   // Go through AppController's implementation to bring up the branded panel.
    302   [[NSApp delegate] orderFrontStandardAboutPanel:nil];
    303 }
    304 
    305 void BrowserWindowCocoa::ShowUpdateChromeDialog() {
    306   restart_browser::RequestRestart(window());
    307 }
    308 
    309 void BrowserWindowCocoa::ShowTaskManager() {
    310   TaskManagerMac::Show(false);
    311 }
    312 
    313 void BrowserWindowCocoa::ShowBackgroundPages() {
    314   TaskManagerMac::Show(true);
    315 }
    316 
    317 void BrowserWindowCocoa::ShowBookmarkBubble(const GURL& url,
    318                                             bool already_bookmarked) {
    319   [controller_ showBookmarkBubbleForURL:url
    320                       alreadyBookmarked:(already_bookmarked ? YES : NO)];
    321 }
    322 
    323 bool BrowserWindowCocoa::IsDownloadShelfVisible() const {
    324   return [controller_ isDownloadShelfVisible] != NO;
    325 }
    326 
    327 DownloadShelf* BrowserWindowCocoa::GetDownloadShelf() {
    328   DownloadShelfController* shelfController = [controller_ downloadShelf];
    329   return [shelfController bridge];
    330 }
    331 
    332 void BrowserWindowCocoa::ShowRepostFormWarningDialog(
    333     TabContents* tab_contents) {
    334   RepostFormWarningMac::Create(GetNativeHandle(), tab_contents);
    335 }
    336 
    337 void BrowserWindowCocoa::ShowCollectedCookiesDialog(TabContents* tab_contents) {
    338   // Deletes itself on close.
    339   new CollectedCookiesMac(GetNativeHandle(), tab_contents);
    340 }
    341 
    342 void BrowserWindowCocoa::ShowThemeInstallBubble() {
    343   ThemeInstallBubbleView::Show(window());
    344 }
    345 
    346 // We allow closing the window here since the real quit decision on Mac is made
    347 // in [AppController quit:].
    348 void BrowserWindowCocoa::ConfirmBrowserCloseWithPendingDownloads() {
    349   // Call InProgressDownloadResponse asynchronously to avoid a crash when the
    350   // browser window is closed here (http://crbug.com/44454).
    351   MessageLoop::current()->PostTask(
    352       FROM_HERE,
    353       confirm_close_factory_.NewRunnableMethod(
    354           &Browser::InProgressDownloadResponse,
    355           true));
    356 }
    357 
    358 void BrowserWindowCocoa::ShowHTMLDialog(HtmlDialogUIDelegate* delegate,
    359                                         gfx::NativeWindow parent_window) {
    360   [HtmlDialogWindowController showHtmlDialog:delegate
    361                                      profile:browser_->profile()];
    362 }
    363 
    364 void BrowserWindowCocoa::UserChangedTheme() {
    365   [controller_ userChangedTheme];
    366 }
    367 
    368 int BrowserWindowCocoa::GetExtraRenderViewHeight() const {
    369   // Currently this is only used on linux.
    370   return 0;
    371 }
    372 
    373 void BrowserWindowCocoa::TabContentsFocused(TabContents* tab_contents) {
    374   NOTIMPLEMENTED();
    375 }
    376 
    377 void BrowserWindowCocoa::ShowPageInfo(Profile* profile,
    378                                       const GURL& url,
    379                                       const NavigationEntry::SSLStatus& ssl,
    380                                       bool show_history) {
    381   browser::ShowPageInfoBubble(window(), profile, url, ssl, show_history);
    382 }
    383 
    384 void BrowserWindowCocoa::ShowAppMenu() {
    385   // No-op. Mac doesn't support showing the menus via alt keys.
    386 }
    387 
    388 bool BrowserWindowCocoa::PreHandleKeyboardEvent(
    389     const NativeWebKeyboardEvent& event, bool* is_keyboard_shortcut) {
    390   if (event.skip_in_browser || event.type == NativeWebKeyboardEvent::Char)
    391     return false;
    392 
    393   DCHECK(event.os_event != NULL);
    394   int id = GetCommandId(event);
    395   if (id == -1)
    396     return false;
    397 
    398   if (browser_->IsReservedCommandOrKey(id, event))
    399     return HandleKeyboardEventInternal(event.os_event);
    400 
    401   DCHECK(is_keyboard_shortcut != NULL);
    402   *is_keyboard_shortcut = true;
    403 
    404   return false;
    405 }
    406 
    407 void BrowserWindowCocoa::HandleKeyboardEvent(
    408     const NativeWebKeyboardEvent& event) {
    409   if (event.skip_in_browser || event.type == NativeWebKeyboardEvent::Char)
    410     return;
    411 
    412   DCHECK(event.os_event != NULL);
    413   HandleKeyboardEventInternal(event.os_event);
    414 }
    415 
    416 @interface MenuWalker : NSObject
    417 + (NSMenuItem*)itemForKeyEquivalent:(NSEvent*)key
    418                                menu:(NSMenu*)menu;
    419 @end
    420 
    421 @implementation MenuWalker
    422 + (NSMenuItem*)itemForKeyEquivalent:(NSEvent*)key
    423                                menu:(NSMenu*)menu {
    424   NSMenuItem* result = nil;
    425 
    426   for (NSMenuItem *item in [menu itemArray]) {
    427     NSMenu* submenu = [item submenu];
    428     if (submenu) {
    429       if (submenu != [NSApp servicesMenu])
    430         result = [self itemForKeyEquivalent:key
    431                                        menu:submenu];
    432     } else if ([item cr_firesForKeyEventIfEnabled:key]) {
    433       result = item;
    434     }
    435 
    436     if (result)
    437       break;
    438   }
    439 
    440   return result;
    441 }
    442 @end
    443 
    444 int BrowserWindowCocoa::GetCommandId(const NativeWebKeyboardEvent& event) {
    445   if ([event.os_event type] != NSKeyDown)
    446     return -1;
    447 
    448   // Look in menu.
    449   NSMenuItem* item = [MenuWalker itemForKeyEquivalent:event.os_event
    450                                                  menu:[NSApp mainMenu]];
    451 
    452   if (item && [item action] == @selector(commandDispatch:) && [item tag] > 0)
    453     return [item tag];
    454 
    455   // "Close window" doesn't use the |commandDispatch:| mechanism. Menu items
    456   // that do not correspond to IDC_ constants need no special treatment however,
    457   // as they can't be blacklisted in |Browser::IsReservedCommandOrKey()| anyhow.
    458   if (item && [item action] == @selector(performClose:))
    459     return IDC_CLOSE_WINDOW;
    460 
    461   // "Exit" doesn't use the |commandDispatch:| mechanism either.
    462   if (item && [item action] == @selector(terminate:))
    463     return IDC_EXIT;
    464 
    465   // Look in secondary keyboard shortcuts.
    466   NSUInteger modifiers = [event.os_event modifierFlags];
    467   const bool cmdKey = (modifiers & NSCommandKeyMask) != 0;
    468   const bool shiftKey = (modifiers & NSShiftKeyMask) != 0;
    469   const bool cntrlKey = (modifiers & NSControlKeyMask) != 0;
    470   const bool optKey = (modifiers & NSAlternateKeyMask) != 0;
    471   const int keyCode = [event.os_event keyCode];
    472   const unichar keyChar = KeyCharacterForEvent(event.os_event);
    473 
    474   int cmdNum = CommandForWindowKeyboardShortcut(
    475       cmdKey, shiftKey, cntrlKey, optKey, keyCode, keyChar);
    476   if (cmdNum != -1)
    477     return cmdNum;
    478 
    479   cmdNum = CommandForBrowserKeyboardShortcut(
    480       cmdKey, shiftKey, cntrlKey, optKey, keyCode, keyChar);
    481   if (cmdNum != -1)
    482     return cmdNum;
    483 
    484   return -1;
    485 }
    486 
    487 bool BrowserWindowCocoa::HandleKeyboardEventInternal(NSEvent* event) {
    488   ChromeEventProcessingWindow* event_window =
    489       static_cast<ChromeEventProcessingWindow*>(window());
    490   DCHECK([event_window isKindOfClass:[ChromeEventProcessingWindow class]]);
    491 
    492   // Do not fire shortcuts on key up.
    493   if ([event type] == NSKeyDown) {
    494     // Send the event to the menu before sending it to the browser/window
    495     // shortcut handling, so that if a user configures cmd-left to mean
    496     // "previous tab", it takes precedence over the built-in "history back"
    497     // binding. Other than that, the |-redispatchKeyEvent:| call would take care
    498     // of invoking the original menu item shortcut as well.
    499 
    500     if ([[NSApp mainMenu] performKeyEquivalent:event])
    501       return true;
    502 
    503     if ([event_window handleExtraBrowserKeyboardShortcut:event])
    504       return true;
    505 
    506     if ([event_window handleExtraWindowKeyboardShortcut:event])
    507       return true;
    508 
    509     if ([event_window handleDelayedWindowKeyboardShortcut:event])
    510       return true;
    511   }
    512 
    513   return [event_window redispatchKeyEvent:event];
    514 }
    515 
    516 void BrowserWindowCocoa::ShowCreateWebAppShortcutsDialog(
    517     TabContentsWrapper* tab_contents) {
    518   NOTIMPLEMENTED();
    519 }
    520 
    521 void BrowserWindowCocoa::ShowCreateChromeAppShortcutsDialog(
    522     Profile* profile, const Extension* app) {
    523   NOTIMPLEMENTED();
    524 }
    525 
    526 void BrowserWindowCocoa::Cut() {
    527   [NSApp sendAction:@selector(cut:) to:nil from:nil];
    528 }
    529 
    530 void BrowserWindowCocoa::Copy() {
    531   [NSApp sendAction:@selector(copy:) to:nil from:nil];
    532 }
    533 
    534 void BrowserWindowCocoa::Paste() {
    535   [NSApp sendAction:@selector(paste:) to:nil from:nil];
    536 }
    537 
    538 void BrowserWindowCocoa::ToggleTabStripMode() {
    539   [controller_ toggleTabStripDisplayMode];
    540 }
    541 
    542 void BrowserWindowCocoa::OpenTabpose() {
    543   [controller_ openTabpose];
    544 }
    545 
    546 void BrowserWindowCocoa::PrepareForInstant() {
    547   // TODO: implement fade as done on windows.
    548 }
    549 
    550 void BrowserWindowCocoa::ShowInstant(TabContentsWrapper* preview) {
    551   [controller_ showInstant:preview->tab_contents()];
    552 }
    553 
    554 void BrowserWindowCocoa::HideInstant(bool instant_is_active) {
    555   [controller_ hideInstant];
    556 
    557   // TODO: add support for |instant_is_active|.
    558 }
    559 
    560 gfx::Rect BrowserWindowCocoa::GetInstantBounds() {
    561   // Flip coordinates based on the primary screen.
    562   NSScreen* screen = [[NSScreen screens] objectAtIndex:0];
    563   NSRect monitorFrame = [screen frame];
    564   NSRect frame = [controller_ instantFrame];
    565   gfx::Rect bounds(NSRectToCGRect(frame));
    566   bounds.set_y(NSHeight(monitorFrame) - bounds.y() - bounds.height());
    567   return bounds;
    568 }
    569 
    570 void BrowserWindowCocoa::Observe(NotificationType type,
    571                                  const NotificationSource& source,
    572                                  const NotificationDetails& details) {
    573   switch (type.value) {
    574     // Only the key window gets a direct toggle from the menu.
    575     // Other windows hear about it from the notification.
    576     case NotificationType::BOOKMARK_BAR_VISIBILITY_PREF_CHANGED:
    577       [controller_ updateBookmarkBarVisibilityWithAnimation:YES];
    578       break;
    579     case NotificationType::SIDEBAR_CHANGED:
    580       UpdateSidebarForContents(
    581           Details<SidebarContainer>(details)->tab_contents());
    582       break;
    583     default:
    584       NOTREACHED();  // we don't ask for anything else!
    585       break;
    586   }
    587 }
    588 
    589 void BrowserWindowCocoa::DestroyBrowser() {
    590   [controller_ destroyBrowser];
    591 
    592   // at this point the controller is dead (autoreleased), so
    593   // make sure we don't try to reference it any more.
    594 }
    595 
    596 NSWindow* BrowserWindowCocoa::window() const {
    597   return [controller_ window];
    598 }
    599 
    600 void BrowserWindowCocoa::UpdateSidebarForContents(TabContents* tab_contents) {
    601   if (tab_contents == browser_->GetSelectedTabContents()) {
    602     [controller_ updateSidebarForContents:tab_contents];
    603   }
    604 }
    605