blob: 032f72d35e2cc1d92aea62377c43c8baf54e2f16 [file] [log] [blame]
[email protected]c8de64a2011-01-25 17:10:231// Copyright (c) 2011 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]d630d7d52010-02-21 00:55:117#include "base/auto_reset.h"
[email protected]57750f822009-04-21 21:43:098#include "base/command_line.h"
[email protected]f805fe82010-08-03 22:47:109#include "base/file_path.h"
[email protected]151c4a62011-04-22 04:15:1310#include "base/mac/foundation_util.h"
[email protected]0378bf42011-01-01 18:20:1411#include "base/mac/mac_util.h"
[email protected]2c47bc12009-04-10 20:14:0012#include "base/message_loop.h"
[email protected]528c56d2010-07-30 19:28:4413#include "base/string_number_conversions.h"
[email protected]57750f822009-04-21 21:43:0914#include "base/sys_string_conversions.h"
[email protected]061a5ea82011-08-03 10:11:5015#include "base/utf_string_conversions.h"
[email protected]1a3aba82010-11-08 23:52:5416#include "chrome/app/chrome_command_ids.h"
[email protected]a07676b22011-06-17 16:36:5317#include "chrome/browser/background/background_application_list_model.h"
[email protected]5c238752009-06-13 10:29:0718#include "chrome/browser/browser_process.h"
[email protected]41741a962009-02-18 21:51:3919#include "chrome/browser/browser_shutdown.h"
[email protected]2c47bc12009-04-10 20:14:0020#include "chrome/browser/command_updater.h"
[email protected]09729a552009-08-03 23:21:4121#include "chrome/browser/download/download_manager.h"
[email protected]0934de82011-03-09 18:42:1622#include "chrome/browser/instant/instant_confirm_dialog.h"
[email protected]a99fce0e2011-03-21 20:58:4823#include "chrome/browser/prefs/pref_service.h"
[email protected]e7dd6d82010-10-07 23:21:1224#include "chrome/browser/printing/print_job_manager.h"
[email protected]8ecad5e2010-12-02 21:18:3325#include "chrome/browser/profiles/profile_manager.h"
[email protected]d2912a22011-03-15 15:20:5026#include "chrome/browser/sessions/session_service.h"
[email protected]92371eb2011-04-28 11:50:1527#include "chrome/browser/sessions/session_service_factory.h"
[email protected]fbc947b2009-06-19 13:28:2428#include "chrome/browser/sessions/tab_restore_service.h"
[email protected]92371eb2011-04-28 11:50:1529#include "chrome/browser/sessions/tab_restore_service_factory.h"
[email protected]f9bc9b92009-11-24 00:55:3530#include "chrome/browser/sync/profile_sync_service.h"
[email protected]40f047972009-11-25 03:54:4031#include "chrome/browser/sync/sync_ui_util.h"
32#include "chrome/browser/sync/sync_ui_util_mac.h"
[email protected]ae04f592010-11-18 20:41:3533#include "chrome/browser/ui/browser.h"
[email protected]f7002802010-11-12 19:50:2834#include "chrome/browser/ui/browser_init.h"
[email protected]ae04f592010-11-18 20:41:3535#include "chrome/browser/ui/browser_list.h"
36#include "chrome/browser/ui/browser_window.h"
[email protected]7d791652010-12-01 16:34:4937#import "chrome/browser/ui/cocoa/about_window_controller.h"
38#import "chrome/browser/ui/cocoa/bookmarks/bookmark_menu_bridge.h"
39#import "chrome/browser/ui/cocoa/browser_window_cocoa.h"
40#import "chrome/browser/ui/cocoa/browser_window_controller.h"
41#import "chrome/browser/ui/cocoa/bug_report_window_controller.h"
[email protected]7d791652010-12-01 16:34:4942#import "chrome/browser/ui/cocoa/confirm_quit_panel_controller.h"
43#import "chrome/browser/ui/cocoa/encoding_menu_controller_delegate_mac.h"
44#import "chrome/browser/ui/cocoa/history_menu_bridge.h"
[email protected]961a6932011-07-19 19:52:4645#import "chrome/browser/ui/cocoa/profile_menu_controller.h"
[email protected]cfe1d612011-01-19 20:06:4746#import "chrome/browser/ui/cocoa/tabs/tab_strip_controller.h"
47#import "chrome/browser/ui/cocoa/tabs/tab_window_controller.h"
[email protected]7d791652010-12-01 16:34:4948#include "chrome/browser/ui/cocoa/task_manager_mac.h"
[email protected]14fb7d32011-07-10 17:53:3249#include "chrome/common/chrome_notification_types.h"
[email protected]646b9b92010-04-30 21:51:5850#include "chrome/common/app_mode_common_mac.h"
[email protected]12dc3d42010-02-22 23:37:1251#include "chrome/common/chrome_paths_internal.h"
[email protected]f4acfae82009-09-11 00:33:3852#include "chrome/common/chrome_switches.h"
[email protected]a99fce0e2011-03-21 20:58:4853#include "chrome/common/pref_names.h"
[email protected]024617a2010-08-20 05:08:0554#include "chrome/common/url_constants.h"
[email protected]7324d1d02011-03-01 05:02:1655#include "content/browser/browser_thread.h"
56#include "content/browser/tab_contents/tab_contents.h"
[email protected]afd1e522011-04-27 23:29:5957#include "content/browser/user_metrics.h"
[email protected]432115822011-07-10 15:52:2758#include "content/common/content_notification_types.h"
[email protected]f20d7332011-03-08 21:11:5359#include "content/common/notification_service.h"
[email protected]9dcb59662009-09-23 01:15:5060#include "grit/chromium_strings.h"
[email protected]09729a552009-08-03 23:21:4161#include "grit/generated_resources.h"
[email protected]caadfca2010-01-22 21:51:2162#include "net/base/net_util.h"
[email protected]c051a1b2011-01-21 23:30:1763#include "ui/base/l10n/l10n_util.h"
64#include "ui/base/l10n/l10n_util_mac.h"
[email protected]ddd653f42011-03-02 15:14:3865#include "ui/base/models/accelerator_cocoa.h"
[email protected]88d74942009-01-21 22:04:4466
[email protected]f2c20fa2009-12-01 17:42:0267// 10.6 adds a public API for the Spotlight-backed search menu item in the Help
68// menu. Provide the declaration so it can be called below when building with
69// the 10.5 SDK.
70#if !defined(MAC_OS_X_VERSION_10_6) || \
71 MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6
72@interface NSApplication (SnowLeopardSDKDeclarations)
[email protected]caadfca2010-01-22 21:51:2173- (void)setHelpMenu:(NSMenu*)helpMenu;
[email protected]f2c20fa2009-12-01 17:42:0274@end
75#endif
76
[email protected]d630d7d52010-02-21 00:55:1177namespace {
78
79// True while AppController is calling Browser::OpenEmptyWindow(). We need a
80// global flag here, analogue to BrowserInit::InProcessStartup() because
81// otherwise the SessionService will try to restore sessions when we make a new
82// window while there are no other active windows.
83bool g_is_opening_new_window = false;
84
85// Activates a browser window having the given profile (the last one active) if
86// possible and returns a pointer to the activate |Browser| or NULL if this was
87// not possible. If the last active browser is minimized (in particular, if
88// there are only minimized windows), it will unminimize it.
89Browser* ActivateBrowser(Profile* profile) {
90 Browser* browser = BrowserList::GetLastActiveWithProfile(profile);
91 if (browser)
92 browser->window()->Activate();
93 return browser;
94}
95
96// Creates an empty browser window with the given profile and returns a pointer
97// to the new |Browser|.
98Browser* CreateBrowser(Profile* profile) {
99 {
[email protected]0fbd70332010-06-01 19:28:34100 AutoReset<bool> auto_reset_in_run(&g_is_opening_new_window, true);
[email protected]d630d7d52010-02-21 00:55:11101 Browser::OpenEmptyWindow(profile);
102 }
103
104 Browser* browser = BrowserList::GetLastActive();
105 CHECK(browser);
106 return browser;
107}
108
109// Activates a browser window having the given profile (the last one active) if
110// possible or creates an empty one if necessary. Returns a pointer to the
111// activated/new |Browser|.
112Browser* ActivateOrCreateBrowser(Profile* profile) {
113 if (Browser* browser = ActivateBrowser(profile))
114 return browser;
115 return CreateBrowser(profile);
116}
117
[email protected]151c4a62011-04-22 04:15:13118CFStringRef BaseBundleID_CFString() {
119 NSString* base_bundle_id =
120 [NSString stringWithUTF8String:base::mac::BaseBundleID()];
121 return base::mac::NSToCFCast(base_bundle_id);
122}
123
[email protected]12dc3d42010-02-22 23:37:12124// This task synchronizes preferences (under "org.chromium.Chromium" or
125// "com.google.Chrome"), in particular, writes them out to disk.
126class PrefsSyncTask : public Task {
127 public:
128 PrefsSyncTask() {}
129 virtual ~PrefsSyncTask() {}
130 virtual void Run() {
[email protected]151c4a62011-04-22 04:15:13131 if (!CFPreferencesAppSynchronize(BaseBundleID_CFString()))
[email protected]12dc3d42010-02-22 23:37:12132 LOG(WARNING) << "Error recording application bundle path.";
133 }
134};
135
136// Record the location of the application bundle (containing the main framework)
137// from which Chromium was loaded. This is used by app mode shims to find
138// Chromium.
139void RecordLastRunAppBundlePath() {
140 // Going up three levels from |chrome::GetVersionedDirectory()| gives the
141 // real, user-visible app bundle directory. (The alternatives give either the
142 // framework's path or the initial app's path, which may be an app mode shim
143 // or a unit test.)
144 FilePath appBundlePath =
145 chrome::GetVersionedDirectory().DirName().DirName().DirName();
146 CFPreferencesSetAppValue(app_mode::kLastRunAppBundlePathPrefsKey,
147 base::SysUTF8ToCFStringRef(appBundlePath.value()),
[email protected]151c4a62011-04-22 04:15:13148 BaseBundleID_CFString());
[email protected]12dc3d42010-02-22 23:37:12149
150 // Sync after a delay avoid I/O contention on startup; 1500 ms is plenty.
[email protected]d04e7662010-10-10 22:24:48151 BrowserThread::PostDelayedTask(BrowserThread::FILE, FROM_HERE,
152 new PrefsSyncTask(), 1500);
[email protected]12dc3d42010-02-22 23:37:12153}
154
[email protected]d630d7d52010-02-21 00:55:11155} // anonymous namespace
156
[email protected]a99fce0e2011-03-21 20:58:48157@interface AppController (Private)
[email protected]88d74942009-01-21 22:04:44158- (void)initMenuState;
[email protected]961a6932011-07-19 19:52:46159- (void)initProfileMenu;
[email protected]a99fce0e2011-03-21 20:58:48160- (void)updateConfirmToQuitPrefMenuItem:(NSMenuItem*)item;
[email protected]eef99c22010-08-17 05:55:16161- (void)registerServicesMenuTypesTo:(NSApplication*)app;
[email protected]d630d7d52010-02-21 00:55:11162- (void)openUrls:(const std::vector<GURL>&)urls;
[email protected]57750f822009-04-21 21:43:09163- (void)getUrl:(NSAppleEventDescriptor*)event
164 withReply:(NSAppleEventDescriptor*)reply;
[email protected]449dd2f2009-05-27 13:04:00165- (void)windowLayeringDidChange:(NSNotification*)inNotification;
[email protected]5c593702011-06-17 18:35:24166- (void)windowChangedToProfile:(Profile*)profile;
[email protected]dd6ab1282010-07-20 01:07:32167- (void)checkForAnyKeyWindows;
[email protected]73f5b362009-08-10 23:58:21168- (BOOL)userWillWaitForInProgressDownloads:(int)downloadCount;
[email protected]09729a552009-08-03 23:21:41169- (BOOL)shouldQuitWithInProgressDownloads;
[email protected]93b59fc2010-12-21 20:00:47170- (void)executeApplication:(id)sender;
[email protected]88d74942009-01-21 22:04:44171@end
[email protected]1bcdb532009-01-16 17:47:57172
173@implementation AppController
174
[email protected]caadfca2010-01-22 21:51:21175@synthesize startupComplete = startupComplete_;
176
[email protected]cd63ef62009-05-06 19:41:37177// This method is called very early in application startup (ie, before
178// the profile is loaded or any preferences have been registered). Defer any
179// user-data initialization until -applicationDidFinishLaunching:.
[email protected]88d74942009-01-21 22:04:44180- (void)awakeFromNib {
[email protected]136140c2009-05-19 13:58:25181 // We need to register the handlers early to catch events fired on launch.
[email protected]57750f822009-04-21 21:43:09182 NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager];
183 [em setEventHandler:self
184 andSelector:@selector(getUrl:withReply:)
185 forEventClass:kInternetEventClass
186 andEventID:kAEGetURL];
187 [em setEventHandler:self
188 andSelector:@selector(getUrl:withReply:)
189 forEventClass:'WWW!' // A particularly ancient AppleEvent that dates
190 andEventID:'OURL']; // back to the Spyglass days.
[email protected]cd63ef62009-05-06 19:41:37191
[email protected]449dd2f2009-05-27 13:04:00192 // Register for various window layering changes. We use these to update
193 // various UI elements (command-key equivalents, etc) when the frontmost
194 // window changes.
195 NSNotificationCenter* notificationCenter =
196 [NSNotificationCenter defaultCenter];
[email protected]ce560f82009-06-03 09:39:44197 [notificationCenter
[email protected]449dd2f2009-05-27 13:04:00198 addObserver:self
199 selector:@selector(windowLayeringDidChange:)
200 name:NSWindowDidBecomeKeyNotification
201 object:nil];
202 [notificationCenter
203 addObserver:self
204 selector:@selector(windowLayeringDidChange:)
205 name:NSWindowDidResignKeyNotification
206 object:nil];
207 [notificationCenter
208 addObserver:self
209 selector:@selector(windowLayeringDidChange:)
210 name:NSWindowDidBecomeMainNotification
211 object:nil];
212 [notificationCenter
213 addObserver:self
214 selector:@selector(windowLayeringDidChange:)
215 name:NSWindowDidResignMainNotification
216 object:nil];
217
[email protected]136140c2009-05-19 13:58:25218 // Set up the command updater for when there are no windows open
219 [self initMenuState];
[email protected]961a6932011-07-19 19:52:46220
221 // Initialize the Profile menu.
222 [self initProfileMenu];
[email protected]136140c2009-05-19 13:58:25223}
224
[email protected]7dc8c6b2010-04-09 17:02:50225// (NSApplicationDelegate protocol) This is the Apple-approved place to override
226// the default handlers.
227- (void)applicationWillFinishLaunching:(NSNotification*)notification {
[email protected]58877932010-05-25 05:43:11228 // Nothing here right now.
[email protected]7dc8c6b2010-04-09 17:02:50229}
230
231- (BOOL)tryToTerminateApplication:(NSApplication*)app {
[email protected]3076fad2010-04-28 18:11:45232 // Check for in-process downloads, and prompt the user if they really want
233 // to quit (and thus cancel downloads). Only check if we're not already
234 // shutting down, else the user might be prompted multiple times if the
235 // download isn't stopped before terminate is called again.
236 if (!browser_shutdown::IsTryingToQuit() &&
237 ![self shouldQuitWithInProgressDownloads])
238 return NO;
239
[email protected]7dc8c6b2010-04-09 17:02:50240 // TODO(viettrungluu): Remove Apple Event handlers here? (It's safe to leave
241 // them in, but I'm not sure about UX; we'd also want to disable other things
242 // though.) http://crbug.com/40861
243
[email protected]177aceb2010-11-03 16:17:41244 // Check if the user really wants to quit by employing the confirm-to-quit
245 // mechanism.
246 if (!browser_shutdown::IsTryingToQuit() &&
247 [self applicationShouldTerminate:app] != NSTerminateNow)
248 return NO;
249
[email protected]c984d9f2010-07-20 20:52:20250 size_t num_browsers = BrowserList::size();
[email protected]7dc8c6b2010-04-09 17:02:50251
[email protected]e7dd6d82010-10-07 23:21:12252 // Give any print jobs in progress time to finish.
253 if (!browser_shutdown::IsTryingToQuit())
254 g_browser_process->print_job_manager()->StopJobs(true);
255
[email protected]c984d9f2010-07-20 20:52:20256 // Initiate a shutdown (via BrowserList::CloseAllBrowsers()) if we aren't
257 // already shutting down.
258 if (!browser_shutdown::IsTryingToQuit())
[email protected]c6032e82010-09-13 20:06:05259 BrowserList::CloseAllBrowsers();
[email protected]7dc8c6b2010-04-09 17:02:50260
[email protected]c984d9f2010-07-20 20:52:20261 return num_browsers == 0 ? YES : NO;
[email protected]7dc8c6b2010-04-09 17:02:50262}
263
264- (void)stopTryingToTerminateApplication:(NSApplication*)app {
265 if (browser_shutdown::IsTryingToQuit()) {
266 // Reset the "trying to quit" state, so that closing all browser windows
267 // will no longer lead to termination.
268 browser_shutdown::SetTryingToQuit(false);
269
270 // TODO(viettrungluu): Were we to remove Apple Event handlers above, we
271 // would have to reinstall them here. http://crbug.com/40861
272 }
[email protected]a9e8afc2009-08-11 22:03:17273}
274
[email protected]177aceb2010-11-03 16:17:41275- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)app {
[email protected]a99fce0e2011-03-21 20:58:48276 // Check if the preference is turned on.
[email protected]a585a8db2011-06-20 18:58:35277 const PrefService* prefs = [self lastProfile]->GetPrefs();
[email protected]1eaddf32011-04-05 15:09:23278 if (!prefs->GetBoolean(prefs::kConfirmToQuitEnabled)) {
279 confirm_quit::RecordHistogram(confirm_quit::kNoConfirm);
[email protected]177aceb2010-11-03 16:17:41280 return NSTerminateNow;
[email protected]1eaddf32011-04-05 15:09:23281 }
[email protected]177aceb2010-11-03 16:17:41282
283 // If the application is going to terminate as the result of a Cmd+Q
284 // invocation, use the special sauce to prevent accidental quitting.
285 // http://dev.chromium.org/developers/design-documents/confirm-to-quit-experiment
[email protected]177aceb2010-11-03 16:17:41286
[email protected]2020fd0b2010-12-10 01:16:50287 // This logic is only for keyboard-initiated quits.
[email protected]5df54fb2011-03-07 18:15:26288 if (![ConfirmQuitPanelController eventTriggersFeature:[app currentEvent]])
[email protected]2020fd0b2010-12-10 01:16:50289 return NSTerminateNow;
[email protected]177aceb2010-11-03 16:17:41290
[email protected]5df54fb2011-03-07 18:15:26291 return [[ConfirmQuitPanelController sharedController]
292 runModalLoopForApplication:app];
[email protected]177aceb2010-11-03 16:17:41293}
294
[email protected]449dd2f2009-05-27 13:04:00295// Called when the app is shutting down. Clean-up as appropriate.
[email protected]7dc8c6b2010-04-09 17:02:50296- (void)applicationWillTerminate:(NSNotification*)aNotification {
[email protected]3b6aa8b62009-09-15 21:36:11297 NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager];
298 [em removeEventHandlerForEventClass:kInternetEventClass
299 andEventID:kAEGetURL];
300 [em removeEventHandlerForEventClass:'WWW!'
301 andEventID:'OURL'];
[email protected]3b6aa8b62009-09-15 21:36:11302
[email protected]7dc8c6b2010-04-09 17:02:50303 // There better be no browser windows left at this point.
304 CHECK_EQ(BrowserList::size(), 0u);
[email protected]3b6aa8b62009-09-15 21:36:11305
[email protected]c984d9f2010-07-20 20:52:20306 // Tell BrowserList not to keep the browser process alive. Once all the
307 // browsers get dealloc'd, it will stop the RunLoop and fall back into main().
308 BrowserList::EndKeepAlive();
[email protected]fbc947b2009-06-19 13:28:24309
[email protected]d4e18652009-11-19 21:52:16310 // Close these off if they have open windows.
[email protected]d4e18652009-11-19 21:52:16311 [aboutController_ close];
312
[email protected]449dd2f2009-05-27 13:04:00313 [[NSNotificationCenter defaultCenter] removeObserver:self];
314}
315
[email protected]3b6aa8b62009-09-15 21:36:11316- (void)didEndMainMessageLoop {
[email protected]a585a8db2011-06-20 18:58:35317 DCHECK(!BrowserList::HasBrowserWithProfile([self lastProfile]));
318 if (!BrowserList::HasBrowserWithProfile([self lastProfile])) {
[email protected]3b6aa8b62009-09-15 21:36:11319 // As we're shutting down, we need to nuke the TabRestoreService, which
320 // will start the shutdown of the NavigationControllers and allow for
321 // proper shutdown. If we don't do this, Chrome won't shut down cleanly,
322 // and may end up crashing when some thread tries to use the IO thread (or
323 // another thread) that is no longer valid.
[email protected]a585a8db2011-06-20 18:58:35324 TabRestoreServiceFactory::ResetForProfile([self lastProfile]);
[email protected]3b6aa8b62009-09-15 21:36:11325 }
326}
327
[email protected]449dd2f2009-05-27 13:04:00328// Helper routine to get the window controller if the key window is a tabbed
329// window, or nil if not. Examples of non-tabbed windows are "about" or
330// "preferences".
331- (TabWindowController*)keyWindowTabController {
332 NSWindowController* keyWindowController =
[email protected]5dbabd02009-09-17 20:44:13333 [[NSApp keyWindow] windowController];
[email protected]449dd2f2009-05-27 13:04:00334 if ([keyWindowController isKindOfClass:[TabWindowController class]])
335 return (TabWindowController*)keyWindowController;
336
337 return nil;
338}
339
[email protected]74c0c642009-09-22 21:25:50340// Helper routine to get the window controller if the main window is a tabbed
341// window, or nil if not. Examples of non-tabbed windows are "about" or
342// "preferences".
343- (TabWindowController*)mainWindowTabController {
344 NSWindowController* mainWindowController =
345 [[NSApp mainWindow] windowController];
346 if ([mainWindowController isKindOfClass:[TabWindowController class]])
347 return (TabWindowController*)mainWindowController;
348
349 return nil;
350}
351
[email protected]8cfbdbd2011-06-16 04:17:17352// If the window has a tab controller, make "close window" be cmd-shift-w,
353// otherwise leave it as the normal cmd-w. Capitalization of the key equivalent
354// affects whether the shift modifer is used.
355- (void)adjustCloseWindowMenuItemKeyEquivalent:(BOOL)hasTabs {
356 [closeWindowMenuItem_ setKeyEquivalent:(hasTabs ? @"W" : @"w")];
357 [closeWindowMenuItem_ setKeyEquivalentModifierMask:NSCommandKeyMask];
358}
359
360// If the window has a tab controller, make "close tab" take over cmd-w,
361// otherwise it shouldn't have any key-equivalent because it should be disabled.
362- (void)adjustCloseTabMenuItemKeyEquivalent:(BOOL)hasTabs {
363 if (hasTabs) {
364 [closeTabMenuItem_ setKeyEquivalent:@"w"];
365 [closeTabMenuItem_ setKeyEquivalentModifierMask:NSCommandKeyMask];
366 } else {
367 [closeTabMenuItem_ setKeyEquivalent:@""];
368 [closeTabMenuItem_ setKeyEquivalentModifierMask:0];
369 }
370}
371
372// Explicitly remove any command-key equivalents from the close tab/window
373// menus so that nothing can go haywire if we get a user action during pending
374// updates.
375- (void)clearCloseMenuItemKeyEquivalents {
376 [closeTabMenuItem_ setKeyEquivalent:@""];
377 [closeTabMenuItem_ setKeyEquivalentModifierMask:0];
378 [closeWindowMenuItem_ setKeyEquivalent:@""];
379 [closeWindowMenuItem_ setKeyEquivalentModifierMask:0];
380}
381
382// See if we have a window with tabs open, and adjust the key equivalents for
383// Close Tab/Close Window accordingly.
384- (void)fixCloseMenuItemKeyEquivalents:(NSWindow*)window {
385 fileMenuUpdatePending_ = NO;
386 TabWindowController* tabController = [self keyWindowTabController];
387 if (!tabController && ![NSApp keyWindow]) {
388 // There might be a small amount of time where there is no key window,
389 // so just use our main browser window if there is one.
390 tabController = [self mainWindowTabController];
391 }
392 BOOL hasTabs = !!tabController;
393
394 [self adjustCloseWindowMenuItemKeyEquivalent:hasTabs];
395 [self adjustCloseTabMenuItemKeyEquivalent:hasTabs];
396}
397
398// Fix up the "close tab/close window" command-key equivalents. We do this
399// after a delay to ensure that window layer state has been set by the time
400// we do the enabling. This should only be called on the main thread, code that
401// calls this (even as a side-effect) from other threads needs to be fixed.
402- (void)delayedFixCloseMenuItemKeyEquivalents:(NSNotification*)notify {
403 DCHECK([NSThread isMainThread]);
404 if (!fileMenuUpdatePending_) {
405 // The OS prefers keypresses to timers, so it's possible that a cmd-w
406 // can sneak in before this timer fires. In order to prevent that from
407 // having any bad consequences, just clear the keys combos altogether. They
408 // will be reset when the timer eventually fires.
409 if ([NSThread isMainThread]) {
410 fileMenuUpdatePending_ = YES;
411 [self clearCloseMenuItemKeyEquivalents];
412 [self performSelector:@selector(fixCloseMenuItemKeyEquivalents:)
413 withObject:[notify object]
414 afterDelay:0];
415 } else {
416 // This shouldn't be happening, but if it does, force it to the main
417 // thread to avoid dropping the update. Don't mess with
418 // |fileMenuUpdatePending_| as it's not expected to be threadsafe and
419 // there could be a race between the selector finishing and setting the
420 // flag.
421 [self
422 performSelectorOnMainThread:@selector(fixCloseMenuItemKeyEquivalents:)
423 withObject:[notify object]
424 waitUntilDone:NO];
425 }
426 }
427}
428
[email protected]449dd2f2009-05-27 13:04:00429// Called when we get a notification about the window layering changing to
430// update the UI based on the new main window.
431- (void)windowLayeringDidChange:(NSNotification*)notify {
[email protected]8cfbdbd2011-06-16 04:17:17432 [self delayedFixCloseMenuItemKeyEquivalents:notify];
433
[email protected]dd6ab1282010-07-20 01:07:32434 if ([notify name] == NSWindowDidResignKeyNotification) {
435 // If a window is closed, this notification is fired but |[NSApp keyWindow]|
436 // returns nil regardless of whether any suitable candidates for the key
437 // window remain. It seems that the new key window for the app is not set
438 // until after this notification is fired, so a check is performed after the
439 // run loop is allowed to spin.
440 [self performSelector:@selector(checkForAnyKeyWindows)
441 withObject:nil
442 afterDelay:0.0];
443 }
[email protected]5c593702011-06-17 18:35:24444
445 // If the window changed to a new BrowserWindowController, update the profile.
446 id windowController = [[notify object] windowController];
447 if ([windowController isKindOfClass:[BrowserWindowController class]]) {
448 // If the profile is incognito, use the original profile.
449 Profile* newProfile = [windowController profile]->GetOriginalProfile();
450 [self windowChangedToProfile:newProfile];
451 }
452}
453
454// Called when the user has changed browser windows, meaning the backing profile
455// may have changed. This can cause a rebuild of the user-data menus. This is a
456// no-op if the new profile is the same as the current one. This will always be
457// the original profile and never incognito.
458- (void)windowChangedToProfile:(Profile*)profile {
459 if (lastProfile_ == profile)
460 return;
461
462 // Before tearing down the menu controller bridges, return the Cocoa menus to
463 // their initial state.
464 if (bookmarkMenuBridge_.get())
465 bookmarkMenuBridge_->ResetMenu();
466 if (historyMenuBridge_.get())
467 historyMenuBridge_->ResetMenu();
468
469 // Rebuild the menus with the new profile.
470 lastProfile_ = profile;
471
[email protected]0c165cf2011-07-09 03:56:50472 bookmarkMenuBridge_.reset(new BookmarkMenuBridge(lastProfile_,
473 [[[NSApp mainMenu] itemWithTag:IDC_BOOKMARKS_MENU] submenu]));
[email protected]5c593702011-06-17 18:35:24474 bookmarkMenuBridge_->BuildMenu();
475
476 historyMenuBridge_.reset(new HistoryMenuBridge(lastProfile_));
477 historyMenuBridge_->BuildMenu();
[email protected]dd6ab1282010-07-20 01:07:32478}
479
480- (void)checkForAnyKeyWindows {
481 if ([NSApp keyWindow])
482 return;
483
484 NotificationService::current()->Notify(
[email protected]666641df2011-07-10 18:24:38485 content::NOTIFICATION_NO_KEY_WINDOW,
[email protected]dd6ab1282010-07-20 01:07:32486 NotificationService::AllSources(),
487 NotificationService::NoDetails());
[email protected]449dd2f2009-05-27 13:04:00488}
489
[email protected]414fde592009-05-21 16:14:43490// If the auto-update interval is not set, make it 5 hours.
491// This code is specific to Mac Chrome Dev Channel.
492// Placed here for 2 reasons:
493// 1) Same spot as other Pref stuff
494// 2) Try and be friendly by keeping this after app launch
495// TODO(jrg): remove once we go Beta.
496- (void)setUpdateCheckInterval {
497#if defined(GOOGLE_CHROME_BUILD)
498 CFStringRef app = (CFStringRef)@"com.google.Keystone.Agent";
499 CFStringRef checkInterval = (CFStringRef)@"checkInterval";
500 CFPropertyListRef plist = CFPreferencesCopyAppValue(checkInterval, app);
501 if (!plist) {
502 const float fiveHoursInSeconds = 5.0 * 60.0 * 60.0;
[email protected]7dc8c6b2010-04-09 17:02:50503 NSNumber* value = [NSNumber numberWithFloat:fiveHoursInSeconds];
[email protected]414fde592009-05-21 16:14:43504 CFPreferencesSetAppValue(checkInterval, value, app);
505 CFPreferencesAppSynchronize(app);
506 }
507#endif
508}
509
[email protected]136140c2009-05-19 13:58:25510// This is called after profiles have been loaded and preferences registered.
511// It is safe to access the default profile here.
512- (void)applicationDidFinishLaunching:(NSNotification*)notify {
[email protected]c984d9f2010-07-20 20:52:20513 // Notify BrowserList to keep the application running so it doesn't go away
514 // when all the browser windows get closed.
515 BrowserList::StartKeepAlive();
[email protected]136140c2009-05-19 13:58:25516
[email protected]414fde592009-05-21 16:14:43517 [self setUpdateCheckInterval];
[email protected]2bcec612009-05-14 17:50:53518
519 // Build up the encoding menu, the order of the items differs based on the
520 // current locale (see http://crbug.com/7647 for details).
521 // We need a valid g_browser_process to get the profile which is why we can't
522 // call this from awakeFromNib.
[email protected]6f91e5ae2011-03-21 19:53:14523 NSMenu* viewMenu = [[[NSApp mainMenu] itemWithTag:IDC_VIEW_MENU] submenu];
524 NSMenuItem* encodingMenuItem = [viewMenu itemWithTag:IDC_ENCODING_MENU];
525 NSMenu* encodingMenu = [encodingMenuItem submenu];
[email protected]a585a8db2011-06-20 18:58:35526 EncodingMenuControllerDelegate::BuildEncodingMenu([self lastProfile],
[email protected]6f91e5ae2011-03-21 19:53:14527 encodingMenu);
[email protected]2bcec612009-05-14 17:50:53528
[email protected]f2c20fa2009-12-01 17:42:02529 // Since Chrome is localized to more languages than the OS, tell Cocoa which
530 // menu is the Help so it can add the search item to it.
[email protected]caadfca2010-01-22 21:51:21531 if (helpMenu_ && [NSApp respondsToSelector:@selector(setHelpMenu:)])
[email protected]f2c20fa2009-12-01 17:42:02532 [NSApp setHelpMenu:helpMenu_];
[email protected]f2c20fa2009-12-01 17:42:02533
[email protected]12dc3d42010-02-22 23:37:12534 // Record the path to the (browser) app bundle; this is used by the app mode
535 // shim.
536 RecordLastRunAppBundlePath();
537
[email protected]eef99c22010-08-17 05:55:16538 // Makes "Services" menu items available.
539 [self registerServicesMenuTypesTo:[notify object]];
540
[email protected]caadfca2010-01-22 21:51:21541 startupComplete_ = YES;
542
543 // TODO(viettrungluu): This is very temporary, since this should be done "in"
544 // |BrowserMain()|, i.e., this list of startup URLs should be appended to the
545 // (probably-empty) list of URLs from the command line.
[email protected]d630d7d52010-02-21 00:55:11546 if (startupUrls_.size()) {
547 [self openUrls:startupUrls_];
548 [self clearStartupUrls];
[email protected]caadfca2010-01-22 21:51:21549 }
[email protected]aac169d2011-03-18 19:53:03550
551 const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess();
552 if (!parsed_command_line.HasSwitch(switches::kEnableExposeForTabs)) {
553 [tabposeMenuItem_ setHidden:YES];
554 }
[email protected]7c321082009-02-09 15:35:47555}
556
[email protected]a96ec6a2009-11-04 17:27:08557// This is called after profiles have been loaded and preferences registered.
558// It is safe to access the default profile here.
559- (void)applicationDidBecomeActive:(NSNotification*)notify {
[email protected]14fb7d32011-07-10 17:53:32560 NotificationService::current()->Notify(content::NOTIFICATION_APP_ACTIVATED,
[email protected]a96ec6a2009-11-04 17:27:08561 NotificationService::AllSources(),
562 NotificationService::NoDetails());
563}
564
[email protected]73f5b362009-08-10 23:58:21565// Helper function for populating and displaying the in progress downloads at
566// exit alert panel.
567- (BOOL)userWillWaitForInProgressDownloads:(int)downloadCount {
[email protected]e8b5f7882009-09-14 20:59:13568 NSString* warningText = nil;
569 NSString* explanationText = nil;
[email protected]73f5b362009-08-10 23:58:21570 NSString* waitTitle = nil;
571 NSString* exitTitle = nil;
572
[email protected]757127052010-04-27 14:42:26573 string16 product_name = l10n_util::GetStringUTF16(IDS_PRODUCT_NAME);
[email protected]9dcb59662009-09-23 01:15:50574
[email protected]73f5b362009-08-10 23:58:21575 // Set the dialog text based on whether or not there are multiple downloads.
576 if (downloadCount == 1) {
[email protected]e8b5f7882009-09-14 20:59:13577 // Dialog text: warning and explanation.
[email protected]757127052010-04-27 14:42:26578 warningText = l10n_util::GetNSStringF(
579 IDS_SINGLE_DOWNLOAD_REMOVE_CONFIRM_WARNING, product_name);
580 explanationText = l10n_util::GetNSStringF(
581 IDS_SINGLE_DOWNLOAD_REMOVE_CONFIRM_EXPLANATION, product_name);
[email protected]73f5b362009-08-10 23:58:21582
583 // Cancel download and exit button text.
[email protected]757127052010-04-27 14:42:26584 exitTitle = l10n_util::GetNSString(
585 IDS_SINGLE_DOWNLOAD_REMOVE_CONFIRM_OK_BUTTON_LABEL);
[email protected]73f5b362009-08-10 23:58:21586
587 // Wait for download button text.
[email protected]757127052010-04-27 14:42:26588 waitTitle = l10n_util::GetNSString(
589 IDS_SINGLE_DOWNLOAD_REMOVE_CONFIRM_CANCEL_BUTTON_LABEL);
[email protected]73f5b362009-08-10 23:58:21590 } else {
[email protected]e8b5f7882009-09-14 20:59:13591 // Dialog text: warning and explanation.
[email protected]757127052010-04-27 14:42:26592 warningText = l10n_util::GetNSStringF(
593 IDS_MULTIPLE_DOWNLOADS_REMOVE_CONFIRM_WARNING, product_name,
[email protected]528c56d2010-07-30 19:28:44594 base::IntToString16(downloadCount));
[email protected]757127052010-04-27 14:42:26595 explanationText = l10n_util::GetNSStringF(
596 IDS_MULTIPLE_DOWNLOADS_REMOVE_CONFIRM_EXPLANATION, product_name);
[email protected]73f5b362009-08-10 23:58:21597
598 // Cancel downloads and exit button text.
[email protected]757127052010-04-27 14:42:26599 exitTitle = l10n_util::GetNSString(
600 IDS_MULTIPLE_DOWNLOADS_REMOVE_CONFIRM_OK_BUTTON_LABEL);
[email protected]73f5b362009-08-10 23:58:21601
602 // Wait for downloads button text.
[email protected]757127052010-04-27 14:42:26603 waitTitle = l10n_util::GetNSString(
604 IDS_MULTIPLE_DOWNLOADS_REMOVE_CONFIRM_CANCEL_BUTTON_LABEL);
[email protected]73f5b362009-08-10 23:58:21605 }
606
607 // 'waitButton' is the default choice.
[email protected]e8b5f7882009-09-14 20:59:13608 int choice = NSRunAlertPanel(warningText, explanationText,
609 waitTitle, exitTitle, nil);
[email protected]73f5b362009-08-10 23:58:21610 return choice == NSAlertDefaultReturn ? YES : NO;
611}
612
613// Check all profiles for in progress downloads, and if we find any, prompt the
[email protected]09729a552009-08-03 23:21:41614// user to see if we should continue to exit (and thus cancel the downloads), or
615// if we should wait.
616- (BOOL)shouldQuitWithInProgressDownloads {
[email protected]73f5b362009-08-10 23:58:21617 ProfileManager* profile_manager = g_browser_process->profile_manager();
618 if (!profile_manager)
619 return YES;
[email protected]09729a552009-08-03 23:21:41620
[email protected]844a1002011-04-19 11:37:21621 std::vector<Profile*> profiles(profile_manager->GetLoadedProfiles());
622 for (size_t i = 0; i < profiles.size(); ++i) {
623 DownloadManager* download_manager = profiles[i]->GetDownloadManager();
[email protected]73f5b362009-08-10 23:58:21624 if (download_manager && download_manager->in_progress_count() > 0) {
625 int downloadCount = download_manager->in_progress_count();
626 if ([self userWillWaitForInProgressDownloads:downloadCount]) {
627 // Create a new browser window (if necessary) and navigate to the
628 // downloads page if the user chooses to wait.
[email protected]844a1002011-04-19 11:37:21629 Browser* browser = BrowserList::FindBrowserWithProfile(profiles[i]);
[email protected]73f5b362009-08-10 23:58:21630 if (!browser) {
[email protected]844a1002011-04-19 11:37:21631 browser = Browser::Create(profiles[i]);
[email protected]73f5b362009-08-10 23:58:21632 browser->window()->Show();
633 }
634 DCHECK(browser);
635 browser->ShowDownloadsTab();
636 return NO;
637 }
[email protected]09729a552009-08-03 23:21:41638
[email protected]73f5b362009-08-10 23:58:21639 // User wants to exit.
640 return YES;
[email protected]09729a552009-08-03 23:21:41641 }
[email protected]09729a552009-08-03 23:21:41642 }
643
[email protected]73f5b362009-08-10 23:58:21644 // No profiles or active downloads found, okay to exit.
[email protected]09729a552009-08-03 23:21:41645 return YES;
646}
647
[email protected]fbc947b2009-06-19 13:28:24648// Called to determine if we should enable the "restore tab" menu item.
649// Checks with the TabRestoreService to see if there's anything there to
650// restore and returns YES if so.
651- (BOOL)canRestoreTab {
[email protected]92371eb2011-04-28 11:50:15652 TabRestoreService* service =
[email protected]a585a8db2011-06-20 18:58:35653 TabRestoreServiceFactory::GetForProfile([self lastProfile]);
[email protected]fbc947b2009-06-19 13:28:24654 return service && !service->entries().empty();
655}
656
[email protected]f6c15fa2010-06-04 20:08:21657// Returns true if there is not a modal window (either window- or application-
658// modal) blocking the active browser. Note that tab modal dialogs (HTTP auth
659// sheets) will not count as blocking the browser. But things like open/save
660// dialogs that are window modal will block the browser.
661- (BOOL)keyWindowIsNotModal {
[email protected]6a281332009-10-13 17:41:06662 Browser* browser = BrowserList::GetLastActive();
[email protected]dc76e8362010-06-11 16:29:52663 return [NSApp modalWindow] == nil && (!browser ||
[email protected]6a281332009-10-13 17:41:06664 ![[browser->window()->GetNativeHandle() attachedSheet]
[email protected]f6c15fa2010-06-04 20:08:21665 isKindOfClass:[NSWindow class]]);
[email protected]6a281332009-10-13 17:41:06666}
667
[email protected]88d74942009-01-21 22:04:44668// Called to validate menu items when there are no key windows. All the
669// items we care about have been set with the |commandDispatch:| action and
670// a target of FirstResponder in IB. If it's not one of those, let it
671// continue up the responder chain to be handled elsewhere. We pull out the
672// tag as the cross-platform constant to differentiate and dispatch the
673// various commands.
674- (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item {
675 SEL action = [item action];
676 BOOL enable = NO;
677 if (action == @selector(commandDispatch:)) {
678 NSInteger tag = [item tag];
[email protected]fbc947b2009-06-19 13:28:24679 if (menuState_->SupportsCommand(tag)) {
680 switch (tag) {
[email protected]419eb2a2009-11-11 16:26:26681 // The File Menu commands are not automatically disabled by Cocoa when a
682 // dialog sheet obscures the browser window, so we disable several of
683 // them here. We don't need to include IDC_CLOSE_WINDOW, because
684 // app_controller is only activated when there are no key windows (see
685 // function comment).
[email protected]fbc947b2009-06-19 13:28:24686 case IDC_RESTORE_TAB:
[email protected]f6c15fa2010-06-04 20:08:21687 enable = [self keyWindowIsNotModal] && [self canRestoreTab];
[email protected]6a281332009-10-13 17:41:06688 break;
[email protected]f6c15fa2010-06-04 20:08:21689 // Browser-level items that open in new tabs should not open if there's
690 // a window- or app-modal dialog.
[email protected]6a281332009-10-13 17:41:06691 case IDC_OPEN_FILE:
[email protected]6a281332009-10-13 17:41:06692 case IDC_NEW_TAB:
[email protected]f6c15fa2010-06-04 20:08:21693 case IDC_SHOW_HISTORY:
694 case IDC_SHOW_BOOKMARK_MANAGER:
695 enable = [self keyWindowIsNotModal];
696 break;
697 // Browser-level items that open in new windows.
698 case IDC_NEW_WINDOW:
699 case IDC_TASK_MANAGER:
700 // Allow the user to open a new window if there's a window-modal
701 // dialog.
702 enable = [self keyWindowIsNotModal] || ([NSApp modalWindow] == nil);
[email protected]fbc947b2009-06-19 13:28:24703 break;
[email protected]446e16a2010-05-03 21:23:58704 case IDC_SYNC_BOOKMARKS: {
[email protected]a585a8db2011-06-20 18:58:35705 Profile* lastProfile = [self lastProfile];
[email protected]446e16a2010-05-03 21:23:58706 // The profile may be NULL during shutdown -- see
707 // http://code.google.com/p/chromium/issues/detail?id=43048 .
708 //
709 // TODO(akalin,viettrungluu): Figure out whether this method
[email protected]a585a8db2011-06-20 18:58:35710 // can be prevented from being called if lastProfile is
[email protected]446e16a2010-05-03 21:23:58711 // NULL.
[email protected]a585a8db2011-06-20 18:58:35712 if (!lastProfile) {
[email protected]446e16a2010-05-03 21:23:58713 LOG(WARNING)
[email protected]a585a8db2011-06-20 18:58:35714 << "NULL lastProfile detected -- not doing anything";
[email protected]446e16a2010-05-03 21:23:58715 break;
716 }
[email protected]a585a8db2011-06-20 18:58:35717 enable = lastProfile->IsSyncAccessible() &&
[email protected]d3b98c82010-07-14 07:45:59718 [self keyWindowIsNotModal];
[email protected]a585a8db2011-06-20 18:58:35719 sync_ui_util::UpdateSyncItem(item, enable, lastProfile);
[email protected]f9bc9b92009-11-24 00:55:35720 break;
[email protected]446e16a2010-05-03 21:23:58721 }
[email protected]fbc947b2009-06-19 13:28:24722 default:
[email protected]f6c15fa2010-06-04 20:08:21723 enable = menuState_->IsCommandEnabled(tag) ?
724 [self keyWindowIsNotModal] : NO;
[email protected]fbc947b2009-06-19 13:28:24725 }
726 }
[email protected]3b6aa8b62009-09-15 21:36:11727 } else if (action == @selector(terminate:)) {
[email protected]88d74942009-01-21 22:04:44728 enable = YES;
[email protected]3111f08b2009-04-30 16:01:52729 } else if (action == @selector(showPreferences:)) {
730 enable = YES;
[email protected]bde3dda2009-05-20 22:13:07731 } else if (action == @selector(orderFrontStandardAboutPanel:)) {
732 enable = YES;
[email protected]93b59fc2010-12-21 20:00:47733 } else if (action == @selector(commandFromDock:)) {
[email protected]3d4bd23f2009-10-06 03:58:38734 enable = YES;
[email protected]a99fce0e2011-03-21 20:58:48735 } else if (action == @selector(toggleConfirmToQuit:)) {
736 [self updateConfirmToQuitPrefMenuItem:static_cast<NSMenuItem*>(item)];
737 enable = YES;
[email protected]88d74942009-01-21 22:04:44738 }
739 return enable;
740}
741
[email protected]947fc0d2010-01-14 22:33:05742// Called when the user picks a menu item when there are no key windows, or when
743// there is no foreground browser window. Calls through to the browser object to
744// execute the command. This assumes that the command is supported and doesn't
[email protected]d630d7d52010-02-21 00:55:11745// check, otherwise it should have been disabled in the UI in
746// |-validateUserInterfaceItem:|.
[email protected]88d74942009-01-21 22:04:44747- (void)commandDispatch:(id)sender {
[email protected]a585a8db2011-06-20 18:58:35748 Profile* lastProfile = [self lastProfile];
[email protected]f0a51fb52009-03-05 12:46:38749
[email protected]947fc0d2010-01-14 22:33:05750 // Handle the case where we're dispatching a command from a sender that's in a
751 // browser window. This means that the command came from a background window
752 // and is getting here because the foreground window is not a browser window.
753 if ([sender respondsToSelector:@selector(window)]) {
754 id delegate = [[sender window] windowController];
755 if ([delegate isKindOfClass:[BrowserWindowController class]]) {
756 [delegate commandDispatch:sender];
757 return;
758 }
759 }
760
[email protected]88d74942009-01-21 22:04:44761 NSInteger tag = [sender tag];
762 switch (tag) {
[email protected]fbc947b2009-06-19 13:28:24763 case IDC_NEW_TAB:
[email protected]d630d7d52010-02-21 00:55:11764 // Create a new tab in an existing browser window (which we activate) if
765 // possible.
[email protected]a585a8db2011-06-20 18:58:35766 if (Browser* browser = ActivateBrowser(lastProfile)) {
[email protected]d630d7d52010-02-21 00:55:11767 browser->ExecuteCommand(IDC_NEW_TAB);
768 break;
769 }
770 // Else fall through to create new window.
[email protected]88d74942009-01-21 22:04:44771 case IDC_NEW_WINDOW:
[email protected]a585a8db2011-06-20 18:58:35772 CreateBrowser(lastProfile);
[email protected]d630d7d52010-02-21 00:55:11773 break;
[email protected]91a5b3e2009-10-30 19:32:22774 case IDC_FOCUS_LOCATION:
[email protected]a585a8db2011-06-20 18:58:35775 ActivateOrCreateBrowser(lastProfile)->ExecuteCommand(IDC_FOCUS_LOCATION);
[email protected]88d74942009-01-21 22:04:44776 break;
[email protected]cddc5242009-12-07 18:54:33777 case IDC_FOCUS_SEARCH:
[email protected]a585a8db2011-06-20 18:58:35778 ActivateOrCreateBrowser(lastProfile)->ExecuteCommand(IDC_FOCUS_SEARCH);
[email protected]cddc5242009-12-07 18:54:33779 break;
[email protected]863ff662009-01-26 20:18:18780 case IDC_NEW_INCOGNITO_WINDOW:
[email protected]a585a8db2011-06-20 18:58:35781 Browser::OpenEmptyWindow(lastProfile->GetOffTheRecordProfile());
[email protected]863ff662009-01-26 20:18:18782 break;
[email protected]fbc947b2009-06-19 13:28:24783 case IDC_RESTORE_TAB:
[email protected]a585a8db2011-06-20 18:58:35784 Browser::OpenWindowWithRestoredTabs(lastProfile);
[email protected]fbc947b2009-06-19 13:28:24785 break;
[email protected]e19516d2009-04-28 17:15:19786 case IDC_OPEN_FILE:
[email protected]a585a8db2011-06-20 18:58:35787 CreateBrowser(lastProfile)->ExecuteCommand(IDC_OPEN_FILE);
[email protected]e19516d2009-04-28 17:15:19788 break;
[email protected]1fdff702009-10-22 00:36:18789 case IDC_CLEAR_BROWSING_DATA: {
[email protected]93e181762009-05-29 14:30:38790 // There may not be a browser open, so use the default profile.
[email protected]a585a8db2011-06-20 18:58:35791 if (Browser* browser = ActivateBrowser(lastProfile)) {
[email protected]445177652011-03-09 02:04:05792 browser->OpenClearBrowsingDataDialog();
[email protected]c8de64a2011-01-25 17:10:23793 } else {
[email protected]ddddfda2011-07-14 23:19:39794 Browser::OpenClearBrowsingDataDialogWindow(lastProfile);
[email protected]c8de64a2011-01-25 17:10:23795 }
[email protected]93e181762009-05-29 14:30:38796 break;
[email protected]1fdff702009-10-22 00:36:18797 }
[email protected]4aee5652009-11-19 18:59:21798 case IDC_IMPORT_SETTINGS: {
[email protected]a585a8db2011-06-20 18:58:35799 if (Browser* browser = ActivateBrowser(lastProfile)) {
[email protected]445177652011-03-09 02:04:05800 browser->OpenImportSettingsDialog();
[email protected]c8de64a2011-01-25 17:10:23801 } else {
[email protected]a585a8db2011-06-20 18:58:35802 Browser::OpenImportSettingsDialogWindow(lastProfile);
[email protected]c8de64a2011-01-25 17:10:23803 }
[email protected]4aee5652009-11-19 18:59:21804 break;
805 }
[email protected]af7c25e2010-01-23 05:46:45806 case IDC_SHOW_BOOKMARK_MANAGER:
[email protected]afd1e522011-04-27 23:29:59807 UserMetrics::RecordAction(UserMetricsAction("ShowBookmarkManager"));
[email protected]a585a8db2011-06-20 18:58:35808 if (Browser* browser = ActivateBrowser(lastProfile)) {
[email protected]47e532b2010-04-23 17:46:40809 // Open a bookmark manager tab.
[email protected]90a8bf252010-03-12 00:09:47810 browser->OpenBookmarkManager();
811 } else {
[email protected]47e532b2010-04-23 17:46:40812 // No browser window, so create one for the bookmark manager tab.
[email protected]a585a8db2011-06-20 18:58:35813 Browser::OpenBookmarkManagerWindow(lastProfile);
[email protected]90a8bf252010-03-12 00:09:47814 }
[email protected]af7c25e2010-01-23 05:46:45815 break;
[email protected]91a5b3e2009-10-30 19:32:22816 case IDC_SHOW_HISTORY:
[email protected]a585a8db2011-06-20 18:58:35817 if (Browser* browser = ActivateBrowser(lastProfile))
[email protected]d630d7d52010-02-21 00:55:11818 browser->ShowHistoryTab();
819 else
[email protected]a585a8db2011-06-20 18:58:35820 Browser::OpenHistoryWindow(lastProfile);
[email protected]91a5b3e2009-10-30 19:32:22821 break;
822 case IDC_SHOW_DOWNLOADS:
[email protected]a585a8db2011-06-20 18:58:35823 if (Browser* browser = ActivateBrowser(lastProfile))
[email protected]d630d7d52010-02-21 00:55:11824 browser->ShowDownloadsTab();
825 else
[email protected]a585a8db2011-06-20 18:58:35826 Browser::OpenDownloadsWindow(lastProfile);
[email protected]91a5b3e2009-10-30 19:32:22827 break;
[email protected]bb37e6ec2009-11-18 17:59:36828 case IDC_MANAGE_EXTENSIONS:
[email protected]a585a8db2011-06-20 18:58:35829 if (Browser* browser = ActivateBrowser(lastProfile))
[email protected]d630d7d52010-02-21 00:55:11830 browser->ShowExtensionsTab();
831 else
[email protected]a585a8db2011-06-20 18:58:35832 Browser::OpenExtensionsWindow(lastProfile);
[email protected]bb37e6ec2009-11-18 17:59:36833 break;
[email protected]1fdff702009-10-22 00:36:18834 case IDC_HELP_PAGE:
[email protected]a585a8db2011-06-20 18:58:35835 if (Browser* browser = ActivateBrowser(lastProfile))
[email protected]202802e42011-07-06 11:40:46836 browser->ShowHelpTab();
[email protected]d630d7d52010-02-21 00:55:11837 else
[email protected]a585a8db2011-06-20 18:58:35838 Browser::OpenHelpWindow(lastProfile);
[email protected]1fdff702009-10-22 00:36:18839 break;
[email protected]1c41a932010-11-22 23:41:23840 case IDC_FEEDBACK: {
[email protected]3896b182009-11-03 16:15:32841 Browser* browser = BrowserList::GetLastActive();
[email protected]d630d7d52010-02-21 00:55:11842 TabContents* currentTab =
843 browser ? browser->GetSelectedTabContents() : NULL;
[email protected]3896b182009-11-03 16:15:32844 BugReportWindowController* controller =
845 [[BugReportWindowController alloc]
[email protected]d630d7d52010-02-21 00:55:11846 initWithTabContents:currentTab
[email protected]a585a8db2011-06-20 18:58:35847 profile:[self lastProfile]];
[email protected]3896b182009-11-03 16:15:32848 [controller runModalDialog];
849 break;
850 }
[email protected]f9bc9b92009-11-24 00:55:35851 case IDC_SYNC_BOOKMARKS:
[email protected]446e16a2010-05-03 21:23:58852 // The profile may be NULL during shutdown -- see
853 // http://code.google.com/p/chromium/issues/detail?id=43048 .
854 //
855 // TODO(akalin,viettrungluu): Figure out whether this method can
[email protected]a585a8db2011-06-20 18:58:35856 // be prevented from being called if lastProfile is NULL.
857 if (!lastProfile) {
858 LOG(WARNING) << "NULL lastProfile detected -- not doing anything";
[email protected]446e16a2010-05-03 21:23:58859 break;
860 }
[email protected]f9bc9b92009-11-24 00:55:35861 // TODO(akalin): Add a constant to denote starting sync from the
862 // main menu and use that instead of START_FROM_WRENCH.
[email protected]40f047972009-11-25 03:54:40863 sync_ui_util::OpenSyncMyBookmarksDialog(
[email protected]a585a8db2011-06-20 18:58:35864 lastProfile, ActivateBrowser(lastProfile),
[email protected]c8de64a2011-01-25 17:10:23865 ProfileSyncService::START_FROM_WRENCH);
[email protected]f9bc9b92009-11-24 00:55:35866 break;
[email protected]ceaaf1812010-01-20 22:13:09867 case IDC_TASK_MANAGER:
[email protected]afd1e522011-04-27 23:29:59868 UserMetrics::RecordAction(UserMetricsAction("TaskManager"));
[email protected]adb6a84d2011-02-07 16:58:40869 TaskManagerMac::Show(false);
[email protected]ceaaf1812010-01-20 22:13:09870 break;
[email protected]4a42d272010-06-18 01:29:42871 case IDC_OPTIONS:
872 [self showPreferences:sender];
873 break;
[email protected]93b59fc2010-12-21 20:00:47874 default:
875 // Background Applications use dynamic values that must be less than the
876 // smallest value among the predefined IDC_* labels.
877 if ([sender tag] < IDC_MinimumLabelValue)
878 [self executeApplication:sender];
879 break;
[email protected]d630d7d52010-02-21 00:55:11880 }
[email protected]88d74942009-01-21 22:04:44881}
882
[email protected]93b59fc2010-12-21 20:00:47883// Run a (background) application in a new tab.
884- (void)executeApplication:(id)sender {
885 NSInteger tag = [sender tag];
[email protected]a585a8db2011-06-20 18:58:35886 Profile* profile = [self lastProfile];
[email protected]93b59fc2010-12-21 20:00:47887 DCHECK(profile);
888 BackgroundApplicationListModel applications(profile);
889 DCHECK(tag >= 0 &&
890 tag < static_cast<int>(applications.size()));
891 Browser* browser = BrowserList::GetLastActive();
892 if (!browser) {
893 Browser::OpenEmptyWindow(profile);
894 browser = BrowserList::GetLastActive();
895 }
896 const Extension* extension = applications.GetExtension(tag);
[email protected]7e41c2b2011-05-06 10:31:16897 browser->OpenApplicationTab(profile, extension, NEW_FOREGROUND_TAB);
[email protected]93b59fc2010-12-21 20:00:47898}
899
[email protected]947fc0d2010-01-14 22:33:05900// Same as |-commandDispatch:|, but executes commands using a disposition
901// determined by the key flags. This will get called in the case where the
902// frontmost window is not a browser window, and the user has command-clicked
903// a button in a background browser window whose action is
904// |-commandDispatchUsingKeyModifiers:|
905- (void)commandDispatchUsingKeyModifiers:(id)sender {
906 DCHECK(sender);
907 if ([sender respondsToSelector:@selector(window)]) {
908 id delegate = [[sender window] windowController];
909 if ([delegate isKindOfClass:[BrowserWindowController class]]) {
910 [delegate commandDispatchUsingKeyModifiers:sender];
911 }
912 }
913}
914
[email protected]ff81e0a2009-04-13 14:58:54915// NSApplication delegate method called when someone clicks on the
916// dock icon and there are no open windows. To match standard mac
917// behavior, we should open a new window.
918- (BOOL)applicationShouldHandleReopen:(NSApplication*)theApplication
919 hasVisibleWindows:(BOOL)flag {
[email protected]70631402010-04-26 20:16:54920 // If the browser is currently trying to quit, don't do anything and return NO
921 // to prevent AppKit from doing anything.
922 // TODO(rohitrao): Remove this code when http://crbug.com/40861 is resolved.
923 if (browser_shutdown::IsTryingToQuit())
924 return NO;
925
[email protected]ff81e0a2009-04-13 14:58:54926 // Don't do anything if there are visible windows. This will cause
927 // AppKit to unminimize the most recently minimized window.
928 if (flag)
929 return YES;
930
[email protected]0d1210d62010-09-07 17:31:38931 // If launched as a hidden login item (due to installation of a persistent app
932 // or by the user, for example in System Preferenecs->Accounts->Login Items),
933 // allow session to be restored first time the user clicks on a Dock icon.
934 // Normally, it'd just open a new empty page.
935 {
936 static BOOL doneOnce = NO;
937 if (!doneOnce) {
938 doneOnce = YES;
[email protected]0378bf42011-01-01 18:20:14939 if (base::mac::WasLaunchedAsHiddenLoginItem()) {
[email protected]0d1210d62010-09-07 17:31:38940 SessionService* sessionService =
[email protected]a585a8db2011-06-20 18:58:35941 SessionServiceFactory::GetForProfile([self lastProfile]);
[email protected]0d1210d62010-09-07 17:31:38942 if (sessionService &&
943 sessionService->RestoreIfNecessary(std::vector<GURL>()))
944 return NO;
945 }
946 }
947 }
[email protected]ff81e0a2009-04-13 14:58:54948 // Otherwise open a new window.
[email protected]d630d7d52010-02-21 00:55:11949 {
[email protected]0fbd70332010-06-01 19:28:34950 AutoReset<bool> auto_reset_in_run(&g_is_opening_new_window, true);
[email protected]a585a8db2011-06-20 18:58:35951 Browser::OpenEmptyWindow([self lastProfile]);
[email protected]d630d7d52010-02-21 00:55:11952 }
[email protected]ff81e0a2009-04-13 14:58:54953
954 // We've handled the reopen event, so return NO to tell AppKit not
955 // to do anything.
956 return NO;
957}
958
[email protected]88d74942009-01-21 22:04:44959- (void)initMenuState {
[email protected]3111f08b2009-04-30 16:01:52960 menuState_.reset(new CommandUpdater(NULL));
[email protected]fbc947b2009-06-19 13:28:24961 menuState_->UpdateCommandEnabled(IDC_NEW_TAB, true);
[email protected]88d74942009-01-21 22:04:44962 menuState_->UpdateCommandEnabled(IDC_NEW_WINDOW, true);
[email protected]863ff662009-01-26 20:18:18963 menuState_->UpdateCommandEnabled(IDC_NEW_INCOGNITO_WINDOW, true);
[email protected]e19516d2009-04-28 17:15:19964 menuState_->UpdateCommandEnabled(IDC_OPEN_FILE, true);
[email protected]93e181762009-05-29 14:30:38965 menuState_->UpdateCommandEnabled(IDC_CLEAR_BROWSING_DATA, true);
[email protected]fbc947b2009-06-19 13:28:24966 menuState_->UpdateCommandEnabled(IDC_RESTORE_TAB, false);
[email protected]91a5b3e2009-10-30 19:32:22967 menuState_->UpdateCommandEnabled(IDC_FOCUS_LOCATION, true);
[email protected]cddc5242009-12-07 18:54:33968 menuState_->UpdateCommandEnabled(IDC_FOCUS_SEARCH, true);
[email protected]af7c25e2010-01-23 05:46:45969 menuState_->UpdateCommandEnabled(IDC_SHOW_BOOKMARK_MANAGER, true);
[email protected]91a5b3e2009-10-30 19:32:22970 menuState_->UpdateCommandEnabled(IDC_SHOW_HISTORY, true);
971 menuState_->UpdateCommandEnabled(IDC_SHOW_DOWNLOADS, true);
[email protected]bb37e6ec2009-11-18 17:59:36972 menuState_->UpdateCommandEnabled(IDC_MANAGE_EXTENSIONS, true);
[email protected]1fdff702009-10-22 00:36:18973 menuState_->UpdateCommandEnabled(IDC_HELP_PAGE, true);
[email protected]4aee5652009-11-19 18:59:21974 menuState_->UpdateCommandEnabled(IDC_IMPORT_SETTINGS, true);
[email protected]1c41a932010-11-22 23:41:23975 menuState_->UpdateCommandEnabled(IDC_FEEDBACK, true);
[email protected]d3b98c82010-07-14 07:45:59976 menuState_->UpdateCommandEnabled(IDC_SYNC_BOOKMARKS, true);
[email protected]ceaaf1812010-01-20 22:13:09977 menuState_->UpdateCommandEnabled(IDC_TASK_MANAGER, true);
[email protected]88d74942009-01-21 22:04:44978}
[email protected]1bcdb532009-01-16 17:47:57979
[email protected]961a6932011-07-19 19:52:46980// Conditionally adds the Profile menu to the main menu bar.
981- (void)initProfileMenu {
982 bool enableMenu = ProfileManager::IsMultipleProfilesEnabled();
983
984 NSMenu* mainMenu = [NSApp mainMenu];
985 NSMenuItem* profileMenu = [mainMenu itemWithTag:IDC_PROFILE_MAIN_MENU];
[email protected]e354ca42011-08-03 20:31:40986
987 // On Leopard, hiding main menubar items does not work. This manifests itself
988 // in Chromium as squished menu items <http://crbug.com/90753>. To prevent
989 // this, remove the Profile menu on Leopard, regardless of the user's
990 // multiprofile state.
991 if (base::mac::IsOSLeopard()) {
992 [mainMenu removeItem:profileMenu];
993 return;
994 }
995
[email protected]961a6932011-07-19 19:52:46996 [profileMenu setHidden:!enableMenu];
997
998 if (enableMenu) {
999 profileMenuController_.reset(
1000 [[ProfileMenuController alloc] initWithMainMenuItem:profileMenu]);
1001 }
1002}
1003
[email protected]a99fce0e2011-03-21 20:58:481004// The Confirm to Quit preference is atypical in that the preference lives in
1005// the app menu right above the Quit menu item. This method will refresh the
1006// display of that item depending on the preference state.
1007- (void)updateConfirmToQuitPrefMenuItem:(NSMenuItem*)item {
1008 // Format the string so that the correct key equivalent is displayed.
1009 NSString* acceleratorString = [ConfirmQuitPanelController keyCommandString];
1010 NSString* title = l10n_util::GetNSStringF(IDS_CONFIRM_TO_QUIT_OPTION,
1011 base::SysNSStringToUTF16(acceleratorString));
1012 [item setTitle:title];
1013
[email protected]a585a8db2011-06-20 18:58:351014 const PrefService* prefService = [self lastProfile]->GetPrefs();
[email protected]a99fce0e2011-03-21 20:58:481015 bool enabled = prefService->GetBoolean(prefs::kConfirmToQuitEnabled);
1016 [item setState:enabled ? NSOnState : NSOffState];
1017}
1018
[email protected]eef99c22010-08-17 05:55:161019- (void)registerServicesMenuTypesTo:(NSApplication*)app {
[email protected]09ea24c2010-08-23 19:05:411020 // Note that RenderWidgetHostViewCocoa implements NSServicesRequests which
1021 // handles requests from services.
1022 NSArray* types = [NSArray arrayWithObjects:NSStringPboardType, nil];
1023 [app registerServicesMenuSendTypes:types returnTypes:types];
[email protected]eef99c22010-08-17 05:55:161024}
1025
[email protected]a585a8db2011-06-20 18:58:351026- (Profile*)lastProfile {
[email protected]7fd5a462011-06-19 16:57:231027 // Return the profile of the last-used BrowserWindowController, if available.
1028 if (lastProfile_)
1029 return lastProfile_;
1030
[email protected]056f8792011-07-20 15:40:151031 // On first launch, no profile will be stored, so use last from Local State.
[email protected]3f34599d2009-03-25 22:11:431032 if (g_browser_process->profile_manager())
[email protected]056f8792011-07-20 15:40:151033 return g_browser_process->profile_manager()->GetLastUsedProfile();
[email protected]3f34599d2009-03-25 22:11:431034
[email protected]f6314002009-04-23 01:18:131035 return NULL;
[email protected]3f34599d2009-03-25 22:11:431036}
1037
[email protected]57750f822009-04-21 21:43:091038// Various methods to open URLs that we get in a native fashion. We use
1039// BrowserInit here because on the other platforms, URLs to open come through
1040// the ProcessSingleton, and it calls BrowserInit. It's best to bottleneck the
1041// openings through that for uniform handling.
1042
[email protected]d630d7d52010-02-21 00:55:111043- (void)openUrls:(const std::vector<GURL>&)urls {
[email protected]caadfca2010-01-22 21:51:211044 // If the browser hasn't started yet, just queue up the URLs.
1045 if (!startupComplete_) {
[email protected]d630d7d52010-02-21 00:55:111046 startupUrls_.insert(startupUrls_.end(), urls.begin(), urls.end());
[email protected]136140c2009-05-19 13:58:251047 return;
1048 }
[email protected]57750f822009-04-21 21:43:091049
[email protected]0e863512009-05-28 19:45:071050 Browser* browser = BrowserList::GetLastActive();
1051 // if no browser window exists then create one with no tabs to be filled in
1052 if (!browser) {
[email protected]a585a8db2011-06-20 18:58:351053 browser = Browser::Create([self lastProfile]);
[email protected]ce560f82009-06-03 09:39:441054 browser->window()->Show();
[email protected]0e863512009-05-28 19:45:071055 }
1056
[email protected]947446b2010-10-21 03:36:311057 CommandLine dummy(CommandLine::NO_PROGRAM);
[email protected]f805fe82010-08-03 22:47:101058 BrowserInit::LaunchWithProfile launch(FilePath(), dummy);
[email protected]0e863512009-05-28 19:45:071059 launch.OpenURLsInBrowser(browser, false, urls);
[email protected]57750f822009-04-21 21:43:091060}
1061
[email protected]57750f822009-04-21 21:43:091062- (void)getUrl:(NSAppleEventDescriptor*)event
1063 withReply:(NSAppleEventDescriptor*)reply {
1064 NSString* urlStr = [[event paramDescriptorForKeyword:keyDirectObject]
1065 stringValue];
1066
1067 GURL gurl(base::SysNSStringToUTF8(urlStr));
1068 std::vector<GURL> gurlVector;
1069 gurlVector.push_back(gurl);
1070
[email protected]d630d7d52010-02-21 00:55:111071 [self openUrls:gurlVector];
[email protected]57750f822009-04-21 21:43:091072}
1073
[email protected]caadfca2010-01-22 21:51:211074- (void)application:(NSApplication*)sender
1075 openFiles:(NSArray*)filenames {
[email protected]57750f822009-04-21 21:43:091076 std::vector<GURL> gurlVector;
[email protected]caadfca2010-01-22 21:51:211077 for (NSString* file in filenames) {
1078 GURL gurl = net::FilePathToFileURL(FilePath(base::SysNSStringToUTF8(file)));
[email protected]57750f822009-04-21 21:43:091079 gurlVector.push_back(gurl);
1080 }
[email protected]caadfca2010-01-22 21:51:211081 if (!gurlVector.empty())
[email protected]d630d7d52010-02-21 00:55:111082 [self openUrls:gurlVector];
[email protected]caadfca2010-01-22 21:51:211083 else
1084 NOTREACHED() << "Nothing to open!";
[email protected]57750f822009-04-21 21:43:091085
[email protected]caadfca2010-01-22 21:51:211086 [sender replyToOpenOrPrint:NSApplicationDelegateReplySuccess];
[email protected]57750f822009-04-21 21:43:091087}
[email protected]3f34599d2009-03-25 22:11:431088
[email protected]3111f08b2009-04-30 16:01:521089// Show the preferences window, or bring it to the front if it's already
1090// visible.
1091- (IBAction)showPreferences:(id)sender {
[email protected]a585a8db2011-06-20 18:58:351092 if (Browser* browser = ActivateBrowser([self lastProfile])) {
[email protected]445177652011-03-09 02:04:051093 // Show options tab in the active browser window.
1094 browser->OpenOptionsDialog();
[email protected]4a42d272010-06-18 01:29:421095 } else {
[email protected]445177652011-03-09 02:04:051096 // No browser window, so create one for the options tab.
[email protected]a585a8db2011-06-20 18:58:351097 Browser::OpenOptionsWindow([self lastProfile]);
[email protected]4a42d272010-06-18 01:29:421098 }
[email protected]d989f07c2009-11-14 00:35:461099}
1100
[email protected]bde3dda2009-05-20 22:13:071101// Called when the about window is closed. We use this to release the
1102// window controller.
[email protected]d4e18652009-11-19 21:52:161103- (void)aboutWindowClosed:(NSNotification*)notification {
1104 NSWindow* window = [aboutController_ window];
[email protected]2ce9c89752011-02-25 18:24:341105 DCHECK_EQ([notification object], window);
[email protected]bde3dda2009-05-20 22:13:071106 [[NSNotificationCenter defaultCenter]
[email protected]49aeee52009-10-26 19:58:131107 removeObserver:self
[email protected]d4e18652009-11-19 21:52:161108 name:NSWindowWillCloseNotification
1109 object:window];
1110 // AboutWindowControllers are autoreleased in
1111 // -[AboutWindowController windowWillClose:].
1112 aboutController_ = nil;
[email protected]bde3dda2009-05-20 22:13:071113}
1114
1115- (IBAction)orderFrontStandardAboutPanel:(id)sender {
[email protected]bde3dda2009-05-20 22:13:071116 if (!aboutController_) {
[email protected]d4e18652009-11-19 21:52:161117 aboutController_ =
[email protected]a585a8db2011-06-20 18:58:351118 [[AboutWindowController alloc] initWithProfile:[self lastProfile]];
[email protected]49aeee52009-10-26 19:58:131119
[email protected]bde3dda2009-05-20 22:13:071120 // Watch for a notification of when it goes away so that we can destroy
1121 // the controller.
1122 [[NSNotificationCenter defaultCenter]
1123 addObserver:self
1124 selector:@selector(aboutWindowClosed:)
[email protected]d4e18652009-11-19 21:52:161125 name:NSWindowWillCloseNotification
1126 object:[aboutController_ window]];
[email protected]bde3dda2009-05-20 22:13:071127 }
[email protected]49aeee52009-10-26 19:58:131128
[email protected]bde3dda2009-05-20 22:13:071129 [aboutController_ showWindow:self];
[email protected]bde3dda2009-05-20 22:13:071130}
1131
[email protected]a99fce0e2011-03-21 20:58:481132- (IBAction)toggleConfirmToQuit:(id)sender {
[email protected]a585a8db2011-06-20 18:58:351133 PrefService* prefService = [self lastProfile]->GetPrefs();
[email protected]a99fce0e2011-03-21 20:58:481134 bool enabled = prefService->GetBoolean(prefs::kConfirmToQuitEnabled);
1135 prefService->SetBoolean(prefs::kConfirmToQuitEnabled, !enabled);
1136}
1137
[email protected]3d4bd23f2009-10-06 03:58:381138// Explicitly bring to the foreground when creating new windows from the dock.
[email protected]93b59fc2010-12-21 20:00:471139- (void)commandFromDock:(id)sender {
[email protected]3d4bd23f2009-10-06 03:58:381140 [NSApp activateIgnoringOtherApps:YES];
1141 [self commandDispatch:sender];
1142}
1143
[email protected]227fcee2010-06-11 19:19:371144- (NSMenu*)applicationDockMenu:(NSApplication*)sender {
[email protected]018a3962009-09-17 22:23:441145 NSMenu* dockMenu = [[[NSMenu alloc] initWithTitle: @""] autorelease];
[email protected]a585a8db2011-06-20 18:58:351146 Profile* profile = [self lastProfile];
[email protected]93b59fc2010-12-21 20:00:471147
[email protected]018a3962009-09-17 22:23:441148 NSString* titleStr = l10n_util::GetNSStringWithFixup(IDS_NEW_WINDOW_MAC);
[email protected]8c6b029d2011-01-21 22:39:511149 scoped_nsobject<NSMenuItem> item(
1150 [[NSMenuItem alloc] initWithTitle:titleStr
1151 action:@selector(commandFromDock:)
1152 keyEquivalent:@""]);
[email protected]288bfcd32009-09-14 18:14:461153 [item setTarget:self];
1154 [item setTag:IDC_NEW_WINDOW];
[email protected]018a3962009-09-17 22:23:441155 [dockMenu addItem:item];
[email protected]288bfcd32009-09-14 18:14:461156
1157 titleStr = l10n_util::GetNSStringWithFixup(IDS_NEW_INCOGNITO_WINDOW_MAC);
[email protected]018a3962009-09-17 22:23:441158 item.reset([[NSMenuItem alloc] initWithTitle:titleStr
[email protected]8c6b029d2011-01-21 22:39:511159 action:@selector(commandFromDock:)
[email protected]018a3962009-09-17 22:23:441160 keyEquivalent:@""]);
[email protected]288bfcd32009-09-14 18:14:461161 [item setTarget:self];
1162 [item setTag:IDC_NEW_INCOGNITO_WINDOW];
[email protected]018a3962009-09-17 22:23:441163 [dockMenu addItem:item];
[email protected]288bfcd32009-09-14 18:14:461164
[email protected]8c6b029d2011-01-21 22:39:511165 // TODO(rickcam): Mock out BackgroundApplicationListModel, then add unit
1166 // tests which use the mock in place of the profile-initialized model.
1167
1168 // Avoid breaking unit tests which have no profile.
1169 if (profile) {
1170 BackgroundApplicationListModel applications(profile);
1171 if (applications.size()) {
1172 int position = 0;
1173 NSString* menuStr =
1174 l10n_util::GetNSStringWithFixup(IDS_BACKGROUND_APPS_MAC);
1175 scoped_nsobject<NSMenu> appMenu([[NSMenu alloc] initWithTitle:menuStr]);
1176 for (ExtensionList::const_iterator cursor = applications.begin();
1177 cursor != applications.end();
1178 ++cursor, ++position) {
[email protected]2ce9c89752011-02-25 18:24:341179 DCHECK_EQ(applications.GetPosition(*cursor), position);
[email protected]8c6b029d2011-01-21 22:39:511180 NSString* itemStr =
1181 base::SysUTF16ToNSString(UTF8ToUTF16((*cursor)->name()));
1182 scoped_nsobject<NSMenuItem> appItem([[NSMenuItem alloc]
1183 initWithTitle:itemStr
1184 action:@selector(commandFromDock:)
1185 keyEquivalent:@""]);
1186 [appItem setTarget:self];
1187 [appItem setTag:position];
1188 [appMenu addItem:appItem];
1189 }
1190 scoped_nsobject<NSMenuItem> appMenuItem([[NSMenuItem alloc]
1191 initWithTitle:menuStr
1192 action:@selector(commandFromDock:)
1193 keyEquivalent:@""]);
1194 [appMenuItem setTarget:self];
1195 [appMenuItem setTag:position];
1196 [appMenuItem setSubmenu:appMenu];
1197 [dockMenu addItem:appMenuItem];
1198 }
1199 }
1200
[email protected]018a3962009-09-17 22:23:441201 return dockMenu;
[email protected]288bfcd32009-09-14 18:14:461202}
1203
[email protected]d630d7d52010-02-21 00:55:111204- (const std::vector<GURL>&)startupUrls {
1205 return startupUrls_;
[email protected]caadfca2010-01-22 21:51:211206}
1207
[email protected]d630d7d52010-02-21 00:55:111208- (void)clearStartupUrls {
1209 startupUrls_.clear();
[email protected]caadfca2010-01-22 21:51:211210}
1211
[email protected]7e4c69d02011-05-11 22:16:471212- (BookmarkMenuBridge*)bookmarkMenuBridge {
1213 return bookmarkMenuBridge_.get();
1214}
1215
[email protected]d630d7d52010-02-21 00:55:111216@end // @implementation AppController
[email protected]60ad3e22009-09-18 21:07:191217
1218//---------------------------------------------------------------------------
1219
[email protected]0934de82011-03-09 18:42:161220namespace browser {
1221
1222void ShowInstantConfirmDialog(gfx::NativeWindow parent, Profile* profile) {
1223 if (Browser* browser = ActivateBrowser(profile)) {
[email protected]b99081b2011-03-10 17:43:471224 browser->OpenInstantConfirmDialog();
[email protected]0934de82011-03-09 18:42:161225 } else {
[email protected]b99081b2011-03-10 17:43:471226 Browser::OpenInstantConfirmDialogWindow(profile);
[email protected]0934de82011-03-09 18:42:161227 }
1228}
1229
1230} // namespace browser
1231
[email protected]216e9042009-10-29 17:05:231232namespace app_controller_mac {
1233
1234bool IsOpeningNewWindow() {
1235 return g_is_opening_new_window;
1236}
1237
1238} // namespace app_controller_mac