blob: 72a0b75ae10558b7cb34ca83b4016e41f063dbd5 [file] [log] [blame]
[email protected]7dc8c6b2010-04-09 17:02:501// Copyright (c) 2010 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]0ccf4612010-07-31 07:11:367#include "app/l10n_util.h"
[email protected]288bfcd32009-09-14 18:14:468#include "app/l10n_util_mac.h"
[email protected]d630d7d52010-02-21 00:55:119#include "base/auto_reset.h"
[email protected]57750f822009-04-21 21:43:0910#include "base/command_line.h"
[email protected]f805fe82010-08-03 22:47:1011#include "base/file_path.h"
[email protected]09729a552009-08-03 23:21:4112#include "base/mac_util.h"
[email protected]2c47bc12009-04-10 20:14:0013#include "base/message_loop.h"
[email protected]528c56d2010-07-30 19:28:4414#include "base/string_number_conversions.h"
[email protected]57750f822009-04-21 21:43:0915#include "base/sys_string_conversions.h"
[email protected]2c47bc12009-04-10 20:14:0016#include "chrome/app/chrome_dll_resource.h"
17#include "chrome/browser/browser.h"
[email protected]57750f822009-04-21 21:43:0918#include "chrome/browser/browser_init.h"
[email protected]2c47bc12009-04-10 20:14:0019#include "chrome/browser/browser_list.h"
[email protected]5c238752009-06-13 10:29:0720#include "chrome/browser/browser_process.h"
[email protected]41741a962009-02-18 21:51:3921#include "chrome/browser/browser_shutdown.h"
[email protected]ce560f82009-06-03 09:39:4422#include "chrome/browser/browser_window.h"
[email protected]12dc3d42010-02-22 23:37:1223#include "chrome/browser/chrome_thread.h"
[email protected]bde3dda2009-05-20 22:13:0724#import "chrome/browser/cocoa/about_window_controller.h"
[email protected]3f34599d2009-03-25 22:11:4325#import "chrome/browser/cocoa/bookmark_menu_bridge.h"
[email protected]a9e8afc2009-08-11 22:03:1726#import "chrome/browser/cocoa/browser_window_cocoa.h"
27#import "chrome/browser/cocoa/browser_window_controller.h"
[email protected]3896b182009-11-03 16:15:3228#import "chrome/browser/cocoa/bug_report_window_controller.h"
[email protected]93e181762009-05-29 14:30:3829#import "chrome/browser/cocoa/clear_browsing_data_controller.h"
[email protected]2bcec612009-05-14 17:50:5330#import "chrome/browser/cocoa/encoding_menu_controller_delegate_mac.h"
[email protected]4aee5652009-11-19 18:59:2131#import "chrome/browser/cocoa/history_menu_bridge.h"
32#import "chrome/browser/cocoa/import_settings_dialog.h"
[email protected]42404382009-04-30 17:59:2433#import "chrome/browser/cocoa/preferences_window_controller.h"
[email protected]449dd2f2009-05-27 13:04:0034#import "chrome/browser/cocoa/tab_strip_controller.h"
35#import "chrome/browser/cocoa/tab_window_controller.h"
[email protected]ceaaf1812010-01-20 22:13:0936#include "chrome/browser/cocoa/task_manager_mac.h"
[email protected]2c47bc12009-04-10 20:14:0037#include "chrome/browser/command_updater.h"
[email protected]09729a552009-08-03 23:21:4138#include "chrome/browser/download/download_manager.h"
[email protected]4aee5652009-11-19 18:59:2139#include "chrome/browser/metrics/user_metrics.h"
[email protected]60ad3e22009-09-18 21:07:1940#include "chrome/browser/options_window.h"
[email protected]37858e52010-08-26 00:22:0241#include "chrome/browser/prefs/pref_service.h"
[email protected]052313b2010-02-19 09:43:0842#include "chrome/browser/profile_manager.h"
[email protected]fbc947b2009-06-19 13:28:2443#include "chrome/browser/sessions/tab_restore_service.h"
[email protected]f9bc9b92009-11-24 00:55:3544#include "chrome/browser/sync/profile_sync_service.h"
[email protected]40f047972009-11-25 03:54:4045#include "chrome/browser/sync/sync_ui_util.h"
46#include "chrome/browser/sync/sync_ui_util_mac.h"
[email protected]60ad3e22009-09-18 21:07:1947#include "chrome/browser/tab_contents/tab_contents.h"
[email protected]646b9b92010-04-30 21:51:5848#include "chrome/common/app_mode_common_mac.h"
[email protected]12dc3d42010-02-22 23:37:1249#include "chrome/common/chrome_paths_internal.h"
[email protected]f4acfae82009-09-11 00:33:3850#include "chrome/common/chrome_switches.h"
[email protected]a96ec6a2009-11-04 17:27:0851#include "chrome/common/notification_service.h"
[email protected]cd63ef62009-05-06 19:41:3752#include "chrome/common/pref_names.h"
[email protected]024617a2010-08-20 05:08:0553#include "chrome/common/url_constants.h"
[email protected]9dcb59662009-09-23 01:15:5054#include "grit/chromium_strings.h"
[email protected]09729a552009-08-03 23:21:4155#include "grit/generated_resources.h"
[email protected]caadfca2010-01-22 21:51:2156#include "net/base/net_util.h"
[email protected]88d74942009-01-21 22:04:4457
[email protected]f2c20fa2009-12-01 17:42:0258// 10.6 adds a public API for the Spotlight-backed search menu item in the Help
59// menu. Provide the declaration so it can be called below when building with
60// the 10.5 SDK.
61#if !defined(MAC_OS_X_VERSION_10_6) || \
62 MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6
63@interface NSApplication (SnowLeopardSDKDeclarations)
[email protected]caadfca2010-01-22 21:51:2164- (void)setHelpMenu:(NSMenu*)helpMenu;
[email protected]f2c20fa2009-12-01 17:42:0265@end
66#endif
67
[email protected]d630d7d52010-02-21 00:55:1168namespace {
69
70// True while AppController is calling Browser::OpenEmptyWindow(). We need a
71// global flag here, analogue to BrowserInit::InProcessStartup() because
72// otherwise the SessionService will try to restore sessions when we make a new
73// window while there are no other active windows.
74bool g_is_opening_new_window = false;
75
76// Activates a browser window having the given profile (the last one active) if
77// possible and returns a pointer to the activate |Browser| or NULL if this was
78// not possible. If the last active browser is minimized (in particular, if
79// there are only minimized windows), it will unminimize it.
80Browser* ActivateBrowser(Profile* profile) {
81 Browser* browser = BrowserList::GetLastActiveWithProfile(profile);
82 if (browser)
83 browser->window()->Activate();
84 return browser;
85}
86
87// Creates an empty browser window with the given profile and returns a pointer
88// to the new |Browser|.
89Browser* CreateBrowser(Profile* profile) {
90 {
[email protected]0fbd70332010-06-01 19:28:3491 AutoReset<bool> auto_reset_in_run(&g_is_opening_new_window, true);
[email protected]d630d7d52010-02-21 00:55:1192 Browser::OpenEmptyWindow(profile);
93 }
94
95 Browser* browser = BrowserList::GetLastActive();
96 CHECK(browser);
97 return browser;
98}
99
100// Activates a browser window having the given profile (the last one active) if
101// possible or creates an empty one if necessary. Returns a pointer to the
102// activated/new |Browser|.
103Browser* ActivateOrCreateBrowser(Profile* profile) {
104 if (Browser* browser = ActivateBrowser(profile))
105 return browser;
106 return CreateBrowser(profile);
107}
108
[email protected]12dc3d42010-02-22 23:37:12109// This task synchronizes preferences (under "org.chromium.Chromium" or
110// "com.google.Chrome"), in particular, writes them out to disk.
111class PrefsSyncTask : public Task {
112 public:
113 PrefsSyncTask() {}
114 virtual ~PrefsSyncTask() {}
115 virtual void Run() {
116 if (!CFPreferencesAppSynchronize(app_mode::kAppPrefsID))
117 LOG(WARNING) << "Error recording application bundle path.";
118 }
119};
120
121// Record the location of the application bundle (containing the main framework)
122// from which Chromium was loaded. This is used by app mode shims to find
123// Chromium.
124void RecordLastRunAppBundlePath() {
125 // Going up three levels from |chrome::GetVersionedDirectory()| gives the
126 // real, user-visible app bundle directory. (The alternatives give either the
127 // framework's path or the initial app's path, which may be an app mode shim
128 // or a unit test.)
129 FilePath appBundlePath =
130 chrome::GetVersionedDirectory().DirName().DirName().DirName();
131 CFPreferencesSetAppValue(app_mode::kLastRunAppBundlePathPrefsKey,
132 base::SysUTF8ToCFStringRef(appBundlePath.value()),
133 app_mode::kAppPrefsID);
134
135 // Sync after a delay avoid I/O contention on startup; 1500 ms is plenty.
136 ChromeThread::PostDelayedTask(ChromeThread::FILE, FROM_HERE,
137 new PrefsSyncTask(), 1500);
138}
139
[email protected]d630d7d52010-02-21 00:55:11140} // anonymous namespace
141
142@interface AppController(Private)
[email protected]88d74942009-01-21 22:04:44143- (void)initMenuState;
[email protected]eef99c22010-08-17 05:55:16144- (void)registerServicesMenuTypesTo:(NSApplication*)app;
[email protected]d630d7d52010-02-21 00:55:11145- (void)openUrls:(const std::vector<GURL>&)urls;
[email protected]57750f822009-04-21 21:43:09146- (void)getUrl:(NSAppleEventDescriptor*)event
147 withReply:(NSAppleEventDescriptor*)reply;
[email protected]449dd2f2009-05-27 13:04:00148- (void)windowLayeringDidChange:(NSNotification*)inNotification;
[email protected]dd6ab1282010-07-20 01:07:32149- (void)checkForAnyKeyWindows;
[email protected]73f5b362009-08-10 23:58:21150- (BOOL)userWillWaitForInProgressDownloads:(int)downloadCount;
[email protected]09729a552009-08-03 23:21:41151- (BOOL)shouldQuitWithInProgressDownloads;
[email protected]d989f07c2009-11-14 00:35:46152- (void)showPreferencesWindow:(id)sender
153 page:(OptionsPage)page
154 profile:(Profile*)profile;
[email protected]88d74942009-01-21 22:04:44155@end
[email protected]1bcdb532009-01-16 17:47:57156
157@implementation AppController
158
[email protected]caadfca2010-01-22 21:51:21159@synthesize startupComplete = startupComplete_;
160
[email protected]cd63ef62009-05-06 19:41:37161// This method is called very early in application startup (ie, before
162// the profile is loaded or any preferences have been registered). Defer any
163// user-data initialization until -applicationDidFinishLaunching:.
[email protected]88d74942009-01-21 22:04:44164- (void)awakeFromNib {
[email protected]136140c2009-05-19 13:58:25165 // We need to register the handlers early to catch events fired on launch.
[email protected]57750f822009-04-21 21:43:09166 NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager];
167 [em setEventHandler:self
168 andSelector:@selector(getUrl:withReply:)
169 forEventClass:kInternetEventClass
170 andEventID:kAEGetURL];
171 [em setEventHandler:self
172 andSelector:@selector(getUrl:withReply:)
173 forEventClass:'WWW!' // A particularly ancient AppleEvent that dates
174 andEventID:'OURL']; // back to the Spyglass days.
[email protected]cd63ef62009-05-06 19:41:37175
[email protected]449dd2f2009-05-27 13:04:00176 // Register for various window layering changes. We use these to update
177 // various UI elements (command-key equivalents, etc) when the frontmost
178 // window changes.
179 NSNotificationCenter* notificationCenter =
180 [NSNotificationCenter defaultCenter];
[email protected]ce560f82009-06-03 09:39:44181 [notificationCenter
[email protected]449dd2f2009-05-27 13:04:00182 addObserver:self
183 selector:@selector(windowLayeringDidChange:)
184 name:NSWindowDidBecomeKeyNotification
185 object:nil];
186 [notificationCenter
187 addObserver:self
188 selector:@selector(windowLayeringDidChange:)
189 name:NSWindowDidResignKeyNotification
190 object:nil];
191 [notificationCenter
192 addObserver:self
193 selector:@selector(windowLayeringDidChange:)
194 name:NSWindowDidBecomeMainNotification
195 object:nil];
196 [notificationCenter
197 addObserver:self
198 selector:@selector(windowLayeringDidChange:)
199 name:NSWindowDidResignMainNotification
200 object:nil];
201
202 // Register for a notification that the number of tabs changes in windows
203 // so we can adjust the close tab/window command keys.
204 [notificationCenter
205 addObserver:self
206 selector:@selector(tabsChanged:)
207 name:kTabStripNumberOfTabsChanged
208 object:nil];
209
[email protected]136140c2009-05-19 13:58:25210 // Set up the command updater for when there are no windows open
211 [self initMenuState];
[email protected]f4acfae82009-09-11 00:33:38212
213 // Activate (bring to foreground) if asked to do so. On
214 // Windows this logic isn't necessary since
215 // BrowserWindow::Activate() calls ::SetForegroundWindow() which is
216 // adequate. On Mac, BrowserWindow::Activate() calls -[NSWindow
217 // makeKeyAndOrderFront:] which does not activate the application
218 // itself.
219 const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess();
220 if (parsed_command_line.HasSwitch(switches::kActivateOnLaunch)) {
221 [NSApp activateIgnoringOtherApps:YES];
222 }
[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]c984d9f2010-07-20 20:52:20244 size_t num_browsers = BrowserList::size();
[email protected]7dc8c6b2010-04-09 17:02:50245
[email protected]c984d9f2010-07-20 20:52:20246 // Initiate a shutdown (via BrowserList::CloseAllBrowsers()) if we aren't
247 // already shutting down.
248 if (!browser_shutdown::IsTryingToQuit())
[email protected]c6032e82010-09-13 20:06:05249 BrowserList::CloseAllBrowsers();
[email protected]7dc8c6b2010-04-09 17:02:50250
[email protected]c984d9f2010-07-20 20:52:20251 return num_browsers == 0 ? YES : NO;
[email protected]7dc8c6b2010-04-09 17:02:50252}
253
254- (void)stopTryingToTerminateApplication:(NSApplication*)app {
255 if (browser_shutdown::IsTryingToQuit()) {
256 // Reset the "trying to quit" state, so that closing all browser windows
257 // will no longer lead to termination.
258 browser_shutdown::SetTryingToQuit(false);
259
260 // TODO(viettrungluu): Were we to remove Apple Event handlers above, we
261 // would have to reinstall them here. http://crbug.com/40861
262 }
[email protected]a9e8afc2009-08-11 22:03:17263}
264
[email protected]449dd2f2009-05-27 13:04:00265// Called when the app is shutting down. Clean-up as appropriate.
[email protected]7dc8c6b2010-04-09 17:02:50266- (void)applicationWillTerminate:(NSNotification*)aNotification {
[email protected]3b6aa8b62009-09-15 21:36:11267 NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager];
268 [em removeEventHandlerForEventClass:kInternetEventClass
269 andEventID:kAEGetURL];
270 [em removeEventHandlerForEventClass:'WWW!'
271 andEventID:'OURL'];
[email protected]3b6aa8b62009-09-15 21:36:11272
[email protected]7dc8c6b2010-04-09 17:02:50273 // There better be no browser windows left at this point.
274 CHECK_EQ(BrowserList::size(), 0u);
[email protected]3b6aa8b62009-09-15 21:36:11275
[email protected]c984d9f2010-07-20 20:52:20276 // Tell BrowserList not to keep the browser process alive. Once all the
277 // browsers get dealloc'd, it will stop the RunLoop and fall back into main().
278 BrowserList::EndKeepAlive();
[email protected]fbc947b2009-06-19 13:28:24279
[email protected]d4e18652009-11-19 21:52:16280 // Close these off if they have open windows.
281 [prefsController_ close];
282 [aboutController_ close];
283
[email protected]449dd2f2009-05-27 13:04:00284 [[NSNotificationCenter defaultCenter] removeObserver:self];
285}
286
[email protected]3b6aa8b62009-09-15 21:36:11287- (void)didEndMainMessageLoop {
288 DCHECK(!BrowserList::HasBrowserWithProfile([self defaultProfile]));
289 if (!BrowserList::HasBrowserWithProfile([self defaultProfile])) {
290 // As we're shutting down, we need to nuke the TabRestoreService, which
291 // will start the shutdown of the NavigationControllers and allow for
292 // proper shutdown. If we don't do this, Chrome won't shut down cleanly,
293 // and may end up crashing when some thread tries to use the IO thread (or
294 // another thread) that is no longer valid.
295 [self defaultProfile]->ResetTabRestoreService();
296 }
297}
298
[email protected]449dd2f2009-05-27 13:04:00299// Helper routine to get the window controller if the key window is a tabbed
300// window, or nil if not. Examples of non-tabbed windows are "about" or
301// "preferences".
302- (TabWindowController*)keyWindowTabController {
303 NSWindowController* keyWindowController =
[email protected]5dbabd02009-09-17 20:44:13304 [[NSApp keyWindow] windowController];
[email protected]449dd2f2009-05-27 13:04:00305 if ([keyWindowController isKindOfClass:[TabWindowController class]])
306 return (TabWindowController*)keyWindowController;
307
308 return nil;
309}
310
[email protected]74c0c642009-09-22 21:25:50311// Helper routine to get the window controller if the main window is a tabbed
312// window, or nil if not. Examples of non-tabbed windows are "about" or
313// "preferences".
314- (TabWindowController*)mainWindowTabController {
315 NSWindowController* mainWindowController =
316 [[NSApp mainWindow] windowController];
317 if ([mainWindowController isKindOfClass:[TabWindowController class]])
318 return (TabWindowController*)mainWindowController;
319
320 return nil;
321}
322
[email protected]449dd2f2009-05-27 13:04:00323// If the window has tabs, make "close window" be cmd-shift-w, otherwise leave
324// it as the normal cmd-w. Capitalization of the key equivalent affects whether
325// the shift modifer is used.
326- (void)adjustCloseWindowMenuItemKeyEquivalent:(BOOL)inHaveTabs {
327 [closeWindowMenuItem_ setKeyEquivalent:(inHaveTabs ? @"W" : @"w")];
[email protected]74c0c642009-09-22 21:25:50328 [closeWindowMenuItem_ setKeyEquivalentModifierMask:NSCommandKeyMask];
[email protected]449dd2f2009-05-27 13:04:00329}
330
331// If the window has tabs, make "close tab" take over cmd-w, otherwise it
332// shouldn't have any key-equivalent because it should be disabled.
333- (void)adjustCloseTabMenuItemKeyEquivalent:(BOOL)hasTabs {
334 if (hasTabs) {
335 [closeTabMenuItem_ setKeyEquivalent:@"w"];
336 [closeTabMenuItem_ setKeyEquivalentModifierMask:NSCommandKeyMask];
337 } else {
338 [closeTabMenuItem_ setKeyEquivalent:@""];
339 [closeTabMenuItem_ setKeyEquivalentModifierMask:0];
340 }
341}
342
[email protected]74c0c642009-09-22 21:25:50343// Explicitly remove any command-key equivalents from the close tab/window
344// menus so that nothing can go haywire if we get a user action during pending
345// updates.
346- (void)clearCloseMenuItemKeyEquivalents {
347 [closeTabMenuItem_ setKeyEquivalent:@""];
348 [closeTabMenuItem_ setKeyEquivalentModifierMask:0];
349 [closeWindowMenuItem_ setKeyEquivalent:@""];
350 [closeWindowMenuItem_ setKeyEquivalentModifierMask:0];
351}
352
[email protected]449dd2f2009-05-27 13:04:00353// See if we have a window with tabs open, and adjust the key equivalents for
[email protected]668c911c2010-03-08 17:05:25354// Close Tab/Close Window accordingly.
[email protected]449dd2f2009-05-27 13:04:00355- (void)fixCloseMenuItemKeyEquivalents {
[email protected]668c911c2010-03-08 17:05:25356 fileMenuUpdatePending_ = NO;
[email protected]449dd2f2009-05-27 13:04:00357 TabWindowController* tabController = [self keyWindowTabController];
[email protected]74c0c642009-09-22 21:25:50358 if (!tabController && ![NSApp keyWindow]) {
359 // There might be a small amount of time where there is no key window,
360 // so just use our main browser window if there is one.
361 tabController = [self mainWindowTabController];
362 }
[email protected]449dd2f2009-05-27 13:04:00363 BOOL windowWithMultipleTabs =
364 (tabController && [tabController numberOfTabs] > 1);
365 [self adjustCloseWindowMenuItemKeyEquivalent:windowWithMultipleTabs];
366 [self adjustCloseTabMenuItemKeyEquivalent:windowWithMultipleTabs];
[email protected]449dd2f2009-05-27 13:04:00367}
368
369// Fix up the "close tab/close window" command-key equivalents. We do this
370// after a delay to ensure that window layer state has been set by the time
[email protected]f2a5c342010-01-21 17:00:29371// we do the enabling. This should only be called on the main thread, code that
372// calls this (even as a side-effect) from other threads needs to be fixed.
[email protected]449dd2f2009-05-27 13:04:00373- (void)delayedFixCloseMenuItemKeyEquivalents {
[email protected]f2a5c342010-01-21 17:00:29374 DCHECK([NSThread isMainThread]);
[email protected]449dd2f2009-05-27 13:04:00375 if (!fileMenuUpdatePending_) {
[email protected]74c0c642009-09-22 21:25:50376 // The OS prefers keypresses to timers, so it's possible that a cmd-w
377 // can sneak in before this timer fires. In order to prevent that from
378 // having any bad consequences, just clear the keys combos altogether. They
379 // will be reset when the timer eventually fires.
[email protected]668c911c2010-03-08 17:05:25380 if ([NSThread isMainThread]) {
381 fileMenuUpdatePending_ = YES;
382 [self clearCloseMenuItemKeyEquivalents];
383 [self performSelector:@selector(fixCloseMenuItemKeyEquivalents)
384 withObject:nil
385 afterDelay:0];
386 } else {
387 // This shouldn't be happening, but if it does, force it to the main
388 // thread to avoid dropping the update. Don't mess with
389 // |fileMenuUpdatePending_| as it's not expected to be threadsafe and
390 // there could be a race between the selector finishing and setting the
391 // flag.
392 [self
393 performSelectorOnMainThread:@selector(fixCloseMenuItemKeyEquivalents)
[email protected]f2a5c342010-01-21 17:00:29394 withObject:nil
395 waitUntilDone:NO];
[email protected]668c911c2010-03-08 17:05:25396 }
[email protected]449dd2f2009-05-27 13:04:00397 }
398}
399
400// Called when we get a notification about the window layering changing to
401// update the UI based on the new main window.
402- (void)windowLayeringDidChange:(NSNotification*)notify {
403 [self delayedFixCloseMenuItemKeyEquivalents];
[email protected]dd6ab1282010-07-20 01:07:32404
405 if ([notify name] == NSWindowDidResignKeyNotification) {
406 // If a window is closed, this notification is fired but |[NSApp keyWindow]|
407 // returns nil regardless of whether any suitable candidates for the key
408 // window remain. It seems that the new key window for the app is not set
409 // until after this notification is fired, so a check is performed after the
410 // run loop is allowed to spin.
411 [self performSelector:@selector(checkForAnyKeyWindows)
412 withObject:nil
413 afterDelay:0.0];
414 }
415}
416
417- (void)checkForAnyKeyWindows {
418 if ([NSApp keyWindow])
419 return;
420
421 NotificationService::current()->Notify(
422 NotificationType::NO_KEY_WINDOW,
423 NotificationService::AllSources(),
424 NotificationService::NoDetails());
[email protected]449dd2f2009-05-27 13:04:00425}
426
427// Called when the number of tabs changes in one of the browser windows. The
428// object is the tab strip controller, but we don't currently care.
429- (void)tabsChanged:(NSNotification*)notify {
[email protected]e72746b12009-10-19 14:11:51430 // We don't need to do this on a delay, as in the method above, because the
431 // window layering isn't changing. As a result, there's no chance that a
432 // different window will sneak in as the key window and cause the problems
433 // we hacked around above by clearing the key equivalents.
434 [self fixCloseMenuItemKeyEquivalents];
[email protected]449dd2f2009-05-27 13:04:00435}
436
[email protected]414fde592009-05-21 16:14:43437// If the auto-update interval is not set, make it 5 hours.
438// This code is specific to Mac Chrome Dev Channel.
439// Placed here for 2 reasons:
440// 1) Same spot as other Pref stuff
441// 2) Try and be friendly by keeping this after app launch
442// TODO(jrg): remove once we go Beta.
443- (void)setUpdateCheckInterval {
444#if defined(GOOGLE_CHROME_BUILD)
445 CFStringRef app = (CFStringRef)@"com.google.Keystone.Agent";
446 CFStringRef checkInterval = (CFStringRef)@"checkInterval";
447 CFPropertyListRef plist = CFPreferencesCopyAppValue(checkInterval, app);
448 if (!plist) {
449 const float fiveHoursInSeconds = 5.0 * 60.0 * 60.0;
[email protected]7dc8c6b2010-04-09 17:02:50450 NSNumber* value = [NSNumber numberWithFloat:fiveHoursInSeconds];
[email protected]414fde592009-05-21 16:14:43451 CFPreferencesSetAppValue(checkInterval, value, app);
452 CFPreferencesAppSynchronize(app);
453 }
454#endif
455}
456
[email protected]136140c2009-05-19 13:58:25457// This is called after profiles have been loaded and preferences registered.
458// It is safe to access the default profile here.
459- (void)applicationDidFinishLaunching:(NSNotification*)notify {
[email protected]c984d9f2010-07-20 20:52:20460 // Notify BrowserList to keep the application running so it doesn't go away
461 // when all the browser windows get closed.
462 BrowserList::StartKeepAlive();
[email protected]136140c2009-05-19 13:58:25463
[email protected]e5575022009-08-22 00:31:22464 bookmarkMenuBridge_.reset(new BookmarkMenuBridge([self defaultProfile]));
[email protected]1c84c82c2009-07-27 15:37:25465 historyMenuBridge_.reset(new HistoryMenuBridge([self defaultProfile]));
[email protected]cd63ef62009-05-06 19:41:37466
[email protected]414fde592009-05-21 16:14:43467 [self setUpdateCheckInterval];
[email protected]2bcec612009-05-14 17:50:53468
469 // Build up the encoding menu, the order of the items differs based on the
470 // current locale (see http://crbug.com/7647 for details).
471 // We need a valid g_browser_process to get the profile which is why we can't
472 // call this from awakeFromNib.
[email protected]fc349322009-09-23 22:28:23473 NSMenu* view_menu = [[[NSApp mainMenu] itemWithTag:IDC_VIEW_MENU] submenu];
474 NSMenuItem* encoding_menu_item = [view_menu itemWithTag:IDC_ENCODING_MENU];
[email protected]7dc8c6b2010-04-09 17:02:50475 NSMenu* encoding_menu = [encoding_menu_item submenu];
[email protected]fc349322009-09-23 22:28:23476 EncodingMenuControllerDelegate::BuildEncodingMenu([self defaultProfile],
477 encoding_menu);
[email protected]2bcec612009-05-14 17:50:53478
[email protected]f2c20fa2009-12-01 17:42:02479 // Since Chrome is localized to more languages than the OS, tell Cocoa which
480 // menu is the Help so it can add the search item to it.
[email protected]caadfca2010-01-22 21:51:21481 if (helpMenu_ && [NSApp respondsToSelector:@selector(setHelpMenu:)])
[email protected]f2c20fa2009-12-01 17:42:02482 [NSApp setHelpMenu:helpMenu_];
[email protected]f2c20fa2009-12-01 17:42:02483
[email protected]12dc3d42010-02-22 23:37:12484 // Record the path to the (browser) app bundle; this is used by the app mode
485 // shim.
486 RecordLastRunAppBundlePath();
487
[email protected]eef99c22010-08-17 05:55:16488 // Makes "Services" menu items available.
489 [self registerServicesMenuTypesTo:[notify object]];
490
[email protected]caadfca2010-01-22 21:51:21491 startupComplete_ = YES;
492
493 // TODO(viettrungluu): This is very temporary, since this should be done "in"
494 // |BrowserMain()|, i.e., this list of startup URLs should be appended to the
495 // (probably-empty) list of URLs from the command line.
[email protected]d630d7d52010-02-21 00:55:11496 if (startupUrls_.size()) {
497 [self openUrls:startupUrls_];
498 [self clearStartupUrls];
[email protected]caadfca2010-01-22 21:51:21499 }
[email protected]ad2a3ded2010-08-27 13:19:05500
501 const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess();
502 if (!parsed_command_line.HasSwitch(switches::kEnableExposeForTabs)) {
503 [tabposeMenuItem_ setHidden:YES];
504 }
[email protected]7c321082009-02-09 15:35:47505}
506
[email protected]a96ec6a2009-11-04 17:27:08507// This is called after profiles have been loaded and preferences registered.
508// It is safe to access the default profile here.
509- (void)applicationDidBecomeActive:(NSNotification*)notify {
510 NotificationService::current()->Notify(NotificationType::APP_ACTIVATED,
511 NotificationService::AllSources(),
512 NotificationService::NoDetails());
513}
514
[email protected]73f5b362009-08-10 23:58:21515// Helper function for populating and displaying the in progress downloads at
516// exit alert panel.
517- (BOOL)userWillWaitForInProgressDownloads:(int)downloadCount {
[email protected]e8b5f7882009-09-14 20:59:13518 NSString* warningText = nil;
519 NSString* explanationText = nil;
[email protected]73f5b362009-08-10 23:58:21520 NSString* waitTitle = nil;
521 NSString* exitTitle = nil;
522
[email protected]757127052010-04-27 14:42:26523 string16 product_name = l10n_util::GetStringUTF16(IDS_PRODUCT_NAME);
[email protected]9dcb59662009-09-23 01:15:50524
[email protected]73f5b362009-08-10 23:58:21525 // Set the dialog text based on whether or not there are multiple downloads.
526 if (downloadCount == 1) {
[email protected]e8b5f7882009-09-14 20:59:13527 // Dialog text: warning and explanation.
[email protected]757127052010-04-27 14:42:26528 warningText = l10n_util::GetNSStringF(
529 IDS_SINGLE_DOWNLOAD_REMOVE_CONFIRM_WARNING, product_name);
530 explanationText = l10n_util::GetNSStringF(
531 IDS_SINGLE_DOWNLOAD_REMOVE_CONFIRM_EXPLANATION, product_name);
[email protected]73f5b362009-08-10 23:58:21532
533 // Cancel download and exit button text.
[email protected]757127052010-04-27 14:42:26534 exitTitle = l10n_util::GetNSString(
535 IDS_SINGLE_DOWNLOAD_REMOVE_CONFIRM_OK_BUTTON_LABEL);
[email protected]73f5b362009-08-10 23:58:21536
537 // Wait for download button text.
[email protected]757127052010-04-27 14:42:26538 waitTitle = l10n_util::GetNSString(
539 IDS_SINGLE_DOWNLOAD_REMOVE_CONFIRM_CANCEL_BUTTON_LABEL);
[email protected]73f5b362009-08-10 23:58:21540 } else {
[email protected]e8b5f7882009-09-14 20:59:13541 // Dialog text: warning and explanation.
[email protected]757127052010-04-27 14:42:26542 warningText = l10n_util::GetNSStringF(
543 IDS_MULTIPLE_DOWNLOADS_REMOVE_CONFIRM_WARNING, product_name,
[email protected]528c56d2010-07-30 19:28:44544 base::IntToString16(downloadCount));
[email protected]757127052010-04-27 14:42:26545 explanationText = l10n_util::GetNSStringF(
546 IDS_MULTIPLE_DOWNLOADS_REMOVE_CONFIRM_EXPLANATION, product_name);
[email protected]73f5b362009-08-10 23:58:21547
548 // Cancel downloads and exit button text.
[email protected]757127052010-04-27 14:42:26549 exitTitle = l10n_util::GetNSString(
550 IDS_MULTIPLE_DOWNLOADS_REMOVE_CONFIRM_OK_BUTTON_LABEL);
[email protected]73f5b362009-08-10 23:58:21551
552 // Wait for downloads button text.
[email protected]757127052010-04-27 14:42:26553 waitTitle = l10n_util::GetNSString(
554 IDS_MULTIPLE_DOWNLOADS_REMOVE_CONFIRM_CANCEL_BUTTON_LABEL);
[email protected]73f5b362009-08-10 23:58:21555 }
556
557 // 'waitButton' is the default choice.
[email protected]e8b5f7882009-09-14 20:59:13558 int choice = NSRunAlertPanel(warningText, explanationText,
559 waitTitle, exitTitle, nil);
[email protected]73f5b362009-08-10 23:58:21560 return choice == NSAlertDefaultReturn ? YES : NO;
561}
562
563// Check all profiles for in progress downloads, and if we find any, prompt the
[email protected]09729a552009-08-03 23:21:41564// user to see if we should continue to exit (and thus cancel the downloads), or
565// if we should wait.
566- (BOOL)shouldQuitWithInProgressDownloads {
[email protected]73f5b362009-08-10 23:58:21567 ProfileManager* profile_manager = g_browser_process->profile_manager();
568 if (!profile_manager)
569 return YES;
[email protected]09729a552009-08-03 23:21:41570
[email protected]73f5b362009-08-10 23:58:21571 ProfileManager::const_iterator it = profile_manager->begin();
572 for (; it != profile_manager->end(); ++it) {
573 Profile* profile = *it;
[email protected]09729a552009-08-03 23:21:41574 DownloadManager* download_manager = profile->GetDownloadManager();
[email protected]73f5b362009-08-10 23:58:21575 if (download_manager && download_manager->in_progress_count() > 0) {
576 int downloadCount = download_manager->in_progress_count();
577 if ([self userWillWaitForInProgressDownloads:downloadCount]) {
578 // Create a new browser window (if necessary) and navigate to the
579 // downloads page if the user chooses to wait.
580 Browser* browser = BrowserList::FindBrowserWithProfile(profile);
581 if (!browser) {
582 browser = Browser::Create(profile);
583 browser->window()->Show();
584 }
585 DCHECK(browser);
586 browser->ShowDownloadsTab();
587 return NO;
588 }
[email protected]09729a552009-08-03 23:21:41589
[email protected]73f5b362009-08-10 23:58:21590 // User wants to exit.
591 return YES;
[email protected]09729a552009-08-03 23:21:41592 }
[email protected]09729a552009-08-03 23:21:41593 }
594
[email protected]73f5b362009-08-10 23:58:21595 // No profiles or active downloads found, okay to exit.
[email protected]09729a552009-08-03 23:21:41596 return YES;
597}
598
[email protected]fbc947b2009-06-19 13:28:24599// Called to determine if we should enable the "restore tab" menu item.
600// Checks with the TabRestoreService to see if there's anything there to
601// restore and returns YES if so.
602- (BOOL)canRestoreTab {
603 TabRestoreService* service = [self defaultProfile]->GetTabRestoreService();
604 return service && !service->entries().empty();
605}
606
[email protected]f6c15fa2010-06-04 20:08:21607// Returns true if there is not a modal window (either window- or application-
608// modal) blocking the active browser. Note that tab modal dialogs (HTTP auth
609// sheets) will not count as blocking the browser. But things like open/save
610// dialogs that are window modal will block the browser.
611- (BOOL)keyWindowIsNotModal {
[email protected]6a281332009-10-13 17:41:06612 Browser* browser = BrowserList::GetLastActive();
[email protected]dc76e8362010-06-11 16:29:52613 return [NSApp modalWindow] == nil && (!browser ||
[email protected]6a281332009-10-13 17:41:06614 ![[browser->window()->GetNativeHandle() attachedSheet]
[email protected]f6c15fa2010-06-04 20:08:21615 isKindOfClass:[NSWindow class]]);
[email protected]6a281332009-10-13 17:41:06616}
617
[email protected]88d74942009-01-21 22:04:44618// Called to validate menu items when there are no key windows. All the
619// items we care about have been set with the |commandDispatch:| action and
620// a target of FirstResponder in IB. If it's not one of those, let it
621// continue up the responder chain to be handled elsewhere. We pull out the
622// tag as the cross-platform constant to differentiate and dispatch the
623// various commands.
624- (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item {
625 SEL action = [item action];
626 BOOL enable = NO;
627 if (action == @selector(commandDispatch:)) {
628 NSInteger tag = [item tag];
[email protected]fbc947b2009-06-19 13:28:24629 if (menuState_->SupportsCommand(tag)) {
630 switch (tag) {
[email protected]419eb2a2009-11-11 16:26:26631 // The File Menu commands are not automatically disabled by Cocoa when a
632 // dialog sheet obscures the browser window, so we disable several of
633 // them here. We don't need to include IDC_CLOSE_WINDOW, because
634 // app_controller is only activated when there are no key windows (see
635 // function comment).
[email protected]fbc947b2009-06-19 13:28:24636 case IDC_RESTORE_TAB:
[email protected]f6c15fa2010-06-04 20:08:21637 enable = [self keyWindowIsNotModal] && [self canRestoreTab];
[email protected]6a281332009-10-13 17:41:06638 break;
[email protected]f6c15fa2010-06-04 20:08:21639 // Browser-level items that open in new tabs should not open if there's
640 // a window- or app-modal dialog.
[email protected]6a281332009-10-13 17:41:06641 case IDC_OPEN_FILE:
[email protected]6a281332009-10-13 17:41:06642 case IDC_NEW_TAB:
[email protected]f6c15fa2010-06-04 20:08:21643 case IDC_SHOW_HISTORY:
644 case IDC_SHOW_BOOKMARK_MANAGER:
645 enable = [self keyWindowIsNotModal];
646 break;
647 // Browser-level items that open in new windows.
648 case IDC_NEW_WINDOW:
649 case IDC_TASK_MANAGER:
650 // Allow the user to open a new window if there's a window-modal
651 // dialog.
652 enable = [self keyWindowIsNotModal] || ([NSApp modalWindow] == nil);
[email protected]fbc947b2009-06-19 13:28:24653 break;
[email protected]446e16a2010-05-03 21:23:58654 case IDC_SYNC_BOOKMARKS: {
655 Profile* defaultProfile = [self defaultProfile];
656 // The profile may be NULL during shutdown -- see
657 // http://code.google.com/p/chromium/issues/detail?id=43048 .
658 //
659 // TODO(akalin,viettrungluu): Figure out whether this method
660 // can be prevented from being called if defaultProfile is
661 // NULL.
662 if (!defaultProfile) {
663 LOG(WARNING)
664 << "NULL defaultProfile detected -- not doing anything";
665 break;
666 }
[email protected]d3b98c82010-07-14 07:45:59667 enable = defaultProfile->IsSyncAccessible() &&
668 [self keyWindowIsNotModal];
[email protected]446e16a2010-05-03 21:23:58669 sync_ui_util::UpdateSyncItem(item, enable, defaultProfile);
[email protected]f9bc9b92009-11-24 00:55:35670 break;
[email protected]446e16a2010-05-03 21:23:58671 }
[email protected]fbc947b2009-06-19 13:28:24672 default:
[email protected]f6c15fa2010-06-04 20:08:21673 enable = menuState_->IsCommandEnabled(tag) ?
674 [self keyWindowIsNotModal] : NO;
[email protected]fbc947b2009-06-19 13:28:24675 }
676 }
[email protected]3b6aa8b62009-09-15 21:36:11677 } else if (action == @selector(terminate:)) {
[email protected]88d74942009-01-21 22:04:44678 enable = YES;
[email protected]3111f08b2009-04-30 16:01:52679 } else if (action == @selector(showPreferences:)) {
680 enable = YES;
[email protected]bde3dda2009-05-20 22:13:07681 } else if (action == @selector(orderFrontStandardAboutPanel:)) {
682 enable = YES;
[email protected]3d4bd23f2009-10-06 03:58:38683 } else if (action == @selector(newWindowFromDock:)) {
684 enable = YES;
[email protected]88d74942009-01-21 22:04:44685 }
686 return enable;
687}
688
[email protected]947fc0d2010-01-14 22:33:05689// Called when the user picks a menu item when there are no key windows, or when
690// there is no foreground browser window. Calls through to the browser object to
691// execute the command. This assumes that the command is supported and doesn't
[email protected]d630d7d52010-02-21 00:55:11692// check, otherwise it should have been disabled in the UI in
693// |-validateUserInterfaceItem:|.
[email protected]88d74942009-01-21 22:04:44694- (void)commandDispatch:(id)sender {
[email protected]93e181762009-05-29 14:30:38695 Profile* defaultProfile = [self defaultProfile];
[email protected]f0a51fb52009-03-05 12:46:38696
[email protected]947fc0d2010-01-14 22:33:05697 // Handle the case where we're dispatching a command from a sender that's in a
698 // browser window. This means that the command came from a background window
699 // and is getting here because the foreground window is not a browser window.
700 if ([sender respondsToSelector:@selector(window)]) {
701 id delegate = [[sender window] windowController];
702 if ([delegate isKindOfClass:[BrowserWindowController class]]) {
703 [delegate commandDispatch:sender];
704 return;
705 }
706 }
707
[email protected]88d74942009-01-21 22:04:44708 NSInteger tag = [sender tag];
709 switch (tag) {
[email protected]fbc947b2009-06-19 13:28:24710 case IDC_NEW_TAB:
[email protected]d630d7d52010-02-21 00:55:11711 // Create a new tab in an existing browser window (which we activate) if
712 // possible.
713 if (Browser* browser = ActivateBrowser(defaultProfile)) {
714 browser->ExecuteCommand(IDC_NEW_TAB);
715 break;
716 }
717 // Else fall through to create new window.
[email protected]88d74942009-01-21 22:04:44718 case IDC_NEW_WINDOW:
[email protected]d630d7d52010-02-21 00:55:11719 CreateBrowser(defaultProfile);
720 break;
[email protected]91a5b3e2009-10-30 19:32:22721 case IDC_FOCUS_LOCATION:
[email protected]d630d7d52010-02-21 00:55:11722 ActivateOrCreateBrowser(defaultProfile)->
723 ExecuteCommand(IDC_FOCUS_LOCATION);
[email protected]88d74942009-01-21 22:04:44724 break;
[email protected]cddc5242009-12-07 18:54:33725 case IDC_FOCUS_SEARCH:
[email protected]d630d7d52010-02-21 00:55:11726 ActivateOrCreateBrowser(defaultProfile)->ExecuteCommand(IDC_FOCUS_SEARCH);
[email protected]cddc5242009-12-07 18:54:33727 break;
[email protected]863ff662009-01-26 20:18:18728 case IDC_NEW_INCOGNITO_WINDOW:
[email protected]e9e211272009-09-22 16:39:11729 Browser::OpenEmptyWindow(defaultProfile->GetOffTheRecordProfile());
[email protected]863ff662009-01-26 20:18:18730 break;
[email protected]fbc947b2009-06-19 13:28:24731 case IDC_RESTORE_TAB:
732 Browser::OpenWindowWithRestoredTabs(defaultProfile);
733 break;
[email protected]e19516d2009-04-28 17:15:19734 case IDC_OPEN_FILE:
[email protected]d630d7d52010-02-21 00:55:11735 CreateBrowser(defaultProfile)->ExecuteCommand(IDC_OPEN_FILE);
[email protected]e19516d2009-04-28 17:15:19736 break;
[email protected]1fdff702009-10-22 00:36:18737 case IDC_CLEAR_BROWSING_DATA: {
[email protected]93e181762009-05-29 14:30:38738 // There may not be a browser open, so use the default profile.
[email protected]049ac832009-12-15 16:26:54739 [ClearBrowsingDataController
740 showClearBrowsingDialogForProfile:defaultProfile];
[email protected]93e181762009-05-29 14:30:38741 break;
[email protected]1fdff702009-10-22 00:36:18742 }
[email protected]4aee5652009-11-19 18:59:21743 case IDC_IMPORT_SETTINGS: {
[email protected]896220042010-03-23 18:14:28744 UserMetrics::RecordAction(UserMetricsAction("Import_ShowDlg"),
745 defaultProfile);
[email protected]2989cce12010-02-01 10:56:53746 [ImportSettingsDialogController
747 showImportSettingsDialogForProfile:defaultProfile];
[email protected]4aee5652009-11-19 18:59:21748 break;
749 }
[email protected]af7c25e2010-01-23 05:46:45750 case IDC_SHOW_BOOKMARK_MANAGER:
[email protected]896220042010-03-23 18:14:28751 UserMetrics::RecordAction(UserMetricsAction("ShowBookmarkManager"),
752 defaultProfile);
[email protected]90a8bf252010-03-12 00:09:47753 if (Browser* browser = ActivateBrowser(defaultProfile)) {
[email protected]47e532b2010-04-23 17:46:40754 // Open a bookmark manager tab.
[email protected]90a8bf252010-03-12 00:09:47755 browser->OpenBookmarkManager();
756 } else {
[email protected]47e532b2010-04-23 17:46:40757 // No browser window, so create one for the bookmark manager tab.
758 Browser::OpenBookmarkManagerWindow(defaultProfile);
[email protected]90a8bf252010-03-12 00:09:47759 }
[email protected]af7c25e2010-01-23 05:46:45760 break;
[email protected]91a5b3e2009-10-30 19:32:22761 case IDC_SHOW_HISTORY:
[email protected]d630d7d52010-02-21 00:55:11762 if (Browser* browser = ActivateBrowser(defaultProfile))
763 browser->ShowHistoryTab();
764 else
765 Browser::OpenHistoryWindow(defaultProfile);
[email protected]91a5b3e2009-10-30 19:32:22766 break;
767 case IDC_SHOW_DOWNLOADS:
[email protected]d630d7d52010-02-21 00:55:11768 if (Browser* browser = ActivateBrowser(defaultProfile))
769 browser->ShowDownloadsTab();
770 else
771 Browser::OpenDownloadsWindow(defaultProfile);
[email protected]91a5b3e2009-10-30 19:32:22772 break;
[email protected]bb37e6ec2009-11-18 17:59:36773 case IDC_MANAGE_EXTENSIONS:
[email protected]d630d7d52010-02-21 00:55:11774 if (Browser* browser = ActivateBrowser(defaultProfile))
775 browser->ShowExtensionsTab();
776 else
777 Browser::OpenExtensionsWindow(defaultProfile);
[email protected]bb37e6ec2009-11-18 17:59:36778 break;
[email protected]1fdff702009-10-22 00:36:18779 case IDC_HELP_PAGE:
[email protected]d630d7d52010-02-21 00:55:11780 if (Browser* browser = ActivateBrowser(defaultProfile))
781 browser->OpenHelpTab();
782 else
783 Browser::OpenHelpWindow(defaultProfile);
[email protected]1fdff702009-10-22 00:36:18784 break;
[email protected]3896b182009-11-03 16:15:32785 case IDC_REPORT_BUG: {
786 Browser* browser = BrowserList::GetLastActive();
[email protected]d630d7d52010-02-21 00:55:11787 TabContents* currentTab =
788 browser ? browser->GetSelectedTabContents() : NULL;
[email protected]3896b182009-11-03 16:15:32789 BugReportWindowController* controller =
790 [[BugReportWindowController alloc]
[email protected]d630d7d52010-02-21 00:55:11791 initWithTabContents:currentTab
792 profile:[self defaultProfile]];
[email protected]3896b182009-11-03 16:15:32793 [controller runModalDialog];
794 break;
795 }
[email protected]f9bc9b92009-11-24 00:55:35796 case IDC_SYNC_BOOKMARKS:
[email protected]446e16a2010-05-03 21:23:58797 // The profile may be NULL during shutdown -- see
798 // http://code.google.com/p/chromium/issues/detail?id=43048 .
799 //
800 // TODO(akalin,viettrungluu): Figure out whether this method can
801 // be prevented from being called if defaultProfile is NULL.
802 if (!defaultProfile) {
803 LOG(WARNING) << "NULL defaultProfile detected -- not doing anything";
804 break;
805 }
[email protected]f9bc9b92009-11-24 00:55:35806 // TODO(akalin): Add a constant to denote starting sync from the
807 // main menu and use that instead of START_FROM_WRENCH.
[email protected]40f047972009-11-25 03:54:40808 sync_ui_util::OpenSyncMyBookmarksDialog(
[email protected]f9bc9b92009-11-24 00:55:35809 defaultProfile, ProfileSyncService::START_FROM_WRENCH);
810 break;
[email protected]ceaaf1812010-01-20 22:13:09811 case IDC_TASK_MANAGER:
[email protected]896220042010-03-23 18:14:28812 UserMetrics::RecordAction(UserMetricsAction("TaskManager"),
813 defaultProfile);
[email protected]ceaaf1812010-01-20 22:13:09814 TaskManagerMac::Show();
815 break;
[email protected]4a42d272010-06-18 01:29:42816 case IDC_OPTIONS:
817 [self showPreferences:sender];
818 break;
[email protected]d630d7d52010-02-21 00:55:11819 }
[email protected]88d74942009-01-21 22:04:44820}
821
[email protected]947fc0d2010-01-14 22:33:05822// Same as |-commandDispatch:|, but executes commands using a disposition
823// determined by the key flags. This will get called in the case where the
824// frontmost window is not a browser window, and the user has command-clicked
825// a button in a background browser window whose action is
826// |-commandDispatchUsingKeyModifiers:|
827- (void)commandDispatchUsingKeyModifiers:(id)sender {
828 DCHECK(sender);
829 if ([sender respondsToSelector:@selector(window)]) {
830 id delegate = [[sender window] windowController];
831 if ([delegate isKindOfClass:[BrowserWindowController class]]) {
832 [delegate commandDispatchUsingKeyModifiers:sender];
833 }
834 }
835}
836
[email protected]ff81e0a2009-04-13 14:58:54837// NSApplication delegate method called when someone clicks on the
838// dock icon and there are no open windows. To match standard mac
839// behavior, we should open a new window.
840- (BOOL)applicationShouldHandleReopen:(NSApplication*)theApplication
841 hasVisibleWindows:(BOOL)flag {
[email protected]70631402010-04-26 20:16:54842 // If the browser is currently trying to quit, don't do anything and return NO
843 // to prevent AppKit from doing anything.
844 // TODO(rohitrao): Remove this code when http://crbug.com/40861 is resolved.
845 if (browser_shutdown::IsTryingToQuit())
846 return NO;
847
[email protected]ff81e0a2009-04-13 14:58:54848 // Don't do anything if there are visible windows. This will cause
849 // AppKit to unminimize the most recently minimized window.
850 if (flag)
851 return YES;
852
[email protected]0d1210d62010-09-07 17:31:38853 // If launched as a hidden login item (due to installation of a persistent app
854 // or by the user, for example in System Preferenecs->Accounts->Login Items),
855 // allow session to be restored first time the user clicks on a Dock icon.
856 // Normally, it'd just open a new empty page.
857 {
858 static BOOL doneOnce = NO;
859 if (!doneOnce) {
860 doneOnce = YES;
861 if (mac_util::WasLaunchedAsHiddenLoginItem()) {
862 SessionService* sessionService =
863 [self defaultProfile]->GetSessionService();
864 if (sessionService &&
865 sessionService->RestoreIfNecessary(std::vector<GURL>()))
866 return NO;
867 }
868 }
869 }
[email protected]ff81e0a2009-04-13 14:58:54870 // Otherwise open a new window.
[email protected]d630d7d52010-02-21 00:55:11871 {
[email protected]0fbd70332010-06-01 19:28:34872 AutoReset<bool> auto_reset_in_run(&g_is_opening_new_window, true);
[email protected]d630d7d52010-02-21 00:55:11873 Browser::OpenEmptyWindow([self defaultProfile]);
874 }
[email protected]ff81e0a2009-04-13 14:58:54875
876 // We've handled the reopen event, so return NO to tell AppKit not
877 // to do anything.
878 return NO;
879}
880
[email protected]88d74942009-01-21 22:04:44881- (void)initMenuState {
[email protected]3111f08b2009-04-30 16:01:52882 menuState_.reset(new CommandUpdater(NULL));
[email protected]fbc947b2009-06-19 13:28:24883 menuState_->UpdateCommandEnabled(IDC_NEW_TAB, true);
[email protected]88d74942009-01-21 22:04:44884 menuState_->UpdateCommandEnabled(IDC_NEW_WINDOW, true);
[email protected]863ff662009-01-26 20:18:18885 menuState_->UpdateCommandEnabled(IDC_NEW_INCOGNITO_WINDOW, true);
[email protected]e19516d2009-04-28 17:15:19886 menuState_->UpdateCommandEnabled(IDC_OPEN_FILE, true);
[email protected]93e181762009-05-29 14:30:38887 menuState_->UpdateCommandEnabled(IDC_CLEAR_BROWSING_DATA, true);
[email protected]fbc947b2009-06-19 13:28:24888 menuState_->UpdateCommandEnabled(IDC_RESTORE_TAB, false);
[email protected]91a5b3e2009-10-30 19:32:22889 menuState_->UpdateCommandEnabled(IDC_FOCUS_LOCATION, true);
[email protected]cddc5242009-12-07 18:54:33890 menuState_->UpdateCommandEnabled(IDC_FOCUS_SEARCH, true);
[email protected]af7c25e2010-01-23 05:46:45891 menuState_->UpdateCommandEnabled(IDC_SHOW_BOOKMARK_MANAGER, true);
[email protected]91a5b3e2009-10-30 19:32:22892 menuState_->UpdateCommandEnabled(IDC_SHOW_HISTORY, true);
893 menuState_->UpdateCommandEnabled(IDC_SHOW_DOWNLOADS, true);
[email protected]bb37e6ec2009-11-18 17:59:36894 menuState_->UpdateCommandEnabled(IDC_MANAGE_EXTENSIONS, true);
[email protected]1fdff702009-10-22 00:36:18895 menuState_->UpdateCommandEnabled(IDC_HELP_PAGE, true);
[email protected]4aee5652009-11-19 18:59:21896 menuState_->UpdateCommandEnabled(IDC_IMPORT_SETTINGS, true);
[email protected]3896b182009-11-03 16:15:32897 menuState_->UpdateCommandEnabled(IDC_REPORT_BUG, true);
[email protected]d3b98c82010-07-14 07:45:59898 menuState_->UpdateCommandEnabled(IDC_SYNC_BOOKMARKS, true);
[email protected]ceaaf1812010-01-20 22:13:09899 menuState_->UpdateCommandEnabled(IDC_TASK_MANAGER, true);
[email protected]88d74942009-01-21 22:04:44900}
[email protected]1bcdb532009-01-16 17:47:57901
[email protected]eef99c22010-08-17 05:55:16902- (void)registerServicesMenuTypesTo:(NSApplication*)app {
[email protected]09ea24c2010-08-23 19:05:41903 // Note that RenderWidgetHostViewCocoa implements NSServicesRequests which
904 // handles requests from services.
905 NSArray* types = [NSArray arrayWithObjects:NSStringPboardType, nil];
906 [app registerServicesMenuSendTypes:types returnTypes:types];
[email protected]eef99c22010-08-17 05:55:16907}
908
[email protected]3f34599d2009-03-25 22:11:43909- (Profile*)defaultProfile {
[email protected]3f34599d2009-03-25 22:11:43910 // TODO(jrg): Find a better way to get the "default" profile.
911 if (g_browser_process->profile_manager())
[email protected]065566b2010-01-20 20:38:26912 return *g_browser_process->profile_manager()->begin();
[email protected]3f34599d2009-03-25 22:11:43913
[email protected]f6314002009-04-23 01:18:13914 return NULL;
[email protected]3f34599d2009-03-25 22:11:43915}
916
[email protected]57750f822009-04-21 21:43:09917// Various methods to open URLs that we get in a native fashion. We use
918// BrowserInit here because on the other platforms, URLs to open come through
919// the ProcessSingleton, and it calls BrowserInit. It's best to bottleneck the
920// openings through that for uniform handling.
921
[email protected]d630d7d52010-02-21 00:55:11922- (void)openUrls:(const std::vector<GURL>&)urls {
[email protected]caadfca2010-01-22 21:51:21923 // If the browser hasn't started yet, just queue up the URLs.
924 if (!startupComplete_) {
[email protected]d630d7d52010-02-21 00:55:11925 startupUrls_.insert(startupUrls_.end(), urls.begin(), urls.end());
[email protected]136140c2009-05-19 13:58:25926 return;
927 }
[email protected]57750f822009-04-21 21:43:09928
[email protected]0e863512009-05-28 19:45:07929 Browser* browser = BrowserList::GetLastActive();
930 // if no browser window exists then create one with no tabs to be filled in
931 if (!browser) {
932 browser = Browser::Create([self defaultProfile]);
[email protected]ce560f82009-06-03 09:39:44933 browser->window()->Show();
[email protected]0e863512009-05-28 19:45:07934 }
935
[email protected]51343d5a2009-10-26 22:39:33936 CommandLine dummy(CommandLine::ARGUMENTS_ONLY);
[email protected]f805fe82010-08-03 22:47:10937 BrowserInit::LaunchWithProfile launch(FilePath(), dummy);
[email protected]0e863512009-05-28 19:45:07938 launch.OpenURLsInBrowser(browser, false, urls);
[email protected]57750f822009-04-21 21:43:09939}
940
[email protected]57750f822009-04-21 21:43:09941- (void)getUrl:(NSAppleEventDescriptor*)event
942 withReply:(NSAppleEventDescriptor*)reply {
943 NSString* urlStr = [[event paramDescriptorForKeyword:keyDirectObject]
944 stringValue];
945
946 GURL gurl(base::SysNSStringToUTF8(urlStr));
947 std::vector<GURL> gurlVector;
948 gurlVector.push_back(gurl);
949
[email protected]d630d7d52010-02-21 00:55:11950 [self openUrls:gurlVector];
[email protected]57750f822009-04-21 21:43:09951}
952
[email protected]caadfca2010-01-22 21:51:21953- (void)application:(NSApplication*)sender
954 openFiles:(NSArray*)filenames {
[email protected]57750f822009-04-21 21:43:09955 std::vector<GURL> gurlVector;
[email protected]caadfca2010-01-22 21:51:21956 for (NSString* file in filenames) {
957 GURL gurl = net::FilePathToFileURL(FilePath(base::SysNSStringToUTF8(file)));
[email protected]57750f822009-04-21 21:43:09958 gurlVector.push_back(gurl);
959 }
[email protected]caadfca2010-01-22 21:51:21960 if (!gurlVector.empty())
[email protected]d630d7d52010-02-21 00:55:11961 [self openUrls:gurlVector];
[email protected]caadfca2010-01-22 21:51:21962 else
963 NOTREACHED() << "Nothing to open!";
[email protected]57750f822009-04-21 21:43:09964
[email protected]caadfca2010-01-22 21:51:21965 [sender replyToOpenOrPrint:NSApplicationDelegateReplySuccess];
[email protected]57750f822009-04-21 21:43:09966}
[email protected]3f34599d2009-03-25 22:11:43967
[email protected]767543d2009-04-30 19:23:58968// Called when the preferences window is closed. We use this to release the
969// window controller.
[email protected]d4e18652009-11-19 21:52:16970- (void)prefsWindowClosed:(NSNotification*)notification {
971 NSWindow* window = [prefsController_ window];
972 DCHECK([notification object] == window);
973 NSNotificationCenter* defaultCenter = [NSNotificationCenter defaultCenter];
974 [defaultCenter removeObserver:self
975 name:NSWindowWillCloseNotification
976 object:window];
977 // PreferencesWindowControllers are autoreleased in
978 // -[PreferencesWindowController windowWillClose:].
979 prefsController_ = nil;
[email protected]767543d2009-04-30 19:23:58980}
981
[email protected]3111f08b2009-04-30 16:01:52982// Show the preferences window, or bring it to the front if it's already
983// visible.
984- (IBAction)showPreferences:(id)sender {
[email protected]4a42d272010-06-18 01:29:42985 const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess();
986 if (parsed_command_line.HasSwitch(switches::kEnableTabbedOptions)) {
987 if (Browser* browser = ActivateBrowser([self defaultProfile])) {
988 // Show options tab in the active browser window.
[email protected]024617a2010-08-20 05:08:05989 browser->ShowOptionsTab(chrome::kDefaultOptionsSubPage);
[email protected]4a42d272010-06-18 01:29:42990 } else {
991 // No browser window, so create one for the options tab.
992 Browser::OpenOptionsWindow([self defaultProfile]);
993 }
994 } else {
995 [self showPreferencesWindow:sender
996 page:OPTIONS_PAGE_DEFAULT
997 profile:[self defaultProfile]];
998 }
[email protected]d989f07c2009-11-14 00:35:46999}
1000
1001- (void)showPreferencesWindow:(id)sender
1002 page:(OptionsPage)page
1003 profile:(Profile*)profile {
[email protected]d4e18652009-11-19 21:52:161004 if (prefsController_) {
[email protected]d989f07c2009-11-14 00:35:461005 [prefsController_ switchToPage:page animate:YES];
1006 } else {
[email protected]d4e18652009-11-19 21:52:161007 prefsController_ =
1008 [[PreferencesWindowController alloc] initWithProfile:profile
1009 initialPage:page];
[email protected]767543d2009-04-30 19:23:581010 // Watch for a notification of when it goes away so that we can destroy
1011 // the controller.
1012 [[NSNotificationCenter defaultCenter]
1013 addObserver:self
1014 selector:@selector(prefsWindowClosed:)
[email protected]d4e18652009-11-19 21:52:161015 name:NSWindowWillCloseNotification
1016 object:[prefsController_ window]];
[email protected]42404382009-04-30 17:59:241017 }
1018 [prefsController_ showPreferences:sender];
[email protected]3111f08b2009-04-30 16:01:521019}
1020
[email protected]bde3dda2009-05-20 22:13:071021// Called when the about window is closed. We use this to release the
1022// window controller.
[email protected]d4e18652009-11-19 21:52:161023- (void)aboutWindowClosed:(NSNotification*)notification {
1024 NSWindow* window = [aboutController_ window];
1025 DCHECK(window == [notification object]);
[email protected]bde3dda2009-05-20 22:13:071026 [[NSNotificationCenter defaultCenter]
[email protected]49aeee52009-10-26 19:58:131027 removeObserver:self
[email protected]d4e18652009-11-19 21:52:161028 name:NSWindowWillCloseNotification
1029 object:window];
1030 // AboutWindowControllers are autoreleased in
1031 // -[AboutWindowController windowWillClose:].
1032 aboutController_ = nil;
[email protected]bde3dda2009-05-20 22:13:071033}
1034
1035- (IBAction)orderFrontStandardAboutPanel:(id)sender {
[email protected]bde3dda2009-05-20 22:13:071036 if (!aboutController_) {
[email protected]d4e18652009-11-19 21:52:161037 aboutController_ =
1038 [[AboutWindowController alloc] initWithProfile:[self defaultProfile]];
[email protected]49aeee52009-10-26 19:58:131039
[email protected]bde3dda2009-05-20 22:13:071040 // Watch for a notification of when it goes away so that we can destroy
1041 // the controller.
1042 [[NSNotificationCenter defaultCenter]
1043 addObserver:self
1044 selector:@selector(aboutWindowClosed:)
[email protected]d4e18652009-11-19 21:52:161045 name:NSWindowWillCloseNotification
1046 object:[aboutController_ window]];
[email protected]bde3dda2009-05-20 22:13:071047 }
[email protected]49aeee52009-10-26 19:58:131048
[email protected]bde3dda2009-05-20 22:13:071049 [aboutController_ showWindow:self];
[email protected]bde3dda2009-05-20 22:13:071050}
1051
[email protected]3d4bd23f2009-10-06 03:58:381052// Explicitly bring to the foreground when creating new windows from the dock.
1053- (void)newWindowFromDock:(id)sender {
1054 [NSApp activateIgnoringOtherApps:YES];
1055 [self commandDispatch:sender];
1056}
1057
[email protected]227fcee2010-06-11 19:19:371058- (NSMenu*)applicationDockMenu:(NSApplication*)sender {
[email protected]018a3962009-09-17 22:23:441059 NSMenu* dockMenu = [[[NSMenu alloc] initWithTitle: @""] autorelease];
1060 NSString* titleStr = l10n_util::GetNSStringWithFixup(IDS_NEW_WINDOW_MAC);
1061 scoped_nsobject<NSMenuItem> item([[NSMenuItem alloc]
1062 initWithTitle:titleStr
[email protected]3d4bd23f2009-10-06 03:58:381063 action:@selector(newWindowFromDock:)
[email protected]018a3962009-09-17 22:23:441064 keyEquivalent:@""]);
[email protected]288bfcd32009-09-14 18:14:461065 [item setTarget:self];
1066 [item setTag:IDC_NEW_WINDOW];
[email protected]018a3962009-09-17 22:23:441067 [dockMenu addItem:item];
[email protected]288bfcd32009-09-14 18:14:461068
1069 titleStr = l10n_util::GetNSStringWithFixup(IDS_NEW_INCOGNITO_WINDOW_MAC);
[email protected]018a3962009-09-17 22:23:441070 item.reset([[NSMenuItem alloc] initWithTitle:titleStr
[email protected]3d4bd23f2009-10-06 03:58:381071 action:@selector(newWindowFromDock:)
[email protected]018a3962009-09-17 22:23:441072 keyEquivalent:@""]);
[email protected]288bfcd32009-09-14 18:14:461073 [item setTarget:self];
1074 [item setTag:IDC_NEW_INCOGNITO_WINDOW];
[email protected]018a3962009-09-17 22:23:441075 [dockMenu addItem:item];
[email protected]288bfcd32009-09-14 18:14:461076
[email protected]018a3962009-09-17 22:23:441077 return dockMenu;
[email protected]288bfcd32009-09-14 18:14:461078}
1079
[email protected]d630d7d52010-02-21 00:55:111080- (const std::vector<GURL>&)startupUrls {
1081 return startupUrls_;
[email protected]caadfca2010-01-22 21:51:211082}
1083
[email protected]d630d7d52010-02-21 00:55:111084- (void)clearStartupUrls {
1085 startupUrls_.clear();
[email protected]caadfca2010-01-22 21:51:211086}
1087
[email protected]d630d7d52010-02-21 00:55:111088@end // @implementation AppController
[email protected]60ad3e22009-09-18 21:07:191089
1090//---------------------------------------------------------------------------
1091
[email protected]60ad3e22009-09-18 21:07:191092void ShowOptionsWindow(OptionsPage page,
1093 OptionsGroup highlight_group,
1094 Profile* profile) {
[email protected]d989f07c2009-11-14 00:35:461095 // TODO(akalin): Use highlight_group.
1096 AppController* appController = [NSApp delegate];
1097 [appController showPreferencesWindow:nil page:page profile:profile];
[email protected]60ad3e22009-09-18 21:07:191098}
[email protected]216e9042009-10-29 17:05:231099
1100namespace app_controller_mac {
1101
1102bool IsOpeningNewWindow() {
1103 return g_is_opening_new_window;
1104}
1105
1106} // namespace app_controller_mac