// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/ui/cocoa/browser_window_cocoa.h" #include "base/bind.h" #include "base/command_line.h" #include "base/logging.h" #import "base/mac/sdk_forward_declarations.h" #include "base/message_loop/message_loop.h" #include "base/prefs/pref_service.h" #include "base/strings/string_util.h" #include "base/strings/sys_string_conversions.h" #include "chrome/app/chrome_command_ids.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/download/download_shelf.h" #include "chrome/browser/extensions/extension_util.h" #include "chrome/browser/extensions/tab_helper.h" #include "chrome/browser/fullscreen.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/shell_integration.h" #include "chrome/browser/signin/chrome_signin_helper.h" #include "chrome/browser/translate/chrome_translate_client.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_command_controller.h" #include "chrome/browser/ui/browser_commands_mac.h" #include "chrome/browser/ui/browser_list.h" #include "chrome/browser/ui/browser_window_state.h" #import "chrome/browser/ui/cocoa/autofill/save_card_bubble_view_bridge.h" #import "chrome/browser/ui/cocoa/browser/edit_search_engine_cocoa_controller.h" #import "chrome/browser/ui/cocoa/browser_window_controller.h" #import "chrome/browser/ui/cocoa/browser_window_utils.h" #import "chrome/browser/ui/cocoa/chrome_event_processing_window.h" #import "chrome/browser/ui/cocoa/constrained_window/constrained_window_sheet_controller.h" #import "chrome/browser/ui/cocoa/download/download_shelf_controller.h" #import "chrome/browser/ui/cocoa/extensions/browser_actions_controller.h" #include "chrome/browser/ui/cocoa/find_bar/find_bar_bridge.h" #import "chrome/browser/ui/cocoa/info_bubble_view.h" #include "chrome/browser/ui/cocoa/key_equivalent_constants.h" #import "chrome/browser/ui/cocoa/location_bar/location_bar_view_mac.h" #import "chrome/browser/ui/cocoa/nsmenuitem_additions.h" #import "chrome/browser/ui/cocoa/profiles/avatar_base_controller.h" #import "chrome/browser/ui/cocoa/profiles/avatar_menu_bubble_controller.h" #include "chrome/browser/ui/cocoa/restart_browser.h" #include "chrome/browser/ui/cocoa/status_bubble_mac.h" #include "chrome/browser/ui/cocoa/task_manager_mac.h" #import "chrome/browser/ui/cocoa/toolbar/toolbar_controller.h" #import "chrome/browser/ui/cocoa/website_settings/website_settings_bubble_controller.h" #include "chrome/browser/ui/exclusive_access/fullscreen_controller.h" #include "chrome/browser/ui/search/search_model.h" #include "chrome/browser/ui/tabs/tab_strip_model.h" #include "chrome/browser/web_applications/web_app.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/pref_names.h" #include "chrome/grit/generated_resources.h" #include "components/bookmarks/common/bookmark_pref_names.h" #include "components/translate/core/browser/language_state.h" #include "content/public/browser/native_web_keyboard_event.h" #include "content/public/browser/notification_details.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/notification_source.h" #include "content/public/browser/web_contents.h" #include "extensions/browser/extension_registry.h" #include "extensions/browser/extension_system.h" #include "extensions/common/constants.h" #include "grit/components_strings.h" #include "ui/base/l10n/l10n_util_mac.h" #include "ui/events/keycodes/keyboard_codes.h" #include "ui/gfx/geometry/rect.h" #if defined(ENABLE_ONE_CLICK_SIGNIN) #import "chrome/browser/ui/cocoa/one_click_signin_bubble_controller.h" #import "chrome/browser/ui/cocoa/one_click_signin_dialog_controller.h" #endif using content::NativeWebKeyboardEvent; using content::WebContents; namespace { // These UI constants are used in BrowserWindowCocoa::ShowBookmarkAppBubble. // Used for defining the layout of the NSAlert and NSTextField within the // accessory view. const int kAppTextFieldVerticalSpacing = 2; const int kAppTextFieldWidth = 200; const int kAppTextFieldHeight = 22; const int kBookmarkAppBubbleViewWidth = 200; const int kBookmarkAppBubbleViewHeight = 46; const int kIconPreviewTargetSize = 128; base::string16 TrimText(NSString* controlText) { base::string16 text = base::SysNSStringToUTF16(controlText); base::TrimWhitespace(text, base::TRIM_ALL, &text); return text; } } // namespace @interface TextRequiringDelegate : NSObject<NSTextFieldDelegate> { @private // Disable |control_| when text changes to just whitespace or empty string. NSControl* control_; } - (id)initWithControl:(NSControl*)control text:(NSString*)text; - (void)controlTextDidChange:(NSNotification*)notification; @end @interface TextRequiringDelegate () - (void)validateText:(NSString*)text; @end @implementation TextRequiringDelegate - (id)initWithControl:(NSControl*)control text:(NSString*)text { if ((self = [super init])) { control_ = control; [self validateText:text]; } return self; } - (void)controlTextDidChange:(NSNotification*)notification { [self validateText:[[notification object] stringValue]]; } - (void)validateText:(NSString*)text { [control_ setEnabled:TrimText(text).empty() ? NO : YES]; } @end BrowserWindowCocoa::BrowserWindowCocoa(Browser* browser, BrowserWindowController* controller) : browser_(browser), controller_(controller), initial_show_state_(ui::SHOW_STATE_DEFAULT), attention_request_id_(0) { gfx::Rect bounds; chrome::GetSavedWindowBoundsAndShowState(browser_, &bounds, &initial_show_state_); browser_->search_model()->AddObserver(this); } BrowserWindowCocoa::~BrowserWindowCocoa() { browser_->search_model()->RemoveObserver(this); } void BrowserWindowCocoa::Show() { // The Browser associated with this browser window must become the active // browser at the time |Show()| is called. This is the natural behaviour under // Windows, but |-makeKeyAndOrderFront:| won't send |-windowDidBecomeMain:| // until we return to the runloop. Therefore any calls to // |chrome::FindLastActiveWithHostDesktopType| will return the previous // browser instead if we don't explicitly set it here. BrowserList::SetLastActive(browser_); bool is_session_restore = browser_->is_session_restore(); NSWindowAnimationBehavior saved_animation_behavior = NSWindowAnimationBehaviorDefault; bool did_save_animation_behavior = false; // Turn off swishing when restoring windows or showing an app. if ((is_session_restore || browser_->is_app()) && [window() respondsToSelector:@selector(animationBehavior)] && [window() respondsToSelector:@selector(setAnimationBehavior:)]) { did_save_animation_behavior = true; saved_animation_behavior = [window() animationBehavior]; [window() setAnimationBehavior:NSWindowAnimationBehaviorNone]; } { TRACE_EVENT0("ui", "BrowserWindowCocoa::Show makeKeyAndOrderFront"); // This call takes up a substantial part of startup time, and an even more // substantial part of startup time when any CALayers are part of the // window's NSView heirarchy. [window() makeKeyAndOrderFront:controller_]; } // When creating windows from nibs it is necessary to |makeKeyAndOrderFront:| // prior to |orderOut:| then |miniaturize:| when restoring windows in the // minimized state. if (initial_show_state_ == ui::SHOW_STATE_MINIMIZED) { [window() orderOut:controller_]; [window() miniaturize:controller_]; } else if (initial_show_state_ == ui::SHOW_STATE_FULLSCREEN) { chrome::ToggleFullscreenWithToolbarOrFallback(browser_); } initial_show_state_ = ui::SHOW_STATE_DEFAULT; // Restore window animation behavior. if (did_save_animation_behavior) [window() setAnimationBehavior:saved_animation_behavior]; browser_->OnWindowDidShow(); } void BrowserWindowCocoa::ShowInactive() { [window() orderFront:controller_]; } void BrowserWindowCocoa::Hide() { [window() orderOut:controller_]; } void BrowserWindowCocoa::SetBounds(const gfx::Rect& bounds) { gfx::Rect real_bounds = [controller_ enforceMinWindowSize:bounds]; ExitFullscreen(); NSRect cocoa_bounds = NSMakeRect(real_bounds.x(), 0, real_bounds.width(), real_bounds.height()); // Flip coordinates based on the primary screen. NSScreen* screen = [[NSScreen screens] firstObject]; cocoa_bounds.origin.y = NSHeight([screen frame]) - real_bounds.height() - real_bounds.y(); [window() setFrame:cocoa_bounds display:YES]; } // Callers assume that this doesn't immediately delete the Browser object. // The controller implementing the window delegate methods called from // |-performClose:| must take precautions to ensure that. void BrowserWindowCocoa::Close() { // If there is an overlay window, we contain a tab being dragged between // windows. Don't hide the window as it makes the UI extra confused. We can // still close the window, as that will happen when the drag completes. if ([controller_ overlayWindow]) { [controller_ deferPerformClose]; } else { // Using |-performClose:| can prevent the window from actually closing if // a JavaScript beforeunload handler opens an alert during shutdown, as // documented at <http://crbug.com/118424>. Re-implement // -[NSWindow performClose:] as closely as possible to how Apple documents // it. // // Before calling |-close|, hide the window immediately. |-performClose:| // would do something similar, and this ensures that the window is removed // from AppKit's display list. Not doing so can lead to crashes like // <http://crbug.com/156101>. id<NSWindowDelegate> delegate = [window() delegate]; SEL window_should_close = @selector(windowShouldClose:); if ([delegate respondsToSelector:window_should_close]) { if ([delegate windowShouldClose:window()]) { [window() orderOut:nil]; [window() close]; } } else if ([window() respondsToSelector:window_should_close]) { if ([window() performSelector:window_should_close withObject:window()]) { [window() orderOut:nil]; [window() close]; } } else { [window() orderOut:nil]; [window() close]; } } } void BrowserWindowCocoa::Activate() { [controller_ activate]; } void BrowserWindowCocoa::Deactivate() { // TODO(jcivelli): http://crbug.com/51364 Implement me. NOTIMPLEMENTED(); } void BrowserWindowCocoa::FlashFrame(bool flash) { if (flash) { attention_request_id_ = [NSApp requestUserAttention:NSInformationalRequest]; } else { [NSApp cancelUserAttentionRequest:attention_request_id_]; attention_request_id_ = 0; } } bool BrowserWindowCocoa::IsAlwaysOnTop() const { return false; } void BrowserWindowCocoa::SetAlwaysOnTop(bool always_on_top) { // Not implemented for browser windows. NOTIMPLEMENTED(); } bool BrowserWindowCocoa::IsActive() const { return [window() isKeyWindow]; } gfx::NativeWindow BrowserWindowCocoa::GetNativeWindow() const { return window(); } StatusBubble* BrowserWindowCocoa::GetStatusBubble() { return [controller_ statusBubble]; } void BrowserWindowCocoa::UpdateTitleBar() { NSString* newTitle = WindowTitle(); pending_window_title_.reset([BrowserWindowUtils scheduleReplaceOldTitle:pending_window_title_.get() withNewTitle:newTitle forWindow:window()]); } NSString* BrowserWindowCocoa::WindowTitle() { if (media_state_ == TAB_MEDIA_STATE_AUDIO_PLAYING) { return l10n_util::GetNSStringF(IDS_WINDOW_AUDIO_PLAYING_MAC, browser_->GetWindowTitleForCurrentTab(), base::SysNSStringToUTF16(@"🔊")); } else if (media_state_ == TAB_MEDIA_STATE_AUDIO_MUTING) { return l10n_util::GetNSStringF(IDS_WINDOW_AUDIO_MUTING_MAC, browser_->GetWindowTitleForCurrentTab(), base::SysNSStringToUTF16(@"🔇")); } return base::SysUTF16ToNSString(browser_->GetWindowTitleForCurrentTab()); } void BrowserWindowCocoa::BookmarkBarStateChanged( BookmarkBar::AnimateChangeType change_type) { [[controller_ bookmarkBarController] updateState:browser_->bookmark_bar_state() changeType:change_type]; } void BrowserWindowCocoa::UpdateDevTools() { [controller_ updateDevToolsForContents: browser_->tab_strip_model()->GetActiveWebContents()]; } void BrowserWindowCocoa::UpdateLoadingAnimations(bool should_animate) { // Do nothing on Mac. } void BrowserWindowCocoa::SetStarredState(bool is_starred) { [controller_ setStarredState:is_starred]; } void BrowserWindowCocoa::SetTranslateIconToggled(bool is_lit) { [controller_ setCurrentPageIsTranslated:is_lit]; } void BrowserWindowCocoa::OnActiveTabChanged(content::WebContents* old_contents, content::WebContents* new_contents, int index, int reason) { [controller_ onActiveTabChanged:old_contents to:new_contents]; // TODO(pkasting): Perhaps the code in // TabStripController::activateTabWithContents should move here? Or this // should call that (instead of TabStripModelObserverBridge doing so)? It's // not obvious to me why Mac doesn't handle tab changes in BrowserWindow the // way views and GTK do. // See http://crbug.com/340720 for discussion. } void BrowserWindowCocoa::ZoomChangedForActiveTab(bool can_show_bubble) { [controller_ zoomChangedForActiveTab:can_show_bubble ? YES : NO]; } gfx::Rect BrowserWindowCocoa::GetRestoredBounds() const { // Flip coordinates based on the primary screen. NSScreen* screen = [[NSScreen screens] firstObject]; NSRect frame = [controller_ regularWindowFrame]; gfx::Rect bounds(frame.origin.x, 0, NSWidth(frame), NSHeight(frame)); bounds.set_y(NSHeight([screen frame]) - NSMaxY(frame)); return bounds; } ui::WindowShowState BrowserWindowCocoa::GetRestoredState() const { if (IsMaximized()) return ui::SHOW_STATE_MAXIMIZED; if (IsMinimized()) return ui::SHOW_STATE_MINIMIZED; return ui::SHOW_STATE_NORMAL; } gfx::Rect BrowserWindowCocoa::GetBounds() const { return GetRestoredBounds(); } bool BrowserWindowCocoa::IsMaximized() const { // -isZoomed returns YES if the window's frame equals the rect returned by // -windowWillUseStandardFrame:defaultFrame:, even if the window is in the // dock, so have to explicitly check for miniaturization state first. return ![window() isMiniaturized] && [window() isZoomed]; } bool BrowserWindowCocoa::IsMinimized() const { return [window() isMiniaturized]; } void BrowserWindowCocoa::Maximize() { // Zoom toggles so only call if not already maximized. if (!IsMaximized()) [window() zoom:controller_]; } void BrowserWindowCocoa::Minimize() { [window() miniaturize:controller_]; } void BrowserWindowCocoa::Restore() { if (IsMaximized()) [window() zoom:controller_]; // Toggles zoom mode. else if (IsMinimized()) [window() deminiaturize:controller_]; } // See browser_window_controller.h for a detailed explanation of the logic in // this method. void BrowserWindowCocoa::EnterFullscreen(const GURL& url, ExclusiveAccessBubbleType bubble_type, bool with_toolbar) { if (browser_->exclusive_access_manager() ->fullscreen_controller() ->IsWindowFullscreenForTabOrPending()) [controller_ enterWebContentFullscreenForURL:url bubbleType:bubble_type]; else if (!url.is_empty()) [controller_ enterExtensionFullscreenForURL:url bubbleType:bubble_type]; else [controller_ enterBrowserFullscreenWithToolbar:with_toolbar]; } void BrowserWindowCocoa::ExitFullscreen() { [controller_ exitAnyFullscreen]; } void BrowserWindowCocoa::UpdateExclusiveAccessExitBubbleContent( const GURL& url, ExclusiveAccessBubbleType bubble_type) { [controller_ updateFullscreenExitBubbleURL:url bubbleType:bubble_type]; } void BrowserWindowCocoa::OnExclusiveAccessUserInput() { // TODO(mgiuca): Route this signal to the exclusive access bubble on Mac. } bool BrowserWindowCocoa::ShouldHideUIForFullscreen() const { // On Mac, fullscreen mode has most normal things (in a slide-down panel). return false; } bool BrowserWindowCocoa::IsFullscreen() const { return [controller_ isInAnyFullscreenMode]; } bool BrowserWindowCocoa::IsFullscreenBubbleVisible() const { return false; } bool BrowserWindowCocoa::SupportsFullscreenWithToolbar() const { return chrome::mac::SupportsSystemFullscreen(); } void BrowserWindowCocoa::UpdateFullscreenWithToolbar(bool with_toolbar) { [controller_ updateFullscreenWithToolbar:with_toolbar]; } void BrowserWindowCocoa::ToggleFullscreenToolbar() { [controller_ toggleFullscreenToolbar]; } bool BrowserWindowCocoa::IsFullscreenWithToolbar() const { return IsFullscreen() && ![controller_ inPresentationMode]; } bool BrowserWindowCocoa::ShouldHideFullscreenToolbar() const { return [controller_ shouldHideFullscreenToolbar]; } void BrowserWindowCocoa::ConfirmAddSearchProvider( TemplateURL* template_url, Profile* profile) { // The controller will release itself when the window closes. EditSearchEngineCocoaController* editor = [[EditSearchEngineCocoaController alloc] initWithProfile:profile delegate:NULL templateURL:template_url]; [NSApp beginSheet:[editor window] modalForWindow:window() modalDelegate:controller_ didEndSelector:@selector(sheetDidEnd:returnCode:context:) contextInfo:NULL]; } LocationBar* BrowserWindowCocoa::GetLocationBar() const { return [controller_ locationBarBridge]; } void BrowserWindowCocoa::SetFocusToLocationBar(bool select_all) { [controller_ focusLocationBar:select_all ? YES : NO]; } void BrowserWindowCocoa::UpdateReloadStopState(bool is_loading, bool force) { [controller_ setIsLoading:is_loading force:force]; } void BrowserWindowCocoa::UpdateToolbar(content::WebContents* contents) { [controller_ updateToolbarWithContents:contents]; } void BrowserWindowCocoa::ResetToolbarTabState(content::WebContents* contents) { [controller_ resetTabState:contents]; } void BrowserWindowCocoa::FocusToolbar() { // Not needed on the Mac. } ToolbarActionsBar* BrowserWindowCocoa::GetToolbarActionsBar() { if ([controller_ hasToolbar]) return [[[controller_ toolbarController] browserActionsController] toolbarActionsBar]; return nullptr; } void BrowserWindowCocoa::ToolbarSizeChanged(bool is_animating) { // Not needed on the Mac. } void BrowserWindowCocoa::FocusAppMenu() { // Chrome uses the standard Mac OS X menu bar, so this isn't needed. } void BrowserWindowCocoa::RotatePaneFocus(bool forwards) { // Not needed on the Mac. } void BrowserWindowCocoa::FocusBookmarksToolbar() { // Not needed on the Mac. } void BrowserWindowCocoa::FocusInfobars() { // Not needed on the Mac. } bool BrowserWindowCocoa::IsBookmarkBarVisible() const { return browser_->profile()->GetPrefs()->GetBoolean( bookmarks::prefs::kShowBookmarkBar); } bool BrowserWindowCocoa::IsBookmarkBarAnimating() const { return [controller_ isBookmarkBarAnimating]; } bool BrowserWindowCocoa::IsTabStripEditable() const { return ![controller_ isDragSessionActive]; } bool BrowserWindowCocoa::IsToolbarVisible() const { return browser_->SupportsWindowFeature(Browser::FEATURE_TOOLBAR) || browser_->SupportsWindowFeature(Browser::FEATURE_LOCATIONBAR); } gfx::Rect BrowserWindowCocoa::GetRootWindowResizerRect() const { if (IsDownloadShelfVisible()) return gfx::Rect(); NSRect tabRect = [controller_ selectedTabGrowBoxRect]; return gfx::Rect(NSRectToCGRect(tabRect)); } void BrowserWindowCocoa::AddFindBar( FindBarCocoaController* find_bar_cocoa_controller) { [controller_ addFindBar:find_bar_cocoa_controller]; } void BrowserWindowCocoa::UpdateMediaState(TabMediaState media_state) { media_state_ = media_state; UpdateTitleBar(); } void BrowserWindowCocoa::ShowUpdateChromeDialog() { restart_browser::RequestRestart(window()); } void BrowserWindowCocoa::ShowBookmarkBubble(const GURL& url, bool already_bookmarked) { [controller_ showBookmarkBubbleForURL:url alreadyBookmarked:(already_bookmarked ? YES : NO)]; } void BrowserWindowCocoa::ShowBookmarkAppBubble( const WebApplicationInfo& web_app_info, const ShowBookmarkAppBubbleCallback& callback) { base::scoped_nsobject<NSAlert> alert([[NSAlert alloc] init]); [alert setMessageText:l10n_util::GetNSString( IDS_ADD_TO_APPLICATIONS_BUBBLE_TITLE)]; [alert setAlertStyle:NSInformationalAlertStyle]; NSButton* continue_button = [alert addButtonWithTitle:l10n_util::GetNSString(IDS_OK)]; [continue_button setKeyEquivalent:kKeyEquivalentReturn]; NSButton* cancel_button = [alert addButtonWithTitle:l10n_util::GetNSString(IDS_CANCEL)]; [cancel_button setKeyEquivalent:kKeyEquivalentEscape]; base::scoped_nsobject<NSButton> open_as_window_checkbox( [[NSButton alloc] initWithFrame:NSZeroRect]); [open_as_window_checkbox setButtonType:NSSwitchButton]; [open_as_window_checkbox setTitle:l10n_util::GetNSString(IDS_BOOKMARK_APP_BUBBLE_OPEN_AS_WINDOW)]; [open_as_window_checkbox setState:web_app_info.open_as_window]; [open_as_window_checkbox sizeToFit]; base::scoped_nsobject<NSTextField> app_title([[NSTextField alloc] initWithFrame:NSMakeRect(0, kAppTextFieldHeight + kAppTextFieldVerticalSpacing, kAppTextFieldWidth, kAppTextFieldHeight)]); NSString* original_title = SysUTF16ToNSString(web_app_info.title); [[app_title cell] setWraps:NO]; [[app_title cell] setScrollable:YES]; [app_title setStringValue:original_title]; base::scoped_nsobject<TextRequiringDelegate> delegate( [[TextRequiringDelegate alloc] initWithControl:continue_button text:[app_title stringValue]]); [app_title setDelegate:delegate]; base::scoped_nsobject<NSView> view([[NSView alloc] initWithFrame:NSMakeRect(0, 0, kBookmarkAppBubbleViewWidth, kBookmarkAppBubbleViewHeight)]); // When CanHostedAppsOpenInWindows() returns false, do not show the open as // window checkbox to avoid confusing users. if (extensions::util::CanHostedAppsOpenInWindows()) [view addSubview:open_as_window_checkbox]; [view addSubview:app_title]; [alert setAccessoryView:view]; // Find the image with target size. // Assumes that the icons are sorted in ascending order of size. if (!web_app_info.icons.empty()) { for (const WebApplicationInfo::IconInfo& info : web_app_info.icons) { if (info.width == kIconPreviewTargetSize && info.height == kIconPreviewTargetSize) { gfx::Image icon_image = gfx::Image::CreateFrom1xBitmap(info.data); [alert setIcon:icon_image.ToNSImage()]; break; } } } NSInteger response = [alert runModal]; // Prevent |app_title| from accessing |delegate| after it's destroyed. [app_title setDelegate:nil]; if (response == NSAlertFirstButtonReturn) { WebApplicationInfo updated_info = web_app_info; updated_info.open_as_window = [open_as_window_checkbox state] == NSOnState; updated_info.title = TrimText([app_title stringValue]); callback.Run(true, updated_info); return; } callback.Run(false, web_app_info); } autofill::SaveCardBubbleView* BrowserWindowCocoa::ShowSaveCreditCardBubble( content::WebContents* web_contents, autofill::SaveCardBubbleController* controller, bool user_gesture) { return new autofill::SaveCardBubbleViewBridge(controller, controller_); } void BrowserWindowCocoa::ShowTranslateBubble( content::WebContents* contents, translate::TranslateStep step, translate::TranslateErrors::Type error_type, bool is_user_gesture) { ChromeTranslateClient* chrome_translate_client = ChromeTranslateClient::FromWebContents(contents); translate::LanguageState& language_state = chrome_translate_client->GetLanguageState(); language_state.SetTranslateEnabled(true); [controller_ showTranslateBubbleForWebContents:contents step:step errorType:error_type]; } #if defined(ENABLE_ONE_CLICK_SIGNIN) void BrowserWindowCocoa::ShowOneClickSigninBubble( OneClickSigninBubbleType type, const base::string16& email, const base::string16& error_message, const StartSyncCallback& start_sync_callback) { WebContents* web_contents = browser_->tab_strip_model()->GetActiveWebContents(); if (type == ONE_CLICK_SIGNIN_BUBBLE_TYPE_BUBBLE) { base::scoped_nsobject<OneClickSigninBubbleController> bubble_controller([ [OneClickSigninBubbleController alloc] initWithBrowserWindowController:cocoa_controller() webContents:web_contents errorMessage:base::SysUTF16ToNSString(error_message) callback:start_sync_callback]); [bubble_controller showWindow:nil]; } else { // Deletes itself when the dialog closes. new OneClickSigninDialogController( web_contents, start_sync_callback, email); } } #endif bool BrowserWindowCocoa::IsDownloadShelfVisible() const { return [controller_ isDownloadShelfVisible] != NO; } DownloadShelf* BrowserWindowCocoa::GetDownloadShelf() { [controller_ createAndAddDownloadShelf]; DownloadShelfController* shelfController = [controller_ downloadShelf]; return [shelfController bridge]; } // We allow closing the window here since the real quit decision on Mac is made // in [AppController quit:]. void BrowserWindowCocoa::ConfirmBrowserCloseWithPendingDownloads( int download_count, Browser::DownloadClosePreventionType dialog_type, bool app_modal, const base::Callback<void(bool)>& callback) { callback.Run(true); } void BrowserWindowCocoa::UserChangedTheme() { [controller_ userChangedTheme]; } void BrowserWindowCocoa::ShowWebsiteSettings( Profile* profile, content::WebContents* web_contents, const GURL& url, const security_state::SecurityStateModel::SecurityInfo& security_info) { WebsiteSettingsUIBridge::Show(window(), profile, web_contents, url, security_info); } void BrowserWindowCocoa::ShowAppMenu() { // No-op. Mac doesn't support showing the menus via alt keys. } bool BrowserWindowCocoa::PreHandleKeyboardEvent( const NativeWebKeyboardEvent& event, bool* is_keyboard_shortcut) { // Handle ESC to dismiss permission bubbles, but still forward it // to the window afterwards. if (event.windowsKeyCode == ui::VKEY_ESCAPE) [controller_ dismissPermissionBubble]; if (![BrowserWindowUtils shouldHandleKeyboardEvent:event]) return false; if (event.type == blink::WebInputEvent::RawKeyDown && [controller_ handledByExtensionCommand:event.os_event priority:ui::AcceleratorManager::kHighPriority]) return true; int id = [BrowserWindowUtils getCommandId:event]; if (id == -1) return false; if (browser_->command_controller()->IsReservedCommandOrKey(id, event)) { return [BrowserWindowUtils handleKeyboardEvent:event.os_event inWindow:window()]; } DCHECK(is_keyboard_shortcut); *is_keyboard_shortcut = true; return false; } void BrowserWindowCocoa::HandleKeyboardEvent( const NativeWebKeyboardEvent& event) { if ([BrowserWindowUtils shouldHandleKeyboardEvent:event]) [BrowserWindowUtils handleKeyboardEvent:event.os_event inWindow:window()]; } void BrowserWindowCocoa::CutCopyPaste(int command_id) { if (command_id == IDC_CUT) [NSApp sendAction:@selector(cut:) to:nil from:nil]; else if (command_id == IDC_COPY) [NSApp sendAction:@selector(copy:) to:nil from:nil]; else [NSApp sendAction:@selector(paste:) to:nil from:nil]; } WindowOpenDisposition BrowserWindowCocoa::GetDispositionForPopupBounds( const gfx::Rect& bounds) { // When using Cocoa's System Fullscreen mode, convert popups into tabs. if ([controller_ isInAppKitFullscreen]) return NEW_FOREGROUND_TAB; return NEW_POPUP; } FindBar* BrowserWindowCocoa::CreateFindBar() { // We could push the AddFindBar() call into the FindBarBridge // constructor or the FindBarCocoaController init, but that makes // unit testing difficult, since we would also require a // BrowserWindow object. FindBarBridge* bridge = new FindBarBridge(browser_); AddFindBar(bridge->find_bar_cocoa_controller()); return bridge; } web_modal::WebContentsModalDialogHost* BrowserWindowCocoa::GetWebContentsModalDialogHost() { DCHECK([controller_ window]); ConstrainedWindowSheetController* sheet_controller = [ConstrainedWindowSheetController controllerForParentWindow:[controller_ window]]; DCHECK(sheet_controller); return [sheet_controller dialogHost]; } extensions::ActiveTabPermissionGranter* BrowserWindowCocoa::GetActiveTabPermissionGranter() { WebContents* web_contents = browser_->tab_strip_model()->GetActiveWebContents(); if (!web_contents) return NULL; extensions::TabHelper* tab_helper = extensions::TabHelper::FromWebContents(web_contents); return tab_helper ? tab_helper->active_tab_permission_granter() : NULL; } void BrowserWindowCocoa::ModelChanged(const SearchModel::State& old_state, const SearchModel::State& new_state) { } void BrowserWindowCocoa::DestroyBrowser() { [controller_ destroyBrowser]; // at this point the controller is dead (autoreleased), so // make sure we don't try to reference it any more. } NSWindow* BrowserWindowCocoa::window() const { return [controller_ window]; } void BrowserWindowCocoa::ShowAvatarBubbleFromAvatarButton( AvatarBubbleMode mode, const signin::ManageAccountsParams& manage_accounts_params, signin_metrics::AccessPoint access_point) { AvatarBaseController* controller = [controller_ avatarButtonController]; NSView* anchor = [controller buttonView]; if ([anchor isHiddenOrHasHiddenAncestor]) anchor = [[controller_ toolbarController] appMenuButton]; [controller showAvatarBubbleAnchoredAt:anchor withMode:mode withServiceType:manage_accounts_params.service_type fromAccessPoint:access_point]; } void BrowserWindowCocoa::ShowModalSigninWindow( AvatarBubbleMode mode, signin_metrics::AccessPoint access_point) { NOTREACHED(); } void BrowserWindowCocoa::CloseModalSigninWindow() { NOTREACHED(); } void BrowserWindowCocoa::ShowModalSyncConfirmationWindow() { NOTREACHED(); } int BrowserWindowCocoa::GetRenderViewHeightInsetWithDetachedBookmarkBar() { if (browser_->bookmark_bar_state() != BookmarkBar::DETACHED) return 0; return 40; } void BrowserWindowCocoa::ExecuteExtensionCommand( const extensions::Extension* extension, const extensions::Command& command) { [cocoa_controller() executeExtensionCommand:extension->id() command:command]; } ExclusiveAccessContext* BrowserWindowCocoa::GetExclusiveAccessContext() { return this; } Profile* BrowserWindowCocoa::GetProfile() { return browser_->profile(); } WebContents* BrowserWindowCocoa::GetActiveWebContents() { return browser_->tab_strip_model()->GetActiveWebContents(); } void BrowserWindowCocoa::UnhideDownloadShelf() { GetDownloadShelf()->Unhide(); } void BrowserWindowCocoa::HideDownloadShelf() { GetDownloadShelf()->Hide(); StatusBubble* statusBubble = GetStatusBubble(); if (statusBubble) statusBubble->Hide(); }