blob: 79b2d8902c673089474f0415415b087702024cd8 [file] [log] [blame]
[email protected]71c0eb92012-01-03 17:57:301// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]1bcdb532009-01-16 17:47:572// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]2c47bc12009-04-10 20:14:005#import "chrome/browser/app_controller_mac.h"
[email protected]1bcdb532009-01-16 17:47:576
[email protected]d2a848b12013-09-09 04:13:237#include "apps/app_shim/app_shim_mac.h"
[email protected]90a4b9d2013-07-31 12:14:408#include "apps/app_shim/extension_app_shim_handler_mac.h"
[email protected]f6d9b282013-08-09 11:03:209#include "apps/shell_window_registry.h"
[email protected]d630d7d52010-02-21 00:55:1110#include "base/auto_reset.h"
[email protected]0b8caad2011-11-28 23:33:2711#include "base/bind.h"
[email protected]57750f822009-04-21 21:43:0912#include "base/command_line.h"
[email protected]57999812013-02-24 05:40:5213#include "base/files/file_path.h"
[email protected]151c4a62011-04-22 04:15:1314#include "base/mac/foundation_util.h"
[email protected]0378bf42011-01-01 18:20:1415#include "base/mac/mac_util.h"
[email protected]fa1e0e12013-07-18 00:10:1416#include "base/message_loop/message_loop.h"
[email protected]3853a4c2013-02-11 17:15:5717#include "base/prefs/pref_service.h"
[email protected]3ea1b182013-02-08 22:38:4118#include "base/strings/string_number_conversions.h"
[email protected]3268d7b72013-03-28 17:41:4319#include "base/strings/sys_string_conversions.h"
[email protected]135cb802013-06-09 16:44:2020#include "base/strings/utf_string_conversions.h"
[email protected]1a3aba82010-11-08 23:52:5421#include "chrome/app/chrome_command_ids.h"
[email protected]a07676b22011-06-17 16:36:5322#include "chrome/browser/background/background_application_list_model.h"
[email protected]78d78a62011-09-29 18:24:3323#include "chrome/browser/background/background_mode_manager.h"
[email protected]5c238752009-06-13 10:29:0724#include "chrome/browser/browser_process.h"
[email protected]41741a962009-02-18 21:51:3925#include "chrome/browser/browser_shutdown.h"
[email protected]fdf40f3e2013-07-11 23:55:4626#include "chrome/browser/chrome_notification_types.h"
[email protected]2c47bc12009-04-10 20:14:0027#include "chrome/browser/command_updater.h"
[email protected]9bb54ee2011-10-12 17:43:3528#include "chrome/browser/download/download_service.h"
29#include "chrome/browser/download/download_service_factory.h"
[email protected]ccfab592013-01-15 06:24:3230#include "chrome/browser/extensions/extension_service.h"
31#include "chrome/browser/extensions/extension_system.h"
[email protected]e4a377cf2011-10-05 02:51:4932#include "chrome/browser/first_run/first_run.h"
[email protected]2e6389f2012-05-18 19:41:2533#include "chrome/browser/lifetime/application_lifetime.h"
[email protected]a529af52011-08-15 20:56:1734#include "chrome/browser/printing/print_dialog_cloud.h"
[email protected]1c62b2f2013-06-28 00:15:0035#include "chrome/browser/profiles/profile_info_cache_observer.h"
[email protected]8ecad5e2010-12-02 21:18:3336#include "chrome/browser/profiles/profile_manager.h"
[email protected]de71ae992013-07-18 03:30:3837#include "chrome/browser/profiles/profiles_state.h"
[email protected]cbf160aa2013-11-05 17:54:5538#include "chrome/browser/service_process/service_process_control.h"
[email protected]8d4bbdc2012-09-20 21:36:4939#include "chrome/browser/sessions/session_restore.h"
[email protected]d2912a22011-03-15 15:20:5040#include "chrome/browser/sessions/session_service.h"
[email protected]92371eb2011-04-28 11:50:1541#include "chrome/browser/sessions/session_service_factory.h"
[email protected]fbc947b2009-06-19 13:28:2442#include "chrome/browser/sessions/tab_restore_service.h"
[email protected]92371eb2011-04-28 11:50:1543#include "chrome/browser/sessions/tab_restore_service_factory.h"
[email protected]074311a2013-02-28 23:14:0944#include "chrome/browser/signin/signin_manager.h"
45#include "chrome/browser/signin/signin_manager_factory.h"
[email protected]3d27d272013-07-31 03:15:1646#include "chrome/browser/signin/signin_promo.h"
[email protected]f9bc9b92009-11-24 00:55:3547#include "chrome/browser/sync/profile_sync_service.h"
[email protected]40f047972009-11-25 03:54:4048#include "chrome/browser/sync/sync_ui_util.h"
[email protected]ae04f592010-11-18 20:41:3549#include "chrome/browser/ui/browser.h"
[email protected]05454532013-01-22 21:09:0850#include "chrome/browser/ui/browser_command_controller.h"
[email protected]a37d4b02012-06-25 21:56:1051#include "chrome/browser/ui/browser_commands.h"
[email protected]d8748142012-05-16 21:13:4352#include "chrome/browser/ui/browser_finder.h"
[email protected]b4207c42013-02-12 06:44:2053#include "chrome/browser/ui/browser_iterator.h"
[email protected]a13b76f2012-06-20 15:36:2954#include "chrome/browser/ui/browser_mac.h"
[email protected]ae04f592010-11-18 20:41:3555#include "chrome/browser/ui/browser_window.h"
[email protected]5d9cace72012-06-21 16:07:1256#include "chrome/browser/ui/chrome_pages.h"
[email protected]edb2d032013-08-30 09:03:1757#import "chrome/browser/ui/cocoa/apps/app_shim_menu_controller_mac.h"
[email protected]7d791652010-12-01 16:34:4958#import "chrome/browser/ui/cocoa/bookmarks/bookmark_menu_bridge.h"
59#import "chrome/browser/ui/cocoa/browser_window_cocoa.h"
60#import "chrome/browser/ui/cocoa/browser_window_controller.h"
[email protected]b6366ffa2012-02-29 17:12:2661#import "chrome/browser/ui/cocoa/confirm_quit.h"
[email protected]7d791652010-12-01 16:34:4962#import "chrome/browser/ui/cocoa/confirm_quit_panel_controller.h"
63#import "chrome/browser/ui/cocoa/encoding_menu_controller_delegate_mac.h"
64#import "chrome/browser/ui/cocoa/history_menu_bridge.h"
[email protected]4feb37d92012-07-01 20:22:1365#include "chrome/browser/ui/cocoa/last_active_browser_cocoa.h"
[email protected]961a6932011-07-19 19:52:4666#import "chrome/browser/ui/cocoa/profile_menu_controller.h"
[email protected]cfe1d612011-01-19 20:06:4767#import "chrome/browser/ui/cocoa/tabs/tab_strip_controller.h"
68#import "chrome/browser/ui/cocoa/tabs/tab_window_controller.h"
[email protected]7d791652010-12-01 16:34:4969#include "chrome/browser/ui/cocoa/task_manager_mac.h"
[email protected]ccfab592013-01-15 06:24:3270#include "chrome/browser/ui/extensions/application_launch.h"
[email protected]3b265312013-01-17 02:49:5571#include "chrome/browser/ui/host_desktop.h"
[email protected]4feb37d92012-07-01 20:22:1372#include "chrome/browser/ui/startup/startup_browser_creator.h"
73#include "chrome/browser/ui/startup/startup_browser_creator_impl.h"
[email protected]12dc3d42010-02-22 23:37:1274#include "chrome/common/chrome_paths_internal.h"
[email protected]f4acfae82009-09-11 00:33:3875#include "chrome/common/chrome_switches.h"
[email protected]13aec6c42011-10-27 21:22:0876#include "chrome/common/cloud_print/cloud_print_class_mac.h"
[email protected]ccfab592013-01-15 06:24:3277#include "chrome/common/extensions/extension_constants.h"
[email protected]6c583d32011-08-19 14:46:2278#include "chrome/common/mac/app_mode_common.h"
[email protected]a99fce0e2011-03-21 20:58:4879#include "chrome/common/pref_names.h"
[email protected]a529af52011-08-15 20:56:1780#include "chrome/common/service_messages.h"
[email protected]024617a2010-08-20 05:08:0581#include "chrome/common/url_constants.h"
[email protected]c38831a12011-10-28 12:44:4982#include "content/public/browser/browser_thread.h"
[email protected]e582fdd2011-12-20 16:48:1783#include "content/public/browser/download_manager.h"
[email protected]ad50def52011-10-19 23:17:0784#include "content/public/browser/notification_service.h"
[email protected]0d6e9bd2011-10-18 04:29:1685#include "content/public/browser/notification_types.h"
[email protected]5904cb42012-09-24 15:05:2086#include "content/public/browser/plugin_service.h"
[email protected]7f6f44c2011-12-14 13:23:3887#include "content/public/browser/user_metrics.h"
[email protected]9dcb59662009-09-23 01:15:5088#include "grit/chromium_strings.h"
[email protected]09729a552009-08-03 23:21:4189#include "grit/generated_resources.h"
[email protected]caadfca2010-01-22 21:51:2190#include "net/base/net_util.h"
[email protected]2e29e2232013-07-26 10:40:5991#include "ui/base/cocoa/focus_window_set.h"
[email protected]c051a1b2011-01-21 23:30:1792#include "ui/base/l10n/l10n_util.h"
93#include "ui/base/l10n/l10n_util_mac.h"
[email protected]88d74942009-01-21 22:04:4494
[email protected]b441a8492012-06-06 14:55:5795using content::BrowserContext;
[email protected]631bb742011-11-02 11:29:3996using content::BrowserThread;
[email protected]e582fdd2011-12-20 16:48:1797using content::DownloadManager;
[email protected]7f6f44c2011-12-14 13:23:3898using content::UserMetricsAction;
[email protected]631bb742011-11-02 11:29:3999
[email protected]d630d7d52010-02-21 00:55:11100namespace {
101
[email protected]ebbe94b2012-01-18 20:11:56102// Declare notification names from the 10.7 SDK.
103#if !defined(MAC_OS_X_VERSION_10_7) || \
104 MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
105NSString* NSPopoverDidShowNotification = @"NSPopoverDidShowNotification";
106NSString* NSPopoverDidCloseNotification = @"NSPopoverDidCloseNotification";
107#endif
108
[email protected]d4c811e72013-10-29 21:57:55109// How long we allow a workspace change notification to wait to be
110// associated with a dock activation. The animation lasts 250ms. See
111// applicationShouldHandleReopen:hasVisibleWindows:.
112static const int kWorkspaceChangeTimeoutMs = 500;
113
[email protected]a37d4b02012-06-25 21:56:10114// True while AppController is calling chrome::NewEmptyWindow(). We need a
[email protected]fe7c4872012-05-10 20:06:03115// global flag here, analogue to StartupBrowserCreator::InProcessStartup()
116// because otherwise the SessionService will try to restore sessions when we
117// make a new window while there are no other active windows.
[email protected]d630d7d52010-02-21 00:55:11118bool g_is_opening_new_window = false;
119
120// Activates a browser window having the given profile (the last one active) if
121// possible and returns a pointer to the activate |Browser| or NULL if this was
122// not possible. If the last active browser is minimized (in particular, if
123// there are only minimized windows), it will unminimize it.
124Browser* ActivateBrowser(Profile* profile) {
[email protected]694256072012-11-29 13:40:34125 Browser* browser = chrome::FindLastActiveWithProfile(profile,
126 chrome::HOST_DESKTOP_TYPE_NATIVE);
[email protected]d630d7d52010-02-21 00:55:11127 if (browser)
128 browser->window()->Activate();
129 return browser;
130}
131
132// Creates an empty browser window with the given profile and returns a pointer
133// to the new |Browser|.
134Browser* CreateBrowser(Profile* profile) {
135 {
[email protected]997ec9f2012-11-21 04:44:14136 base::AutoReset<bool> auto_reset_in_run(&g_is_opening_new_window, true);
[email protected]a8e3c532013-02-20 06:03:41137 chrome::NewEmptyWindow(profile, chrome::HOST_DESKTOP_TYPE_NATIVE);
[email protected]d630d7d52010-02-21 00:55:11138 }
139
[email protected]f2bc6e92012-10-14 02:08:38140 Browser* browser = chrome::GetLastActiveBrowser();
[email protected]d630d7d52010-02-21 00:55:11141 CHECK(browser);
142 return browser;
143}
144
145// Activates a browser window having the given profile (the last one active) if
146// possible or creates an empty one if necessary. Returns a pointer to the
147// activated/new |Browser|.
148Browser* ActivateOrCreateBrowser(Profile* profile) {
149 if (Browser* browser = ActivateBrowser(profile))
150 return browser;
151 return CreateBrowser(profile);
152}
153
[email protected]151c4a62011-04-22 04:15:13154CFStringRef BaseBundleID_CFString() {
155 NSString* base_bundle_id =
156 [NSString stringWithUTF8String:base::mac::BaseBundleID()];
157 return base::mac::NSToCFCast(base_bundle_id);
158}
159
[email protected]0b8caad2011-11-28 23:33:27160// This callback synchronizes preferences (under "org.chromium.Chromium" or
[email protected]12dc3d42010-02-22 23:37:12161// "com.google.Chrome"), in particular, writes them out to disk.
[email protected]0b8caad2011-11-28 23:33:27162void PrefsSyncCallback() {
163 if (!CFPreferencesAppSynchronize(BaseBundleID_CFString()))
164 LOG(WARNING) << "Error recording application bundle path.";
165}
[email protected]12dc3d42010-02-22 23:37:12166
167// Record the location of the application bundle (containing the main framework)
168// from which Chromium was loaded. This is used by app mode shims to find
169// Chromium.
170void RecordLastRunAppBundlePath() {
171 // Going up three levels from |chrome::GetVersionedDirectory()| gives the
172 // real, user-visible app bundle directory. (The alternatives give either the
173 // framework's path or the initial app's path, which may be an app mode shim
174 // or a unit test.)
[email protected]bf0d26b2013-08-29 03:09:14175 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
176
[email protected]1c958682013-09-17 03:38:13177 base::FilePath app_bundle_path =
[email protected]12dc3d42010-02-22 23:37:12178 chrome::GetVersionedDirectory().DirName().DirName().DirName();
[email protected]1c958682013-09-17 03:38:13179 base::ScopedCFTypeRef<CFStringRef> app_bundle_path_cfstring(
180 base::SysUTF8ToCFStringRef(app_bundle_path.value()));
[email protected]b6b72222012-02-11 02:04:13181 CFPreferencesSetAppValue(
182 base::mac::NSToCFCast(app_mode::kLastRunAppBundlePathPrefsKey),
[email protected]1c958682013-09-17 03:38:13183 app_bundle_path_cfstring, BaseBundleID_CFString());
[email protected]12dc3d42010-02-22 23:37:12184
185 // Sync after a delay avoid I/O contention on startup; 1500 ms is plenty.
[email protected]df32e89c2012-05-17 17:47:34186 BrowserThread::PostDelayedTask(
187 BrowserThread::FILE, FROM_HERE,
188 base::Bind(&PrefsSyncCallback),
189 base::TimeDelta::FromMilliseconds(1500));
[email protected]12dc3d42010-02-22 23:37:12190}
191
[email protected]d630d7d52010-02-21 00:55:11192} // anonymous namespace
193
[email protected]a99fce0e2011-03-21 20:58:48194@interface AppController (Private)
[email protected]88d74942009-01-21 22:04:44195- (void)initMenuState;
[email protected]961a6932011-07-19 19:52:46196- (void)initProfileMenu;
[email protected]a99fce0e2011-03-21 20:58:48197- (void)updateConfirmToQuitPrefMenuItem:(NSMenuItem*)item;
[email protected]eef99c22010-08-17 05:55:16198- (void)registerServicesMenuTypesTo:(NSApplication*)app;
[email protected]d630d7d52010-02-21 00:55:11199- (void)openUrls:(const std::vector<GURL>&)urls;
[email protected]57750f822009-04-21 21:43:09200- (void)getUrl:(NSAppleEventDescriptor*)event
201 withReply:(NSAppleEventDescriptor*)reply;
[email protected]a529af52011-08-15 20:56:17202- (void)submitCloudPrintJob:(NSAppleEventDescriptor*)event;
[email protected]449dd2f2009-05-27 13:04:00203- (void)windowLayeringDidChange:(NSNotification*)inNotification;
[email protected]d4c811e72013-10-29 21:57:55204- (void)activeSpaceDidChange:(NSNotification*)inNotification;
[email protected]5c593702011-06-17 18:35:24205- (void)windowChangedToProfile:(Profile*)profile;
[email protected]dd6ab1282010-07-20 01:07:32206- (void)checkForAnyKeyWindows;
[email protected]73f5b362009-08-10 23:58:21207- (BOOL)userWillWaitForInProgressDownloads:(int)downloadCount;
[email protected]09729a552009-08-03 23:21:41208- (BOOL)shouldQuitWithInProgressDownloads;
[email protected]93b59fc2010-12-21 20:00:47209- (void)executeApplication:(id)sender;
[email protected]1c62b2f2013-06-28 00:15:00210- (void)profileWasRemoved:(const base::FilePath&)profilePath;
[email protected]88d74942009-01-21 22:04:44211@end
[email protected]1bcdb532009-01-16 17:47:57212
[email protected]1c62b2f2013-06-28 00:15:00213class AppControllerProfileObserver : public ProfileInfoCacheObserver {
214 public:
215 AppControllerProfileObserver(
216 ProfileManager* profile_manager, AppController* app_controller)
217 : profile_manager_(profile_manager),
218 app_controller_(app_controller) {
219 DCHECK(profile_manager_);
220 DCHECK(app_controller_);
221 profile_manager_->GetProfileInfoCache().AddObserver(this);
222 }
223
224 virtual ~AppControllerProfileObserver() {
225 DCHECK(profile_manager_);
226 profile_manager_->GetProfileInfoCache().RemoveObserver(this);
227 }
228
229 private:
230 // ProfileInfoCacheObserver implementation:
231
232 virtual void OnProfileAdded(const base::FilePath& profile_path) OVERRIDE {
233 }
234
[email protected]96920152013-12-04 21:00:16235 virtual void OnProfileWasRemoved(
236 const base::FilePath& profile_path,
237 const base::string16& profile_name) OVERRIDE {
[email protected]1c62b2f2013-06-28 00:15:00238 // When a profile is deleted we need to notify the AppController,
239 // so it can correctly update its pointer to the last used profile.
240 [app_controller_ profileWasRemoved:profile_path];
241 }
242
243 virtual void OnProfileWillBeRemoved(
244 const base::FilePath& profile_path) OVERRIDE {
245 }
246
[email protected]96920152013-12-04 21:00:16247 virtual void OnProfileNameChanged(
248 const base::FilePath& profile_path,
249 const base::string16& old_profile_name) OVERRIDE {
[email protected]1c62b2f2013-06-28 00:15:00250 }
251
252 virtual void OnProfileAvatarChanged(
253 const base::FilePath& profile_path) OVERRIDE {
254 }
255
256 ProfileManager* profile_manager_;
257
258 AppController* app_controller_; // Weak; owns us.
259
260 DISALLOW_COPY_AND_ASSIGN(AppControllerProfileObserver);
261};
262
[email protected]1bcdb532009-01-16 17:47:57263@implementation AppController
264
[email protected]caadfca2010-01-22 21:51:21265@synthesize startupComplete = startupComplete_;
266
[email protected]cd63ef62009-05-06 19:41:37267// This method is called very early in application startup (ie, before
268// the profile is loaded or any preferences have been registered). Defer any
269// user-data initialization until -applicationDidFinishLaunching:.
[email protected]88d74942009-01-21 22:04:44270- (void)awakeFromNib {
[email protected]136140c2009-05-19 13:58:25271 // We need to register the handlers early to catch events fired on launch.
[email protected]57750f822009-04-21 21:43:09272 NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager];
273 [em setEventHandler:self
274 andSelector:@selector(getUrl:withReply:)
275 forEventClass:kInternetEventClass
276 andEventID:kAEGetURL];
277 [em setEventHandler:self
[email protected]a529af52011-08-15 20:56:17278 andSelector:@selector(submitCloudPrintJob:)
[email protected]13aec6c42011-10-27 21:22:08279 forEventClass:cloud_print::kAECloudPrintClass
280 andEventID:cloud_print::kAECloudPrintClass];
[email protected]a529af52011-08-15 20:56:17281 [em setEventHandler:self
[email protected]57750f822009-04-21 21:43:09282 andSelector:@selector(getUrl:withReply:)
283 forEventClass:'WWW!' // A particularly ancient AppleEvent that dates
284 andEventID:'OURL']; // back to the Spyglass days.
[email protected]cd63ef62009-05-06 19:41:37285
[email protected]449dd2f2009-05-27 13:04:00286 // Register for various window layering changes. We use these to update
287 // various UI elements (command-key equivalents, etc) when the frontmost
288 // window changes.
289 NSNotificationCenter* notificationCenter =
290 [NSNotificationCenter defaultCenter];
[email protected]ce560f82009-06-03 09:39:44291 [notificationCenter
[email protected]449dd2f2009-05-27 13:04:00292 addObserver:self
293 selector:@selector(windowLayeringDidChange:)
294 name:NSWindowDidBecomeKeyNotification
295 object:nil];
296 [notificationCenter
297 addObserver:self
298 selector:@selector(windowLayeringDidChange:)
299 name:NSWindowDidResignKeyNotification
300 object:nil];
301 [notificationCenter
302 addObserver:self
303 selector:@selector(windowLayeringDidChange:)
304 name:NSWindowDidBecomeMainNotification
305 object:nil];
306 [notificationCenter
307 addObserver:self
308 selector:@selector(windowLayeringDidChange:)
309 name:NSWindowDidResignMainNotification
310 object:nil];
311
[email protected]ebbe94b2012-01-18 20:11:56312 if (base::mac::IsOSLionOrLater()) {
313 [notificationCenter
314 addObserver:self
315 selector:@selector(popoverDidShow:)
316 name:NSPopoverDidShowNotification
317 object:nil];
318 [notificationCenter
319 addObserver:self
320 selector:@selector(popoverDidClose:)
321 name:NSPopoverDidCloseNotification
322 object:nil];
323 }
324
[email protected]d4c811e72013-10-29 21:57:55325 // Register for space change notifications.
326 [[[NSWorkspace sharedWorkspace] notificationCenter]
327 addObserver:self
328 selector:@selector(activeSpaceDidChange:)
329 name:NSWorkspaceActiveSpaceDidChangeNotification
330 object:nil];
331
[email protected]136140c2009-05-19 13:58:25332 // Set up the command updater for when there are no windows open
333 [self initMenuState];
[email protected]961a6932011-07-19 19:52:46334
335 // Initialize the Profile menu.
336 [self initProfileMenu];
[email protected]136140c2009-05-19 13:58:25337}
338
[email protected]71c0eb92012-01-03 17:57:30339- (void)unregisterEventHandlers {
340 NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager];
341 [em removeEventHandlerForEventClass:kInternetEventClass
342 andEventID:kAEGetURL];
343 [em removeEventHandlerForEventClass:cloud_print::kAECloudPrintClass
344 andEventID:cloud_print::kAECloudPrintClass];
[email protected]71c0eb92012-01-03 17:57:30345 [em removeEventHandlerForEventClass:'WWW!'
346 andEventID:'OURL'];
347 [[NSNotificationCenter defaultCenter] removeObserver:self];
[email protected]d4c811e72013-10-29 21:57:55348 [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];
[email protected]71c0eb92012-01-03 17:57:30349}
350
[email protected]7dc8c6b2010-04-09 17:02:50351// (NSApplicationDelegate protocol) This is the Apple-approved place to override
352// the default handlers.
353- (void)applicationWillFinishLaunching:(NSNotification*)notification {
[email protected]58877932010-05-25 05:43:11354 // Nothing here right now.
[email protected]7dc8c6b2010-04-09 17:02:50355}
356
357- (BOOL)tryToTerminateApplication:(NSApplication*)app {
[email protected]3076fad2010-04-28 18:11:45358 // Check for in-process downloads, and prompt the user if they really want
359 // to quit (and thus cancel downloads). Only check if we're not already
360 // shutting down, else the user might be prompted multiple times if the
361 // download isn't stopped before terminate is called again.
362 if (!browser_shutdown::IsTryingToQuit() &&
363 ![self shouldQuitWithInProgressDownloads])
364 return NO;
365
[email protected]7dc8c6b2010-04-09 17:02:50366 // TODO(viettrungluu): Remove Apple Event handlers here? (It's safe to leave
367 // them in, but I'm not sure about UX; we'd also want to disable other things
368 // though.) http://crbug.com/40861
369
[email protected]177aceb2010-11-03 16:17:41370 // Check if the user really wants to quit by employing the confirm-to-quit
371 // mechanism.
372 if (!browser_shutdown::IsTryingToQuit() &&
373 [self applicationShouldTerminate:app] != NSTerminateNow)
374 return NO;
375
[email protected]0665ebe2013-02-13 09:53:19376 size_t num_browsers = chrome::GetTotalBrowserCount();
[email protected]7dc8c6b2010-04-09 17:02:50377
[email protected]0c95faf42013-10-28 06:27:20378 // Initiate a shutdown (via chrome::CloseAllBrowsersAndQuit()) if we aren't
[email protected]c984d9f2010-07-20 20:52:20379 // already shutting down.
[email protected]ef61b8c2012-01-20 10:54:56380 if (!browser_shutdown::IsTryingToQuit()) {
381 content::NotificationService::current()->Notify(
[email protected]d53a08c2012-07-18 20:35:30382 chrome::NOTIFICATION_CLOSE_ALL_BROWSERS_REQUEST,
[email protected]ef61b8c2012-01-20 10:54:56383 content::NotificationService::AllSources(),
384 content::NotificationService::NoDetails());
[email protected]0c95faf42013-10-28 06:27:20385 chrome::CloseAllBrowsersAndQuit();
[email protected]ef61b8c2012-01-20 10:54:56386 }
[email protected]7dc8c6b2010-04-09 17:02:50387
[email protected]c984d9f2010-07-20 20:52:20388 return num_browsers == 0 ? YES : NO;
[email protected]7dc8c6b2010-04-09 17:02:50389}
390
391- (void)stopTryingToTerminateApplication:(NSApplication*)app {
392 if (browser_shutdown::IsTryingToQuit()) {
393 // Reset the "trying to quit" state, so that closing all browser windows
394 // will no longer lead to termination.
395 browser_shutdown::SetTryingToQuit(false);
396
397 // TODO(viettrungluu): Were we to remove Apple Event handlers above, we
398 // would have to reinstall them here. http://crbug.com/40861
399 }
[email protected]a9e8afc2009-08-11 22:03:17400}
401
[email protected]177aceb2010-11-03 16:17:41402- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)app {
[email protected]90a4b9d2013-07-31 12:14:40403 // If there are no windows, quit immediately.
404 if (chrome::BrowserIterator().done() &&
[email protected]f8a39122013-08-30 14:44:29405 !apps::ShellWindowRegistry::IsShellWindowRegisteredInAnyProfile(0)) {
[email protected]90a4b9d2013-07-31 12:14:40406 return NSTerminateNow;
407 }
408
[email protected]a99fce0e2011-03-21 20:58:48409 // Check if the preference is turned on.
[email protected]b6366ffa2012-02-29 17:12:26410 const PrefService* prefs = g_browser_process->local_state();
[email protected]1eaddf32011-04-05 15:09:23411 if (!prefs->GetBoolean(prefs::kConfirmToQuitEnabled)) {
412 confirm_quit::RecordHistogram(confirm_quit::kNoConfirm);
[email protected]177aceb2010-11-03 16:17:41413 return NSTerminateNow;
[email protected]1eaddf32011-04-05 15:09:23414 }
[email protected]177aceb2010-11-03 16:17:41415
416 // If the application is going to terminate as the result of a Cmd+Q
417 // invocation, use the special sauce to prevent accidental quitting.
418 // http://dev.chromium.org/developers/design-documents/confirm-to-quit-experiment
[email protected]177aceb2010-11-03 16:17:41419
[email protected]2020fd0b2010-12-10 01:16:50420 // This logic is only for keyboard-initiated quits.
[email protected]5df54fb2011-03-07 18:15:26421 if (![ConfirmQuitPanelController eventTriggersFeature:[app currentEvent]])
[email protected]2020fd0b2010-12-10 01:16:50422 return NSTerminateNow;
[email protected]177aceb2010-11-03 16:17:41423
[email protected]5df54fb2011-03-07 18:15:26424 return [[ConfirmQuitPanelController sharedController]
425 runModalLoopForApplication:app];
[email protected]177aceb2010-11-03 16:17:41426}
427
[email protected]449dd2f2009-05-27 13:04:00428// Called when the app is shutting down. Clean-up as appropriate.
[email protected]7dc8c6b2010-04-09 17:02:50429- (void)applicationWillTerminate:(NSNotification*)aNotification {
[email protected]7dc8c6b2010-04-09 17:02:50430 // There better be no browser windows left at this point.
[email protected]0665ebe2013-02-13 09:53:19431 CHECK_EQ(0u, chrome::GetTotalBrowserCount());
[email protected]3b6aa8b62009-09-15 21:36:11432
[email protected]c984d9f2010-07-20 20:52:20433 // Tell BrowserList not to keep the browser process alive. Once all the
434 // browsers get dealloc'd, it will stop the RunLoop and fall back into main().
[email protected]313fce12013-01-30 17:09:04435 chrome::EndKeepAlive();
[email protected]fbc947b2009-06-19 13:28:24436
[email protected]02a31dd2013-02-05 08:40:52437 // Reset all pref watching, as this object outlives the prefs system.
438 profilePrefRegistrar_.reset();
439 localPrefRegistrar_.RemoveAll();
440
[email protected]71c0eb92012-01-03 17:57:30441 [self unregisterEventHandlers];
[email protected]edb2d032013-08-30 09:03:17442
443 appShimMenuController_.reset();
[email protected]449dd2f2009-05-27 13:04:00444}
445
[email protected]3b6aa8b62009-09-15 21:36:11446- (void)didEndMainMessageLoop {
[email protected]c987a242013-02-28 01:17:41447 DCHECK_EQ(0u, chrome::GetBrowserCount([self lastProfile],
448 chrome::HOST_DESKTOP_TYPE_NATIVE));
449 if (!chrome::GetBrowserCount([self lastProfile],
450 chrome::HOST_DESKTOP_TYPE_NATIVE)) {
[email protected]3b6aa8b62009-09-15 21:36:11451 // As we're shutting down, we need to nuke the TabRestoreService, which
452 // will start the shutdown of the NavigationControllers and allow for
453 // proper shutdown. If we don't do this, Chrome won't shut down cleanly,
454 // and may end up crashing when some thread tries to use the IO thread (or
455 // another thread) that is no longer valid.
[email protected]a585a8db2011-06-20 18:58:35456 TabRestoreServiceFactory::ResetForProfile([self lastProfile]);
[email protected]3b6aa8b62009-09-15 21:36:11457 }
458}
459
[email protected]8cfbdbd2011-06-16 04:17:17460// If the window has a tab controller, make "close window" be cmd-shift-w,
461// otherwise leave it as the normal cmd-w. Capitalization of the key equivalent
[email protected]eddcf7502012-02-09 22:43:48462// affects whether the shift modifier is used.
[email protected]ebbe94b2012-01-18 20:11:56463- (void)adjustCloseWindowMenuItemKeyEquivalent:(BOOL)enableCloseTabShortcut {
464 [closeWindowMenuItem_ setKeyEquivalent:(enableCloseTabShortcut ? @"W" :
465 @"w")];
[email protected]8cfbdbd2011-06-16 04:17:17466 [closeWindowMenuItem_ setKeyEquivalentModifierMask:NSCommandKeyMask];
467}
468
469// If the window has a tab controller, make "close tab" take over cmd-w,
470// otherwise it shouldn't have any key-equivalent because it should be disabled.
[email protected]ebbe94b2012-01-18 20:11:56471- (void)adjustCloseTabMenuItemKeyEquivalent:(BOOL)enableCloseTabShortcut {
472 if (enableCloseTabShortcut) {
[email protected]8cfbdbd2011-06-16 04:17:17473 [closeTabMenuItem_ setKeyEquivalent:@"w"];
474 [closeTabMenuItem_ setKeyEquivalentModifierMask:NSCommandKeyMask];
475 } else {
476 [closeTabMenuItem_ setKeyEquivalent:@""];
477 [closeTabMenuItem_ setKeyEquivalentModifierMask:0];
478 }
479}
480
481// Explicitly remove any command-key equivalents from the close tab/window
482// menus so that nothing can go haywire if we get a user action during pending
483// updates.
484- (void)clearCloseMenuItemKeyEquivalents {
485 [closeTabMenuItem_ setKeyEquivalent:@""];
486 [closeTabMenuItem_ setKeyEquivalentModifierMask:0];
487 [closeWindowMenuItem_ setKeyEquivalent:@""];
488 [closeWindowMenuItem_ setKeyEquivalentModifierMask:0];
489}
490
[email protected]9f5f79b62011-10-28 00:43:45491// See if the focused window window has tabs, and adjust the key equivalents for
[email protected]8cfbdbd2011-06-16 04:17:17492// Close Tab/Close Window accordingly.
[email protected]9f5f79b62011-10-28 00:43:45493- (void)fixCloseMenuItemKeyEquivalents {
[email protected]8cfbdbd2011-06-16 04:17:17494 fileMenuUpdatePending_ = NO;
[email protected]8cfbdbd2011-06-16 04:17:17495
[email protected]9f5f79b62011-10-28 00:43:45496 NSWindow* window = [NSApp keyWindow];
497 NSWindow* mainWindow = [NSApp mainWindow];
498 if (!window || ([window parentWindow] == mainWindow)) {
499 // If the key window is a child of the main window (e.g. a bubble), the main
500 // window should be the one that handles the close menu item action.
501 // Also, there might be a small amount of time where there is no key window;
502 // in that case as well, just use our main browser window if there is one.
503 // You might think that we should just always use the main window, but the
504 // "About Chrome" window serves as a counterexample.
505 window = mainWindow;
506 }
507
508 BOOL hasTabs =
509 [[window windowController] isKindOfClass:[TabWindowController class]];
[email protected]ebbe94b2012-01-18 20:11:56510 BOOL enableCloseTabShortcut = hasTabs && !hasPopover_;
511 [self adjustCloseWindowMenuItemKeyEquivalent:enableCloseTabShortcut];
512 [self adjustCloseTabMenuItemKeyEquivalent:enableCloseTabShortcut];
[email protected]8cfbdbd2011-06-16 04:17:17513}
514
515// Fix up the "close tab/close window" command-key equivalents. We do this
516// after a delay to ensure that window layer state has been set by the time
517// we do the enabling. This should only be called on the main thread, code that
518// calls this (even as a side-effect) from other threads needs to be fixed.
[email protected]9f5f79b62011-10-28 00:43:45519- (void)delayedFixCloseMenuItemKeyEquivalents {
[email protected]8cfbdbd2011-06-16 04:17:17520 DCHECK([NSThread isMainThread]);
521 if (!fileMenuUpdatePending_) {
522 // The OS prefers keypresses to timers, so it's possible that a cmd-w
523 // can sneak in before this timer fires. In order to prevent that from
524 // having any bad consequences, just clear the keys combos altogether. They
525 // will be reset when the timer eventually fires.
526 if ([NSThread isMainThread]) {
527 fileMenuUpdatePending_ = YES;
528 [self clearCloseMenuItemKeyEquivalents];
[email protected]9f5f79b62011-10-28 00:43:45529 [self performSelector:@selector(fixCloseMenuItemKeyEquivalents)
530 withObject:nil
[email protected]8cfbdbd2011-06-16 04:17:17531 afterDelay:0];
532 } else {
533 // This shouldn't be happening, but if it does, force it to the main
534 // thread to avoid dropping the update. Don't mess with
535 // |fileMenuUpdatePending_| as it's not expected to be threadsafe and
536 // there could be a race between the selector finishing and setting the
537 // flag.
538 [self
[email protected]9f5f79b62011-10-28 00:43:45539 performSelectorOnMainThread:@selector(fixCloseMenuItemKeyEquivalents)
540 withObject:nil
[email protected]8cfbdbd2011-06-16 04:17:17541 waitUntilDone:NO];
542 }
543 }
544}
545
[email protected]449dd2f2009-05-27 13:04:00546// Called when we get a notification about the window layering changing to
547// update the UI based on the new main window.
548- (void)windowLayeringDidChange:(NSNotification*)notify {
[email protected]9f5f79b62011-10-28 00:43:45549 [self delayedFixCloseMenuItemKeyEquivalents];
[email protected]8cfbdbd2011-06-16 04:17:17550
[email protected]dd6ab1282010-07-20 01:07:32551 if ([notify name] == NSWindowDidResignKeyNotification) {
552 // If a window is closed, this notification is fired but |[NSApp keyWindow]|
553 // returns nil regardless of whether any suitable candidates for the key
554 // window remain. It seems that the new key window for the app is not set
555 // until after this notification is fired, so a check is performed after the
556 // run loop is allowed to spin.
557 [self performSelector:@selector(checkForAnyKeyWindows)
558 withObject:nil
559 afterDelay:0.0];
560 }
[email protected]5c593702011-06-17 18:35:24561
562 // If the window changed to a new BrowserWindowController, update the profile.
563 id windowController = [[notify object] windowController];
[email protected]dc251482012-01-03 18:55:21564 if ([notify name] == NSWindowDidBecomeMainNotification &&
565 [windowController isKindOfClass:[BrowserWindowController class]]) {
[email protected]5c593702011-06-17 18:35:24566 // If the profile is incognito, use the original profile.
567 Profile* newProfile = [windowController profile]->GetOriginalProfile();
568 [self windowChangedToProfile:newProfile];
[email protected]0665ebe2013-02-13 09:53:19569 } else if (chrome::GetTotalBrowserCount() == 0) {
[email protected]cae68b3a2011-11-21 23:29:49570 [self windowChangedToProfile:
571 g_browser_process->profile_manager()->GetLastUsedProfile()];
[email protected]5c593702011-06-17 18:35:24572 }
573}
574
[email protected]d4c811e72013-10-29 21:57:55575- (void)activeSpaceDidChange:(NSNotification*)notify {
576 if (reopenTime_.is_null() ||
577 ![NSApp isActive] ||
578 (base::TimeTicks::Now() - reopenTime_).InMilliseconds() >
579 kWorkspaceChangeTimeoutMs) {
580 return;
581 }
582
583 // The last applicationShouldHandleReopen:hasVisibleWindows: call
584 // happened during a space change. Now that the change has
585 // completed, raise browser windows.
586 reopenTime_ = base::TimeTicks();
587 std::set<NSWindow*> browserWindows;
588 for (chrome::BrowserIterator iter; !iter.done(); iter.Next()) {
589 Browser* browser = *iter;
590 browserWindows.insert(browser->window()->GetNativeWindow());
591 }
592 if (!browserWindows.empty()) {
593 ui::FocusWindowSet(browserWindows, false);
594 }
595}
596
[email protected]ebbe94b2012-01-18 20:11:56597// Called on Lion and later when a popover (e.g. dictionary) is shown.
598- (void)popoverDidShow:(NSNotification*)notify {
599 hasPopover_ = YES;
600 [self fixCloseMenuItemKeyEquivalents];
601}
602
603// Called on Lion and later when a popover (e.g. dictionary) is closed.
604- (void)popoverDidClose:(NSNotification*)notify {
605 hasPopover_ = NO;
606 [self fixCloseMenuItemKeyEquivalents];
607}
608
[email protected]5c593702011-06-17 18:35:24609// Called when the user has changed browser windows, meaning the backing profile
610// may have changed. This can cause a rebuild of the user-data menus. This is a
611// no-op if the new profile is the same as the current one. This will always be
612// the original profile and never incognito.
613- (void)windowChangedToProfile:(Profile*)profile {
614 if (lastProfile_ == profile)
615 return;
616
617 // Before tearing down the menu controller bridges, return the Cocoa menus to
618 // their initial state.
619 if (bookmarkMenuBridge_.get())
620 bookmarkMenuBridge_->ResetMenu();
621 if (historyMenuBridge_.get())
622 historyMenuBridge_->ResetMenu();
623
624 // Rebuild the menus with the new profile.
625 lastProfile_ = profile;
626
[email protected]0c165cf2011-07-09 03:56:50627 bookmarkMenuBridge_.reset(new BookmarkMenuBridge(lastProfile_,
628 [[[NSApp mainMenu] itemWithTag:IDC_BOOKMARKS_MENU] submenu]));
[email protected]dc251482012-01-03 18:55:21629 // No need to |BuildMenu| here. It is done lazily upon menu access.
[email protected]5c593702011-06-17 18:35:24630
631 historyMenuBridge_.reset(new HistoryMenuBridge(lastProfile_));
632 historyMenuBridge_->BuildMenu();
[email protected]05454532013-01-22 21:09:08633
634 chrome::BrowserCommandController::
635 UpdateSharedCommandsForIncognitoAvailability(
636 menuState_.get(), lastProfile_);
637 profilePrefRegistrar_.reset(new PrefChangeRegistrar());
638 profilePrefRegistrar_->Init(lastProfile_->GetPrefs());
639 profilePrefRegistrar_->Add(
640 prefs::kIncognitoModeAvailability,
641 base::Bind(&chrome::BrowserCommandController::
642 UpdateSharedCommandsForIncognitoAvailability,
643 menuState_.get(),
644 lastProfile_));
[email protected]dd6ab1282010-07-20 01:07:32645}
646
647- (void)checkForAnyKeyWindows {
648 if ([NSApp keyWindow])
649 return;
650
[email protected]ad50def52011-10-19 23:17:07651 content::NotificationService::current()->Notify(
[email protected]d53a08c2012-07-18 20:35:30652 chrome::NOTIFICATION_NO_KEY_WINDOW,
[email protected]ad50def52011-10-19 23:17:07653 content::NotificationService::AllSources(),
654 content::NotificationService::NoDetails());
[email protected]449dd2f2009-05-27 13:04:00655}
656
[email protected]414fde592009-05-21 16:14:43657// If the auto-update interval is not set, make it 5 hours.
[email protected]414fde592009-05-21 16:14:43658// Placed here for 2 reasons:
659// 1) Same spot as other Pref stuff
660// 2) Try and be friendly by keeping this after app launch
[email protected]414fde592009-05-21 16:14:43661- (void)setUpdateCheckInterval {
662#if defined(GOOGLE_CHROME_BUILD)
[email protected]8be07172013-04-27 05:25:49663 CFStringRef app = CFSTR("com.google.Keystone.Agent");
664 CFStringRef checkInterval = CFSTR("checkInterval");
[email protected]414fde592009-05-21 16:14:43665 CFPropertyListRef plist = CFPreferencesCopyAppValue(checkInterval, app);
666 if (!plist) {
667 const float fiveHoursInSeconds = 5.0 * 60.0 * 60.0;
[email protected]7dc8c6b2010-04-09 17:02:50668 NSNumber* value = [NSNumber numberWithFloat:fiveHoursInSeconds];
[email protected]414fde592009-05-21 16:14:43669 CFPreferencesSetAppValue(checkInterval, value, app);
670 CFPreferencesAppSynchronize(app);
671 }
672#endif
673}
674
[email protected]136140c2009-05-19 13:58:25675// This is called after profiles have been loaded and preferences registered.
676// It is safe to access the default profile here.
677- (void)applicationDidFinishLaunching:(NSNotification*)notify {
[email protected]c984d9f2010-07-20 20:52:20678 // Notify BrowserList to keep the application running so it doesn't go away
679 // when all the browser windows get closed.
[email protected]313fce12013-01-30 17:09:04680 chrome::StartKeepAlive();
[email protected]136140c2009-05-19 13:58:25681
[email protected]414fde592009-05-21 16:14:43682 [self setUpdateCheckInterval];
[email protected]2bcec612009-05-14 17:50:53683
[email protected]edb2d032013-08-30 09:03:17684 // Start managing the menu for app windows. This needs to be done here because
685 // main menu item titles are not yet initialized in awakeFromNib.
[email protected]e7dc3992013-11-06 02:16:42686 [self initAppShimMenuController];
[email protected]edb2d032013-08-30 09:03:17687
[email protected]2bcec612009-05-14 17:50:53688 // Build up the encoding menu, the order of the items differs based on the
689 // current locale (see http://crbug.com/7647 for details).
690 // We need a valid g_browser_process to get the profile which is why we can't
691 // call this from awakeFromNib.
[email protected]6f91e5ae2011-03-21 19:53:14692 NSMenu* viewMenu = [[[NSApp mainMenu] itemWithTag:IDC_VIEW_MENU] submenu];
693 NSMenuItem* encodingMenuItem = [viewMenu itemWithTag:IDC_ENCODING_MENU];
694 NSMenu* encodingMenu = [encodingMenuItem submenu];
[email protected]a585a8db2011-06-20 18:58:35695 EncodingMenuControllerDelegate::BuildEncodingMenu([self lastProfile],
[email protected]6f91e5ae2011-03-21 19:53:14696 encodingMenu);
[email protected]2bcec612009-05-14 17:50:53697
[email protected]1c62b2f2013-06-28 00:15:00698 // Instantiate the ProfileInfoCache observer so that we can get
699 // notified when a profile is deleted.
700 profileInfoCacheObserver_.reset(new AppControllerProfileObserver(
701 g_browser_process->profile_manager(), self));
702
[email protected]f2c20fa2009-12-01 17:42:02703 // Since Chrome is localized to more languages than the OS, tell Cocoa which
704 // menu is the Help so it can add the search item to it.
[email protected]73980132012-07-26 20:09:28705 [NSApp setHelpMenu:helpMenu_];
[email protected]f2c20fa2009-12-01 17:42:02706
[email protected]12dc3d42010-02-22 23:37:12707 // Record the path to the (browser) app bundle; this is used by the app mode
[email protected]bf0d26b2013-08-29 03:09:14708 // shim. It has to be done in FILE thread because getting the path requires
709 // I/O.
710 BrowserThread::PostTask(
711 BrowserThread::FILE, FROM_HERE,
712 base::Bind(&RecordLastRunAppBundlePath));
[email protected]12dc3d42010-02-22 23:37:12713
[email protected]eef99c22010-08-17 05:55:16714 // Makes "Services" menu items available.
715 [self registerServicesMenuTypesTo:[notify object]];
716
[email protected]caadfca2010-01-22 21:51:21717 startupComplete_ = YES;
718
719 // TODO(viettrungluu): This is very temporary, since this should be done "in"
720 // |BrowserMain()|, i.e., this list of startup URLs should be appended to the
721 // (probably-empty) list of URLs from the command line.
[email protected]d630d7d52010-02-21 00:55:11722 if (startupUrls_.size()) {
723 [self openUrls:startupUrls_];
724 [self clearStartupUrls];
[email protected]caadfca2010-01-22 21:51:21725 }
[email protected]aac169d2011-03-18 19:53:03726
727 const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess();
728 if (!parsed_command_line.HasSwitch(switches::kEnableExposeForTabs)) {
729 [tabposeMenuItem_ setHidden:YES];
730 }
[email protected]05454532013-01-22 21:09:08731
732 PrefService* localState = g_browser_process->local_state();
733 if (localState) {
734 localPrefRegistrar_.Init(localState);
735 localPrefRegistrar_.Add(
736 prefs::kAllowFileSelectionDialogs,
737 base::Bind(&chrome::BrowserCommandController::UpdateOpenFileState,
738 menuState_.get()));
739 }
[email protected]7c321082009-02-09 15:35:47740}
741
[email protected]a96ec6a2009-11-04 17:27:08742// This is called after profiles have been loaded and preferences registered.
743// It is safe to access the default profile here.
744- (void)applicationDidBecomeActive:(NSNotification*)notify {
[email protected]5904cb42012-09-24 15:05:20745 content::PluginService::GetInstance()->AppActivated();
[email protected]a96ec6a2009-11-04 17:27:08746}
747
[email protected]73f5b362009-08-10 23:58:21748// Helper function for populating and displaying the in progress downloads at
749// exit alert panel.
750- (BOOL)userWillWaitForInProgressDownloads:(int)downloadCount {
[email protected]83a9f652012-04-25 21:03:48751 NSString* titleText = nil;
[email protected]e8b5f7882009-09-14 20:59:13752 NSString* explanationText = nil;
[email protected]73f5b362009-08-10 23:58:21753 NSString* waitTitle = nil;
754 NSString* exitTitle = nil;
755
756 // Set the dialog text based on whether or not there are multiple downloads.
757 if (downloadCount == 1) {
[email protected]e8b5f7882009-09-14 20:59:13758 // Dialog text: warning and explanation.
[email protected]83a9f652012-04-25 21:03:48759 titleText = l10n_util::GetNSString(
760 IDS_SINGLE_DOWNLOAD_REMOVE_CONFIRM_TITLE);
[email protected]ab3b1c112012-01-13 22:04:16761 explanationText = l10n_util::GetNSString(
762 IDS_SINGLE_DOWNLOAD_REMOVE_CONFIRM_EXPLANATION);
[email protected]73f5b362009-08-10 23:58:21763 } else {
[email protected]e8b5f7882009-09-14 20:59:13764 // Dialog text: warning and explanation.
[email protected]83a9f652012-04-25 21:03:48765 titleText = l10n_util::GetNSStringF(
766 IDS_MULTIPLE_DOWNLOADS_REMOVE_CONFIRM_TITLE,
[email protected]528c56d2010-07-30 19:28:44767 base::IntToString16(downloadCount));
[email protected]ab3b1c112012-01-13 22:04:16768 explanationText = l10n_util::GetNSString(
769 IDS_MULTIPLE_DOWNLOADS_REMOVE_CONFIRM_EXPLANATION);
[email protected]73f5b362009-08-10 23:58:21770 }
[email protected]83a9f652012-04-25 21:03:48771 // Cancel download and exit button text.
772 exitTitle = l10n_util::GetNSString(
773 IDS_DOWNLOAD_REMOVE_CONFIRM_OK_BUTTON_LABEL);
774
775 // Wait for download button text.
776 waitTitle = l10n_util::GetNSString(
777 IDS_DOWNLOAD_REMOVE_CONFIRM_CANCEL_BUTTON_LABEL);
[email protected]73f5b362009-08-10 23:58:21778
779 // 'waitButton' is the default choice.
[email protected]361b47c2013-07-02 15:13:24780 int choice = NSRunAlertPanel(titleText, @"%@",
781 waitTitle, exitTitle, nil, explanationText);
[email protected]73f5b362009-08-10 23:58:21782 return choice == NSAlertDefaultReturn ? YES : NO;
783}
784
785// Check all profiles for in progress downloads, and if we find any, prompt the
[email protected]09729a552009-08-03 23:21:41786// user to see if we should continue to exit (and thus cancel the downloads), or
787// if we should wait.
788- (BOOL)shouldQuitWithInProgressDownloads {
[email protected]73f5b362009-08-10 23:58:21789 ProfileManager* profile_manager = g_browser_process->profile_manager();
790 if (!profile_manager)
791 return YES;
[email protected]09729a552009-08-03 23:21:41792
[email protected]844a1002011-04-19 11:37:21793 std::vector<Profile*> profiles(profile_manager->GetLoadedProfiles());
794 for (size_t i = 0; i < profiles.size(); ++i) {
[email protected]9bb54ee2011-10-12 17:43:35795 DownloadService* download_service =
[email protected]423939d2013-07-31 20:00:08796 DownloadServiceFactory::GetForBrowserContext(profiles[i]);
[email protected]9bb54ee2011-10-12 17:43:35797 DownloadManager* download_manager =
798 (download_service->HasCreatedDownloadManager() ?
[email protected]b441a8492012-06-06 14:55:57799 BrowserContext::GetDownloadManager(profiles[i]) : NULL);
[email protected]422a7d12013-10-21 12:10:42800 if (download_manager &&
801 download_manager->NonMaliciousInProgressCount() > 0) {
802 int downloadCount = download_manager->NonMaliciousInProgressCount();
[email protected]73f5b362009-08-10 23:58:21803 if ([self userWillWaitForInProgressDownloads:downloadCount]) {
804 // Create a new browser window (if necessary) and navigate to the
805 // downloads page if the user chooses to wait.
[email protected]3c6a0952012-12-17 11:56:09806 Browser* browser = chrome::FindBrowserWithProfile(
[email protected]323fd7122012-08-24 14:45:11807 profiles[i], chrome::HOST_DESKTOP_TYPE_NATIVE);
[email protected]73f5b362009-08-10 23:58:21808 if (!browser) {
[email protected]c125cdc2013-02-25 19:10:21809 browser = new Browser(Browser::CreateParams(
810 profiles[i], chrome::HOST_DESKTOP_TYPE_NATIVE));
[email protected]73f5b362009-08-10 23:58:21811 browser->window()->Show();
812 }
813 DCHECK(browser);
[email protected]5d9cace72012-06-21 16:07:12814 chrome::ShowDownloads(browser);
[email protected]73f5b362009-08-10 23:58:21815 return NO;
816 }
[email protected]09729a552009-08-03 23:21:41817
[email protected]73f5b362009-08-10 23:58:21818 // User wants to exit.
819 return YES;
[email protected]09729a552009-08-03 23:21:41820 }
[email protected]09729a552009-08-03 23:21:41821 }
822
[email protected]73f5b362009-08-10 23:58:21823 // No profiles or active downloads found, okay to exit.
[email protected]09729a552009-08-03 23:21:41824 return YES;
825}
826
[email protected]fbc947b2009-06-19 13:28:24827// Called to determine if we should enable the "restore tab" menu item.
828// Checks with the TabRestoreService to see if there's anything there to
829// restore and returns YES if so.
830- (BOOL)canRestoreTab {
[email protected]92371eb2011-04-28 11:50:15831 TabRestoreService* service =
[email protected]a585a8db2011-06-20 18:58:35832 TabRestoreServiceFactory::GetForProfile([self lastProfile]);
[email protected]fbc947b2009-06-19 13:28:24833 return service && !service->entries().empty();
834}
835
[email protected]1c62b2f2013-06-28 00:15:00836// Called from the AppControllerProfileObserver every time a profile is deleted.
837- (void)profileWasRemoved:(const base::FilePath&)profilePath {
838 Profile* lastProfile = [self lastProfile];
839
840 // If the lastProfile has been deleted, the profile manager has
841 // already loaded a new one, so the pointer needs to be updated;
842 // otherwise we will try to start up a browser window with a pointer
843 // to the old profile.
844 if (profilePath == lastProfile->GetPath())
845 lastProfile_ = g_browser_process->profile_manager()->GetLastUsedProfile();
846}
847
[email protected]05454532013-01-22 21:09:08848// Returns true if there is a modal window (either window- or application-
[email protected]f6c15fa2010-06-04 20:08:21849// modal) blocking the active browser. Note that tab modal dialogs (HTTP auth
850// sheets) will not count as blocking the browser. But things like open/save
851// dialogs that are window modal will block the browser.
[email protected]05454532013-01-22 21:09:08852- (BOOL)keyWindowIsModal {
853 if ([NSApp modalWindow])
854 return YES;
855
[email protected]f2bc6e92012-10-14 02:08:38856 Browser* browser = chrome::GetLastActiveBrowser();
[email protected]05454532013-01-22 21:09:08857 return browser &&
858 [[browser->window()->GetNativeWindow() attachedSheet]
859 isKindOfClass:[NSWindow class]];
[email protected]6a281332009-10-13 17:41:06860}
861
[email protected]88d74942009-01-21 22:04:44862// Called to validate menu items when there are no key windows. All the
863// items we care about have been set with the |commandDispatch:| action and
864// a target of FirstResponder in IB. If it's not one of those, let it
865// continue up the responder chain to be handled elsewhere. We pull out the
866// tag as the cross-platform constant to differentiate and dispatch the
867// various commands.
868- (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item {
869 SEL action = [item action];
870 BOOL enable = NO;
[email protected]05454532013-01-22 21:09:08871 if (action == @selector(commandDispatch:) ||
872 action == @selector(commandFromDock:)) {
[email protected]88d74942009-01-21 22:04:44873 NSInteger tag = [item tag];
[email protected]05454532013-01-22 21:09:08874 if (menuState_ && // NULL in tests.
875 menuState_->SupportsCommand(tag)) {
[email protected]fbc947b2009-06-19 13:28:24876 switch (tag) {
[email protected]419eb2a2009-11-11 16:26:26877 // The File Menu commands are not automatically disabled by Cocoa when a
878 // dialog sheet obscures the browser window, so we disable several of
879 // them here. We don't need to include IDC_CLOSE_WINDOW, because
880 // app_controller is only activated when there are no key windows (see
881 // function comment).
[email protected]fbc947b2009-06-19 13:28:24882 case IDC_RESTORE_TAB:
[email protected]05454532013-01-22 21:09:08883 enable = ![self keyWindowIsModal] && [self canRestoreTab];
[email protected]6a281332009-10-13 17:41:06884 break;
[email protected]f6c15fa2010-06-04 20:08:21885 // Browser-level items that open in new tabs should not open if there's
886 // a window- or app-modal dialog.
[email protected]6a281332009-10-13 17:41:06887 case IDC_OPEN_FILE:
[email protected]6a281332009-10-13 17:41:06888 case IDC_NEW_TAB:
[email protected]f6c15fa2010-06-04 20:08:21889 case IDC_SHOW_HISTORY:
890 case IDC_SHOW_BOOKMARK_MANAGER:
[email protected]05454532013-01-22 21:09:08891 enable = ![self keyWindowIsModal];
[email protected]f6c15fa2010-06-04 20:08:21892 break;
893 // Browser-level items that open in new windows.
[email protected]f6c15fa2010-06-04 20:08:21894 case IDC_TASK_MANAGER:
895 // Allow the user to open a new window if there's a window-modal
896 // dialog.
[email protected]05454532013-01-22 21:09:08897 enable = ![self keyWindowIsModal];
[email protected]fbc947b2009-06-19 13:28:24898 break;
[email protected]65549182012-02-25 00:45:40899 case IDC_SHOW_SYNC_SETUP: {
[email protected]a585a8db2011-06-20 18:58:35900 Profile* lastProfile = [self lastProfile];
[email protected]446e16a2010-05-03 21:23:58901 // The profile may be NULL during shutdown -- see
902 // http://code.google.com/p/chromium/issues/detail?id=43048 .
903 //
904 // TODO(akalin,viettrungluu): Figure out whether this method
[email protected]a585a8db2011-06-20 18:58:35905 // can be prevented from being called if lastProfile is
[email protected]446e16a2010-05-03 21:23:58906 // NULL.
[email protected]a585a8db2011-06-20 18:58:35907 if (!lastProfile) {
[email protected]446e16a2010-05-03 21:23:58908 LOG(WARNING)
[email protected]a585a8db2011-06-20 18:58:35909 << "NULL lastProfile detected -- not doing anything";
[email protected]446e16a2010-05-03 21:23:58910 break;
911 }
[email protected]074311a2013-02-28 23:14:09912 SigninManager* signin = SigninManagerFactory::GetForProfile(
913 lastProfile->GetOriginalProfile());
914 enable = signin->IsSigninAllowed() &&
915 ![self keyWindowIsModal];
[email protected]6bf51192013-01-18 12:35:25916 [BrowserWindowController updateSigninItem:item
917 shouldShow:enable
918 currentProfile:lastProfile];
[email protected]f9bc9b92009-11-24 00:55:35919 break;
[email protected]446e16a2010-05-03 21:23:58920 }
[email protected]236ad3022013-09-04 03:27:43921#if defined(GOOGLE_CHROME_BUILD)
[email protected]acaa37b2011-08-31 07:19:11922 case IDC_FEEDBACK:
923 enable = NO;
924 break;
[email protected]236ad3022013-09-04 03:27:43925#endif
[email protected]fbc947b2009-06-19 13:28:24926 default:
[email protected]f6c15fa2010-06-04 20:08:21927 enable = menuState_->IsCommandEnabled(tag) ?
[email protected]05454532013-01-22 21:09:08928 ![self keyWindowIsModal] : NO;
[email protected]fbc947b2009-06-19 13:28:24929 }
930 }
[email protected]3b6aa8b62009-09-15 21:36:11931 } else if (action == @selector(terminate:)) {
[email protected]88d74942009-01-21 22:04:44932 enable = YES;
[email protected]3111f08b2009-04-30 16:01:52933 } else if (action == @selector(showPreferences:)) {
934 enable = YES;
[email protected]bde3dda2009-05-20 22:13:07935 } else if (action == @selector(orderFrontStandardAboutPanel:)) {
936 enable = YES;
[email protected]93b59fc2010-12-21 20:00:47937 } else if (action == @selector(commandFromDock:)) {
[email protected]3d4bd23f2009-10-06 03:58:38938 enable = YES;
[email protected]a99fce0e2011-03-21 20:58:48939 } else if (action == @selector(toggleConfirmToQuit:)) {
940 [self updateConfirmToQuitPrefMenuItem:static_cast<NSMenuItem*>(item)];
941 enable = YES;
[email protected]3a90b452013-02-15 04:33:48942 } else if (action == @selector(executeApplication:)) {
943 enable = YES;
[email protected]88d74942009-01-21 22:04:44944 }
945 return enable;
946}
947
[email protected]947fc0d2010-01-14 22:33:05948// Called when the user picks a menu item when there are no key windows, or when
949// there is no foreground browser window. Calls through to the browser object to
950// execute the command. This assumes that the command is supported and doesn't
[email protected]d630d7d52010-02-21 00:55:11951// check, otherwise it should have been disabled in the UI in
952// |-validateUserInterfaceItem:|.
[email protected]88d74942009-01-21 22:04:44953- (void)commandDispatch:(id)sender {
[email protected]a585a8db2011-06-20 18:58:35954 Profile* lastProfile = [self lastProfile];
[email protected]f0a51fb52009-03-05 12:46:38955
[email protected]947fc0d2010-01-14 22:33:05956 // Handle the case where we're dispatching a command from a sender that's in a
957 // browser window. This means that the command came from a background window
958 // and is getting here because the foreground window is not a browser window.
959 if ([sender respondsToSelector:@selector(window)]) {
960 id delegate = [[sender window] windowController];
961 if ([delegate isKindOfClass:[BrowserWindowController class]]) {
962 [delegate commandDispatch:sender];
963 return;
964 }
965 }
966
[email protected]8d4bbdc2012-09-20 21:36:49967 // Ignore commands during session restore's browser creation. It uses a
968 // nested message loop and commands dispatched during this operation cause
969 // havoc.
970 if (SessionRestore::IsRestoring(lastProfile) &&
[email protected]b3a25092013-05-28 22:08:16971 base::MessageLoop::current()->IsNested())
[email protected]8d4bbdc2012-09-20 21:36:49972 return;
973
[email protected]88d74942009-01-21 22:04:44974 NSInteger tag = [sender tag];
975 switch (tag) {
[email protected]fbc947b2009-06-19 13:28:24976 case IDC_NEW_TAB:
[email protected]d630d7d52010-02-21 00:55:11977 // Create a new tab in an existing browser window (which we activate) if
978 // possible.
[email protected]a585a8db2011-06-20 18:58:35979 if (Browser* browser = ActivateBrowser(lastProfile)) {
[email protected]5d98294912012-06-27 22:57:40980 chrome::ExecuteCommand(browser, IDC_NEW_TAB);
[email protected]d630d7d52010-02-21 00:55:11981 break;
982 }
983 // Else fall through to create new window.
[email protected]88d74942009-01-21 22:04:44984 case IDC_NEW_WINDOW:
[email protected]a585a8db2011-06-20 18:58:35985 CreateBrowser(lastProfile);
[email protected]d630d7d52010-02-21 00:55:11986 break;
[email protected]91a5b3e2009-10-30 19:32:22987 case IDC_FOCUS_LOCATION:
[email protected]5d98294912012-06-27 22:57:40988 chrome::ExecuteCommand(ActivateOrCreateBrowser(lastProfile),
989 IDC_FOCUS_LOCATION);
[email protected]88d74942009-01-21 22:04:44990 break;
[email protected]cddc5242009-12-07 18:54:33991 case IDC_FOCUS_SEARCH:
[email protected]5d98294912012-06-27 22:57:40992 chrome::ExecuteCommand(ActivateOrCreateBrowser(lastProfile),
993 IDC_FOCUS_SEARCH);
[email protected]cddc5242009-12-07 18:54:33994 break;
[email protected]863ff662009-01-26 20:18:18995 case IDC_NEW_INCOGNITO_WINDOW:
[email protected]eddcf7502012-02-09 22:43:48996 CreateBrowser(lastProfile->GetOffTheRecordProfile());
[email protected]863ff662009-01-26 20:18:18997 break;
[email protected]fbc947b2009-06-19 13:28:24998 case IDC_RESTORE_TAB:
[email protected]3b265312013-01-17 02:49:55999 // There is only the native desktop on Mac.
1000 chrome::OpenWindowWithRestoredTabs(lastProfile,
1001 chrome::HOST_DESKTOP_TYPE_NATIVE);
[email protected]fbc947b2009-06-19 13:28:241002 break;
[email protected]e19516d2009-04-28 17:15:191003 case IDC_OPEN_FILE:
[email protected]5d98294912012-06-27 22:57:401004 chrome::ExecuteCommand(CreateBrowser(lastProfile), IDC_OPEN_FILE);
[email protected]e19516d2009-04-28 17:15:191005 break;
[email protected]1fdff702009-10-22 00:36:181006 case IDC_CLEAR_BROWSING_DATA: {
[email protected]93e181762009-05-29 14:30:381007 // There may not be a browser open, so use the default profile.
[email protected]a585a8db2011-06-20 18:58:351008 if (Browser* browser = ActivateBrowser(lastProfile)) {
[email protected]5d9cace72012-06-21 16:07:121009 chrome::ShowClearBrowsingDataDialog(browser);
[email protected]c8de64a2011-01-25 17:10:231010 } else {
[email protected]1f0b50b2012-06-22 20:37:161011 chrome::OpenClearBrowsingDataDialogWindow(lastProfile);
[email protected]c8de64a2011-01-25 17:10:231012 }
[email protected]93e181762009-05-29 14:30:381013 break;
[email protected]1fdff702009-10-22 00:36:181014 }
[email protected]4aee5652009-11-19 18:59:211015 case IDC_IMPORT_SETTINGS: {
[email protected]a585a8db2011-06-20 18:58:351016 if (Browser* browser = ActivateBrowser(lastProfile)) {
[email protected]5d9cace72012-06-21 16:07:121017 chrome::ShowImportDialog(browser);
[email protected]c8de64a2011-01-25 17:10:231018 } else {
[email protected]1f0b50b2012-06-22 20:37:161019 chrome::OpenImportSettingsDialogWindow(lastProfile);
[email protected]c8de64a2011-01-25 17:10:231020 }
[email protected]4aee5652009-11-19 18:59:211021 break;
1022 }
[email protected]af7c25e2010-01-23 05:46:451023 case IDC_SHOW_BOOKMARK_MANAGER:
[email protected]7f6f44c2011-12-14 13:23:381024 content::RecordAction(UserMetricsAction("ShowBookmarkManager"));
[email protected]a585a8db2011-06-20 18:58:351025 if (Browser* browser = ActivateBrowser(lastProfile)) {
[email protected]5d9cace72012-06-21 16:07:121026 chrome::ShowBookmarkManager(browser);
[email protected]90a8bf252010-03-12 00:09:471027 } else {
[email protected]47e532b2010-04-23 17:46:401028 // No browser window, so create one for the bookmark manager tab.
[email protected]1f0b50b2012-06-22 20:37:161029 chrome::OpenBookmarkManagerWindow(lastProfile);
[email protected]90a8bf252010-03-12 00:09:471030 }
[email protected]af7c25e2010-01-23 05:46:451031 break;
[email protected]91a5b3e2009-10-30 19:32:221032 case IDC_SHOW_HISTORY:
[email protected]a585a8db2011-06-20 18:58:351033 if (Browser* browser = ActivateBrowser(lastProfile))
[email protected]5d9cace72012-06-21 16:07:121034 chrome::ShowHistory(browser);
[email protected]d630d7d52010-02-21 00:55:111035 else
[email protected]1f0b50b2012-06-22 20:37:161036 chrome::OpenHistoryWindow(lastProfile);
[email protected]91a5b3e2009-10-30 19:32:221037 break;
1038 case IDC_SHOW_DOWNLOADS:
[email protected]a585a8db2011-06-20 18:58:351039 if (Browser* browser = ActivateBrowser(lastProfile))
[email protected]5d9cace72012-06-21 16:07:121040 chrome::ShowDownloads(browser);
[email protected]d630d7d52010-02-21 00:55:111041 else
[email protected]1f0b50b2012-06-22 20:37:161042 chrome::OpenDownloadsWindow(lastProfile);
[email protected]91a5b3e2009-10-30 19:32:221043 break;
[email protected]bb37e6ec2009-11-18 17:59:361044 case IDC_MANAGE_EXTENSIONS:
[email protected]a585a8db2011-06-20 18:58:351045 if (Browser* browser = ActivateBrowser(lastProfile))
[email protected]bc9833c32013-02-28 04:05:081046 chrome::ShowExtensions(browser, std::string());
[email protected]d630d7d52010-02-21 00:55:111047 else
[email protected]1f0b50b2012-06-22 20:37:161048 chrome::OpenExtensionsWindow(lastProfile);
[email protected]bb37e6ec2009-11-18 17:59:361049 break;
[email protected]2ca35612012-06-12 03:49:481050 case IDC_HELP_PAGE_VIA_MENU:
[email protected]a585a8db2011-06-20 18:58:351051 if (Browser* browser = ActivateBrowser(lastProfile))
[email protected]5d9cace72012-06-21 16:07:121052 chrome::ShowHelp(browser, chrome::HELP_SOURCE_MENU);
[email protected]d630d7d52010-02-21 00:55:111053 else
[email protected]1f0b50b2012-06-22 20:37:161054 chrome::OpenHelpWindow(lastProfile, chrome::HELP_SOURCE_MENU);
[email protected]1fdff702009-10-22 00:36:181055 break;
[email protected]65549182012-02-25 00:45:401056 case IDC_SHOW_SYNC_SETUP:
[email protected]3d27d272013-07-31 03:15:161057 if (Browser* browser = ActivateBrowser(lastProfile)) {
1058 chrome::ShowBrowserSignin(browser, signin::SOURCE_MENU);
1059 } else {
1060 chrome::OpenSyncSetupWindow(lastProfile, signin::SOURCE_MENU);
1061 }
[email protected]f9bc9b92009-11-24 00:55:351062 break;
[email protected]ceaaf1812010-01-20 22:13:091063 case IDC_TASK_MANAGER:
[email protected]7f6f44c2011-12-14 13:23:381064 content::RecordAction(UserMetricsAction("TaskManager"));
[email protected]29c262de2013-06-22 15:39:381065 TaskManagerMac::Show();
[email protected]ceaaf1812010-01-20 22:13:091066 break;
[email protected]4a42d272010-06-18 01:29:421067 case IDC_OPTIONS:
1068 [self showPreferences:sender];
1069 break;
[email protected]d630d7d52010-02-21 00:55:111070 }
[email protected]88d74942009-01-21 22:04:441071}
1072
[email protected]93b59fc2010-12-21 20:00:471073// Run a (background) application in a new tab.
1074- (void)executeApplication:(id)sender {
1075 NSInteger tag = [sender tag];
[email protected]a585a8db2011-06-20 18:58:351076 Profile* profile = [self lastProfile];
[email protected]93b59fc2010-12-21 20:00:471077 DCHECK(profile);
1078 BackgroundApplicationListModel applications(profile);
1079 DCHECK(tag >= 0 &&
1080 tag < static_cast<int>(applications.size()));
[email protected]1c321ee2012-05-21 03:02:341081 const extensions::Extension* extension = applications.GetExtension(tag);
[email protected]78d78a62011-09-29 18:24:331082 BackgroundModeManager::LaunchBackgroundApplication(profile, extension);
[email protected]93b59fc2010-12-21 20:00:471083}
1084
[email protected]947fc0d2010-01-14 22:33:051085// Same as |-commandDispatch:|, but executes commands using a disposition
1086// determined by the key flags. This will get called in the case where the
1087// frontmost window is not a browser window, and the user has command-clicked
1088// a button in a background browser window whose action is
1089// |-commandDispatchUsingKeyModifiers:|
1090- (void)commandDispatchUsingKeyModifiers:(id)sender {
1091 DCHECK(sender);
1092 if ([sender respondsToSelector:@selector(window)]) {
1093 id delegate = [[sender window] windowController];
1094 if ([delegate isKindOfClass:[BrowserWindowController class]]) {
1095 [delegate commandDispatchUsingKeyModifiers:sender];
1096 }
1097 }
1098}
1099
[email protected]2e29e2232013-07-26 10:40:591100// NSApplication delegate method called when someone clicks on the dock icon.
1101// To match standard mac behavior, we should open a new window if there are no
1102// browser windows.
[email protected]ff81e0a2009-04-13 14:58:541103- (BOOL)applicationShouldHandleReopen:(NSApplication*)theApplication
[email protected]bf193bc52013-06-08 04:39:581104 hasVisibleWindows:(BOOL)hasVisibleWindows {
[email protected]70631402010-04-26 20:16:541105 // If the browser is currently trying to quit, don't do anything and return NO
1106 // to prevent AppKit from doing anything.
1107 // TODO(rohitrao): Remove this code when http://crbug.com/40861 is resolved.
1108 if (browser_shutdown::IsTryingToQuit())
1109 return NO;
1110
[email protected]2e29e2232013-07-26 10:40:591111 // Bring all browser windows to the front. Specifically, this brings them in
1112 // front of any app windows. FocusWindowSet will also unminimize the most
1113 // recently minimized window if no windows in the set are visible.
[email protected]a25920ee2013-09-05 19:38:491114 // If there are any, return here. Otherwise, the windows are panels or
1115 // notifications so we still need to open a new window.
[email protected]bf193bc52013-06-08 04:39:581116 if (hasVisibleWindows) {
[email protected]2e29e2232013-07-26 10:40:591117 std::set<NSWindow*> browserWindows;
[email protected]b4207c42013-02-12 06:44:201118 for (chrome::BrowserIterator iter; !iter.done(); iter.Next()) {
[email protected]0236be22012-01-11 01:05:351119 Browser* browser = *iter;
[email protected]2e29e2232013-07-26 10:40:591120 browserWindows.insert(browser->window()->GetNativeWindow());
[email protected]0236be22012-01-11 01:05:351121 }
[email protected]a25920ee2013-09-05 19:38:491122 if (!browserWindows.empty()) {
[email protected]d4c811e72013-10-29 21:57:551123 NSWindow* keyWindow = [NSApp keyWindow];
1124 if (keyWindow && ![keyWindow isOnActiveSpace]) {
1125 // The key window is not on the active space. We must be mid-animation
1126 // for a space transition triggered by the dock. Delay the call to
1127 // |ui::FocusWindowSet| until the transition completes. Otherwise, the
1128 // wrong space's windows get raised, resulting in an off-screen key
1129 // window. It does not work to |ui::FocusWindowSet| twice, once here
1130 // and once in |activeSpaceDidChange:|, as that appears to break when
1131 // the omnibox is focused.
1132 //
1133 // This check relies on OS X setting the key window to a window on the
1134 // target space before calling this method.
1135 //
1136 // See http://crbug.com/309656.
1137 reopenTime_ = base::TimeTicks::Now();
1138 } else {
1139 ui::FocusWindowSet(browserWindows, false);
1140 }
1141 // Return NO; we've done (or soon will do) the deminiaturize, so
1142 // AppKit shouldn't do anything.
[email protected]a25920ee2013-09-05 19:38:491143 return NO;
1144 }
[email protected]0236be22012-01-11 01:05:351145 }
[email protected]ff81e0a2009-04-13 14:58:541146
[email protected]0d1210d62010-09-07 17:31:381147 // If launched as a hidden login item (due to installation of a persistent app
[email protected]eddcf7502012-02-09 22:43:481148 // or by the user, for example in System Preferences->Accounts->Login Items),
[email protected]0d1210d62010-09-07 17:31:381149 // allow session to be restored first time the user clicks on a Dock icon.
1150 // Normally, it'd just open a new empty page.
1151 {
[email protected]eddcf7502012-02-09 22:43:481152 static BOOL doneOnce = NO;
1153 if (!doneOnce) {
1154 doneOnce = YES;
1155 if (base::mac::WasLaunchedAsHiddenLoginItem()) {
1156 SessionService* sessionService =
[email protected]26f39ce52013-10-29 05:13:131157 SessionServiceFactory::GetForProfileForSessionRestore(
1158 [self lastProfile]);
[email protected]eddcf7502012-02-09 22:43:481159 if (sessionService &&
1160 sessionService->RestoreIfNecessary(std::vector<GURL>()))
1161 return NO;
[email protected]0d1210d62010-09-07 17:31:381162 }
[email protected]eddcf7502012-02-09 22:43:481163 }
[email protected]0d1210d62010-09-07 17:31:381164 }
[email protected]fc44f242012-02-14 16:54:391165
1166 // Platform apps don't use browser windows so don't do anything if there are
[email protected]bf193bc52013-06-08 04:39:581167 // visible windows, otherwise, launch the browser with the same command line
1168 // which should launch the app again.
[email protected]fc44f242012-02-14 16:54:391169 const CommandLine& command_line = *CommandLine::ForCurrentProcess();
[email protected]bf193bc52013-06-08 04:39:581170 if (command_line.HasSwitch(switches::kAppId)) {
1171 if (hasVisibleWindows)
1172 return YES;
1173
1174 {
1175 base::AutoReset<bool> auto_reset_in_run(&g_is_opening_new_window, true);
1176 int return_code;
1177 StartupBrowserCreator browser_creator;
1178 browser_creator.LaunchBrowser(
1179 command_line, [self lastProfile], base::FilePath(),
1180 chrome::startup::IS_NOT_PROCESS_STARTUP,
1181 chrome::startup::IS_NOT_FIRST_RUN, &return_code);
1182 }
1183 return NO;
1184 }
[email protected]fc44f242012-02-14 16:54:391185
[email protected]ff81e0a2009-04-13 14:58:541186 // Otherwise open a new window.
[email protected]bf193bc52013-06-08 04:39:581187 CreateBrowser([self lastProfile]);
[email protected]ff81e0a2009-04-13 14:58:541188
1189 // We've handled the reopen event, so return NO to tell AppKit not
1190 // to do anything.
1191 return NO;
1192}
1193
[email protected]88d74942009-01-21 22:04:441194- (void)initMenuState {
[email protected]3111f08b2009-04-30 16:01:521195 menuState_.reset(new CommandUpdater(NULL));
[email protected]fbc947b2009-06-19 13:28:241196 menuState_->UpdateCommandEnabled(IDC_NEW_TAB, true);
[email protected]88d74942009-01-21 22:04:441197 menuState_->UpdateCommandEnabled(IDC_NEW_WINDOW, true);
[email protected]863ff662009-01-26 20:18:181198 menuState_->UpdateCommandEnabled(IDC_NEW_INCOGNITO_WINDOW, true);
[email protected]e19516d2009-04-28 17:15:191199 menuState_->UpdateCommandEnabled(IDC_OPEN_FILE, true);
[email protected]93e181762009-05-29 14:30:381200 menuState_->UpdateCommandEnabled(IDC_CLEAR_BROWSING_DATA, true);
[email protected]fbc947b2009-06-19 13:28:241201 menuState_->UpdateCommandEnabled(IDC_RESTORE_TAB, false);
[email protected]91a5b3e2009-10-30 19:32:221202 menuState_->UpdateCommandEnabled(IDC_FOCUS_LOCATION, true);
[email protected]cddc5242009-12-07 18:54:331203 menuState_->UpdateCommandEnabled(IDC_FOCUS_SEARCH, true);
[email protected]af7c25e2010-01-23 05:46:451204 menuState_->UpdateCommandEnabled(IDC_SHOW_BOOKMARK_MANAGER, true);
[email protected]91a5b3e2009-10-30 19:32:221205 menuState_->UpdateCommandEnabled(IDC_SHOW_HISTORY, true);
1206 menuState_->UpdateCommandEnabled(IDC_SHOW_DOWNLOADS, true);
[email protected]bb37e6ec2009-11-18 17:59:361207 menuState_->UpdateCommandEnabled(IDC_MANAGE_EXTENSIONS, true);
[email protected]2ca35612012-06-12 03:49:481208 menuState_->UpdateCommandEnabled(IDC_HELP_PAGE_VIA_MENU, true);
[email protected]4aee5652009-11-19 18:59:211209 menuState_->UpdateCommandEnabled(IDC_IMPORT_SETTINGS, true);
[email protected]236ad3022013-09-04 03:27:431210#if defined(GOOGLE_CHROME_BUILD)
[email protected]1c41a932010-11-22 23:41:231211 menuState_->UpdateCommandEnabled(IDC_FEEDBACK, true);
[email protected]236ad3022013-09-04 03:27:431212#endif
[email protected]65549182012-02-25 00:45:401213 menuState_->UpdateCommandEnabled(IDC_SHOW_SYNC_SETUP, true);
[email protected]ceaaf1812010-01-20 22:13:091214 menuState_->UpdateCommandEnabled(IDC_TASK_MANAGER, true);
[email protected]88d74942009-01-21 22:04:441215}
[email protected]1bcdb532009-01-16 17:47:571216
[email protected]961a6932011-07-19 19:52:461217// Conditionally adds the Profile menu to the main menu bar.
1218- (void)initProfileMenu {
[email protected]961a6932011-07-19 19:52:461219 NSMenu* mainMenu = [NSApp mainMenu];
1220 NSMenuItem* profileMenu = [mainMenu itemWithTag:IDC_PROFILE_MAIN_MENU];
[email protected]e354ca42011-08-03 20:31:401221
[email protected]de71ae992013-07-18 03:30:381222 if (!profiles::IsMultipleProfilesEnabled()) {
[email protected]e354ca42011-08-03 20:31:401223 [mainMenu removeItem:profileMenu];
1224 return;
1225 }
1226
[email protected]6b2d30bc2011-10-11 18:16:391227 // The controller will unhide the menu if necessary.
1228 [profileMenu setHidden:YES];
[email protected]961a6932011-07-19 19:52:461229
[email protected]6b2d30bc2011-10-11 18:16:391230 profileMenuController_.reset(
1231 [[ProfileMenuController alloc] initWithMainMenuItem:profileMenu]);
[email protected]961a6932011-07-19 19:52:461232}
1233
[email protected]a99fce0e2011-03-21 20:58:481234// The Confirm to Quit preference is atypical in that the preference lives in
1235// the app menu right above the Quit menu item. This method will refresh the
1236// display of that item depending on the preference state.
1237- (void)updateConfirmToQuitPrefMenuItem:(NSMenuItem*)item {
1238 // Format the string so that the correct key equivalent is displayed.
1239 NSString* acceleratorString = [ConfirmQuitPanelController keyCommandString];
1240 NSString* title = l10n_util::GetNSStringF(IDS_CONFIRM_TO_QUIT_OPTION,
1241 base::SysNSStringToUTF16(acceleratorString));
1242 [item setTitle:title];
1243
[email protected]b6366ffa2012-02-29 17:12:261244 const PrefService* prefService = g_browser_process->local_state();
[email protected]a99fce0e2011-03-21 20:58:481245 bool enabled = prefService->GetBoolean(prefs::kConfirmToQuitEnabled);
1246 [item setState:enabled ? NSOnState : NSOffState];
1247}
1248
[email protected]eef99c22010-08-17 05:55:161249- (void)registerServicesMenuTypesTo:(NSApplication*)app {
[email protected]09ea24c2010-08-23 19:05:411250 // Note that RenderWidgetHostViewCocoa implements NSServicesRequests which
1251 // handles requests from services.
1252 NSArray* types = [NSArray arrayWithObjects:NSStringPboardType, nil];
1253 [app registerServicesMenuSendTypes:types returnTypes:types];
[email protected]eef99c22010-08-17 05:55:161254}
1255
[email protected]a585a8db2011-06-20 18:58:351256- (Profile*)lastProfile {
[email protected]7fd5a462011-06-19 16:57:231257 // Return the profile of the last-used BrowserWindowController, if available.
1258 if (lastProfile_)
1259 return lastProfile_;
1260
[email protected]056f8792011-07-20 15:40:151261 // On first launch, no profile will be stored, so use last from Local State.
[email protected]3f34599d2009-03-25 22:11:431262 if (g_browser_process->profile_manager())
[email protected]056f8792011-07-20 15:40:151263 return g_browser_process->profile_manager()->GetLastUsedProfile();
[email protected]3f34599d2009-03-25 22:11:431264
[email protected]f6314002009-04-23 01:18:131265 return NULL;
[email protected]3f34599d2009-03-25 22:11:431266}
1267
[email protected]57750f822009-04-21 21:43:091268// Various methods to open URLs that we get in a native fashion. We use
[email protected]fe7c4872012-05-10 20:06:031269// StartupBrowserCreator here because on the other platforms, URLs to open come
1270// through the ProcessSingleton, and it calls StartupBrowserCreator. It's best
1271// to bottleneck the openings through that for uniform handling.
[email protected]57750f822009-04-21 21:43:091272
[email protected]d630d7d52010-02-21 00:55:111273- (void)openUrls:(const std::vector<GURL>&)urls {
[email protected]caadfca2010-01-22 21:51:211274 // If the browser hasn't started yet, just queue up the URLs.
1275 if (!startupComplete_) {
[email protected]d630d7d52010-02-21 00:55:111276 startupUrls_.insert(startupUrls_.end(), urls.begin(), urls.end());
[email protected]136140c2009-05-19 13:58:251277 return;
1278 }
[email protected]57750f822009-04-21 21:43:091279
[email protected]f2bc6e92012-10-14 02:08:381280 Browser* browser = chrome::GetLastActiveBrowser();
[email protected]0e863512009-05-28 19:45:071281 // if no browser window exists then create one with no tabs to be filled in
1282 if (!browser) {
[email protected]c125cdc2013-02-25 19:10:211283 browser = new Browser(Browser::CreateParams(
1284 [self lastProfile], chrome::HOST_DESKTOP_TYPE_NATIVE));
[email protected]ce560f82009-06-03 09:39:441285 browser->window()->Show();
[email protected]0e863512009-05-28 19:45:071286 }
1287
[email protected]947446b2010-10-21 03:36:311288 CommandLine dummy(CommandLine::NO_PROGRAM);
[email protected]4feb37d92012-07-01 20:22:131289 chrome::startup::IsFirstRun first_run = first_run::IsChromeFirstRun() ?
1290 chrome::startup::IS_FIRST_RUN : chrome::startup::IS_NOT_FIRST_RUN;
[email protected]650b2d52013-02-10 03:41:451291 StartupBrowserCreatorImpl launch(base::FilePath(), dummy, first_run);
[email protected]921071f852013-05-25 03:22:081292 launch.OpenURLsInBrowser(browser, false, urls, browser->host_desktop_type());
[email protected]57750f822009-04-21 21:43:091293}
1294
[email protected]57750f822009-04-21 21:43:091295- (void)getUrl:(NSAppleEventDescriptor*)event
1296 withReply:(NSAppleEventDescriptor*)reply {
1297 NSString* urlStr = [[event paramDescriptorForKeyword:keyDirectObject]
1298 stringValue];
1299
1300 GURL gurl(base::SysNSStringToUTF8(urlStr));
1301 std::vector<GURL> gurlVector;
1302 gurlVector.push_back(gurl);
1303
[email protected]d630d7d52010-02-21 00:55:111304 [self openUrls:gurlVector];
[email protected]57750f822009-04-21 21:43:091305}
1306
[email protected]a529af52011-08-15 20:56:171307// Apple Event handler that receives print event from service
1308// process, gets the required data and launches Print dialog.
1309- (void)submitCloudPrintJob:(NSAppleEventDescriptor*)event {
1310 // Pull parameter list out of Apple Event.
[email protected]ebbe94b2012-01-18 20:11:561311 NSAppleEventDescriptor* paramList =
[email protected]13aec6c42011-10-27 21:22:081312 [event paramDescriptorForKeyword:cloud_print::kAECloudPrintClass];
[email protected]a529af52011-08-15 20:56:171313
1314 if (paramList != nil) {
1315 // Pull required fields out of parameter list.
1316 NSString* mime = [[paramList descriptorAtIndex:1] stringValue];
1317 NSString* inputPath = [[paramList descriptorAtIndex:2] stringValue];
1318 NSString* printTitle = [[paramList descriptorAtIndex:3] stringValue];
[email protected]e8368e92011-08-20 04:05:561319 NSString* printTicket = [[paramList descriptorAtIndex:4] stringValue];
[email protected]a529af52011-08-15 20:56:171320 // Convert the title to UTF 16 as required.
[email protected]96920152013-12-04 21:00:161321 base::string16 title16 = base::SysNSStringToUTF16(printTitle);
1322 base::string16 printTicket16 = base::SysNSStringToUTF16(printTicket);
[email protected]a529af52011-08-15 20:56:171323 print_dialog_cloud::CreatePrintDialogForFile(
[email protected]77d83542013-12-13 08:54:291324 ProfileManager::GetActiveUserProfile(), NULL,
1325 base::FilePath([inputPath fileSystemRepresentation]), title16,
[email protected]b5b79d72012-05-24 19:42:281326 printTicket16, [mime UTF8String], /*delete_on_close=*/false);
[email protected]a529af52011-08-15 20:56:171327 }
1328}
1329
[email protected]caadfca2010-01-22 21:51:211330- (void)application:(NSApplication*)sender
1331 openFiles:(NSArray*)filenames {
[email protected]57750f822009-04-21 21:43:091332 std::vector<GURL> gurlVector;
[email protected]caadfca2010-01-22 21:51:211333 for (NSString* file in filenames) {
[email protected]650b2d52013-02-10 03:41:451334 GURL gurl =
[email protected]77d83542013-12-13 08:54:291335 net::FilePathToFileURL(base::FilePath([file fileSystemRepresentation]));
[email protected]57750f822009-04-21 21:43:091336 gurlVector.push_back(gurl);
1337 }
[email protected]caadfca2010-01-22 21:51:211338 if (!gurlVector.empty())
[email protected]d630d7d52010-02-21 00:55:111339 [self openUrls:gurlVector];
[email protected]caadfca2010-01-22 21:51:211340 else
1341 NOTREACHED() << "Nothing to open!";
[email protected]57750f822009-04-21 21:43:091342
[email protected]caadfca2010-01-22 21:51:211343 [sender replyToOpenOrPrint:NSApplicationDelegateReplySuccess];
[email protected]57750f822009-04-21 21:43:091344}
[email protected]3f34599d2009-03-25 22:11:431345
[email protected]3111f08b2009-04-30 16:01:521346// Show the preferences window, or bring it to the front if it's already
1347// visible.
1348- (IBAction)showPreferences:(id)sender {
[email protected]a585a8db2011-06-20 18:58:351349 if (Browser* browser = ActivateBrowser([self lastProfile])) {
[email protected]445177652011-03-09 02:04:051350 // Show options tab in the active browser window.
[email protected]5d9cace72012-06-21 16:07:121351 chrome::ShowSettings(browser);
[email protected]4a42d272010-06-18 01:29:421352 } else {
[email protected]445177652011-03-09 02:04:051353 // No browser window, so create one for the options tab.
[email protected]1f0b50b2012-06-22 20:37:161354 chrome::OpenOptionsWindow([self lastProfile]);
[email protected]4a42d272010-06-18 01:29:421355 }
[email protected]d989f07c2009-11-14 00:35:461356}
1357
[email protected]bde3dda2009-05-20 22:13:071358- (IBAction)orderFrontStandardAboutPanel:(id)sender {
[email protected]a048ad22012-03-23 04:26:561359 if (Browser* browser = ActivateBrowser([self lastProfile])) {
[email protected]5d9cace72012-06-21 16:07:121360 chrome::ShowAboutChrome(browser);
[email protected]a9fbb0962012-03-21 23:25:131361 } else {
[email protected]a048ad22012-03-23 04:26:561362 // No browser window, so create one for the about tab.
[email protected]1f0b50b2012-06-22 20:37:161363 chrome::OpenAboutWindow([self lastProfile]);
[email protected]bde3dda2009-05-20 22:13:071364 }
[email protected]bde3dda2009-05-20 22:13:071365}
1366
[email protected]a99fce0e2011-03-21 20:58:481367- (IBAction)toggleConfirmToQuit:(id)sender {
[email protected]b6366ffa2012-02-29 17:12:261368 PrefService* prefService = g_browser_process->local_state();
[email protected]a99fce0e2011-03-21 20:58:481369 bool enabled = prefService->GetBoolean(prefs::kConfirmToQuitEnabled);
1370 prefService->SetBoolean(prefs::kConfirmToQuitEnabled, !enabled);
1371}
1372
[email protected]3d4bd23f2009-10-06 03:58:381373// Explicitly bring to the foreground when creating new windows from the dock.
[email protected]93b59fc2010-12-21 20:00:471374- (void)commandFromDock:(id)sender {
[email protected]3d4bd23f2009-10-06 03:58:381375 [NSApp activateIgnoringOtherApps:YES];
1376 [self commandDispatch:sender];
1377}
1378
[email protected]227fcee2010-06-11 19:19:371379- (NSMenu*)applicationDockMenu:(NSApplication*)sender {
[email protected]018a3962009-09-17 22:23:441380 NSMenu* dockMenu = [[[NSMenu alloc] initWithTitle: @""] autorelease];
[email protected]a585a8db2011-06-20 18:58:351381 Profile* profile = [self lastProfile];
[email protected]93b59fc2010-12-21 20:00:471382
[email protected]646fadbf2012-09-08 01:49:011383 BOOL profilesAdded = [profileMenuController_ insertItemsIntoMenu:dockMenu
1384 atOffset:0
1385 fromDock:YES];
1386 if (profilesAdded)
1387 [dockMenu addItem:[NSMenuItem separatorItem]];
[email protected]3d272f52012-08-23 19:00:331388
[email protected]646fadbf2012-09-08 01:49:011389 NSString* titleStr = l10n_util::GetNSStringWithFixup(IDS_NEW_WINDOW_MAC);
[email protected]a8522032013-06-24 22:51:461390 base::scoped_nsobject<NSMenuItem> item(
[email protected]8c6b029d2011-01-21 22:39:511391 [[NSMenuItem alloc] initWithTitle:titleStr
1392 action:@selector(commandFromDock:)
1393 keyEquivalent:@""]);
[email protected]288bfcd32009-09-14 18:14:461394 [item setTarget:self];
[email protected]646fadbf2012-09-08 01:49:011395 [item setTag:IDC_NEW_WINDOW];
[email protected]05454532013-01-22 21:09:081396 [item setEnabled:[self validateUserInterfaceItem:item]];
[email protected]646fadbf2012-09-08 01:49:011397 [dockMenu addItem:item];
1398
[email protected]8d10a412013-08-09 19:18:001399 // |profile| can be NULL during unit tests.
1400 if (!profile || !profile->IsManaged()) {
1401 titleStr = l10n_util::GetNSStringWithFixup(IDS_NEW_INCOGNITO_WINDOW_MAC);
1402 item.reset(
1403 [[NSMenuItem alloc] initWithTitle:titleStr
1404 action:@selector(commandFromDock:)
1405 keyEquivalent:@""]);
1406 [item setTarget:self];
1407 [item setTag:IDC_NEW_INCOGNITO_WINDOW];
1408 [item setEnabled:[self validateUserInterfaceItem:item]];
1409 [dockMenu addItem:item];
1410 }
[email protected]288bfcd32009-09-14 18:14:461411
[email protected]8c6b029d2011-01-21 22:39:511412 // TODO(rickcam): Mock out BackgroundApplicationListModel, then add unit
1413 // tests which use the mock in place of the profile-initialized model.
1414
1415 // Avoid breaking unit tests which have no profile.
1416 if (profile) {
1417 BackgroundApplicationListModel applications(profile);
1418 if (applications.size()) {
1419 int position = 0;
1420 NSString* menuStr =
1421 l10n_util::GetNSStringWithFixup(IDS_BACKGROUND_APPS_MAC);
[email protected]a8522032013-06-24 22:51:461422 base::scoped_nsobject<NSMenu> appMenu(
1423 [[NSMenu alloc] initWithTitle:menuStr]);
[email protected]1c321ee2012-05-21 03:02:341424 for (extensions::ExtensionList::const_iterator cursor =
[email protected]3a90b452013-02-15 04:33:481425 applications.begin();
[email protected]8c6b029d2011-01-21 22:39:511426 cursor != applications.end();
1427 ++cursor, ++position) {
[email protected]f15d9ebc2013-07-02 00:14:501428 DCHECK_EQ(applications.GetPosition(cursor->get()), position);
[email protected]8c6b029d2011-01-21 22:39:511429 NSString* itemStr =
[email protected]670d3232013-12-24 17:58:581430 base::SysUTF16ToNSString(base::UTF8ToUTF16((*cursor)->name()));
[email protected]a8522032013-06-24 22:51:461431 base::scoped_nsobject<NSMenuItem> appItem(
1432 [[NSMenuItem alloc] initWithTitle:itemStr
1433 action:@selector(executeApplication:)
1434 keyEquivalent:@""]);
[email protected]8c6b029d2011-01-21 22:39:511435 [appItem setTarget:self];
1436 [appItem setTag:position];
1437 [appMenu addItem:appItem];
1438 }
[email protected]8c6b029d2011-01-21 22:39:511439 }
1440 }
1441
[email protected]018a3962009-09-17 22:23:441442 return dockMenu;
[email protected]288bfcd32009-09-14 18:14:461443}
1444
[email protected]d630d7d52010-02-21 00:55:111445- (const std::vector<GURL>&)startupUrls {
1446 return startupUrls_;
[email protected]caadfca2010-01-22 21:51:211447}
1448
[email protected]d630d7d52010-02-21 00:55:111449- (void)clearStartupUrls {
1450 startupUrls_.clear();
[email protected]caadfca2010-01-22 21:51:211451}
1452
[email protected]7e4c69d02011-05-11 22:16:471453- (BookmarkMenuBridge*)bookmarkMenuBridge {
1454 return bookmarkMenuBridge_.get();
1455}
1456
[email protected]33190c82012-04-10 17:04:441457- (void)addObserverForWorkAreaChange:(ui::WorkAreaWatcherObserver*)observer {
1458 workAreaChangeObservers_.AddObserver(observer);
1459}
1460
1461- (void)removeObserverForWorkAreaChange:(ui::WorkAreaWatcherObserver*)observer {
1462 workAreaChangeObservers_.RemoveObserver(observer);
1463}
1464
[email protected]e7dc3992013-11-06 02:16:421465- (void)initAppShimMenuController {
1466 if (apps::IsAppShimsEnabled() && !appShimMenuController_)
1467 appShimMenuController_.reset([[AppShimMenuController alloc] init]);
1468}
1469
[email protected]ebbe94b2012-01-18 20:11:561470- (void)applicationDidChangeScreenParameters:(NSNotification*)notification {
[email protected]6a455072011-11-02 01:32:281471 // During this callback the working area is not always already updated. Defer.
[email protected]33190c82012-04-10 17:04:441472 [self performSelector:@selector(delayedScreenParametersUpdate)
[email protected]6a455072011-11-02 01:32:281473 withObject:nil
1474 afterDelay:0];
1475}
1476
[email protected]33190c82012-04-10 17:04:441477- (void)delayedScreenParametersUpdate {
1478 FOR_EACH_OBSERVER(ui::WorkAreaWatcherObserver, workAreaChangeObservers_,
1479 WorkAreaChanged());
[email protected]6a455072011-11-02 01:32:281480}
1481
[email protected]d630d7d52010-02-21 00:55:111482@end // @implementation AppController
[email protected]60ad3e22009-09-18 21:07:191483
1484//---------------------------------------------------------------------------
1485
[email protected]216e9042009-10-29 17:05:231486namespace app_controller_mac {
1487
1488bool IsOpeningNewWindow() {
1489 return g_is_opening_new_window;
1490}
1491
1492} // namespace app_controller_mac