blob: c215d9cb42d87ef353052a5db2795bca2400dfbd [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]288bfcd32009-09-14 18:14:467#include "app/l10n_util_mac.h"
[email protected]d630d7d52010-02-21 00:55:118#include "base/auto_reset.h"
[email protected]57750f822009-04-21 21:43:099#include "base/command_line.h"
[email protected]09729a552009-08-03 23:21:4110#include "base/mac_util.h"
[email protected]2c47bc12009-04-10 20:14:0011#include "base/message_loop.h"
[email protected]57750f822009-04-21 21:43:0912#include "base/sys_string_conversions.h"
[email protected]2c47bc12009-04-10 20:14:0013#include "chrome/app/chrome_dll_resource.h"
14#include "chrome/browser/browser.h"
[email protected]57750f822009-04-21 21:43:0915#include "chrome/browser/browser_init.h"
[email protected]2c47bc12009-04-10 20:14:0016#include "chrome/browser/browser_list.h"
[email protected]5c238752009-06-13 10:29:0717#include "chrome/browser/browser_process.h"
[email protected]41741a962009-02-18 21:51:3918#include "chrome/browser/browser_shutdown.h"
[email protected]ce560f82009-06-03 09:39:4419#include "chrome/browser/browser_window.h"
[email protected]12dc3d42010-02-22 23:37:1220#include "chrome/browser/chrome_thread.h"
[email protected]bde3dda2009-05-20 22:13:0721#import "chrome/browser/cocoa/about_window_controller.h"
[email protected]3f34599d2009-03-25 22:11:4322#import "chrome/browser/cocoa/bookmark_menu_bridge.h"
[email protected]a9e8afc2009-08-11 22:03:1723#import "chrome/browser/cocoa/browser_window_cocoa.h"
24#import "chrome/browser/cocoa/browser_window_controller.h"
[email protected]3896b182009-11-03 16:15:3225#import "chrome/browser/cocoa/bug_report_window_controller.h"
[email protected]93e181762009-05-29 14:30:3826#import "chrome/browser/cocoa/clear_browsing_data_controller.h"
[email protected]2bcec612009-05-14 17:50:5327#import "chrome/browser/cocoa/encoding_menu_controller_delegate_mac.h"
[email protected]4aee5652009-11-19 18:59:2128#import "chrome/browser/cocoa/history_menu_bridge.h"
29#import "chrome/browser/cocoa/import_settings_dialog.h"
[email protected]42404382009-04-30 17:59:2430#import "chrome/browser/cocoa/preferences_window_controller.h"
[email protected]449dd2f2009-05-27 13:04:0031#import "chrome/browser/cocoa/tab_strip_controller.h"
32#import "chrome/browser/cocoa/tab_window_controller.h"
[email protected]ceaaf1812010-01-20 22:13:0933#include "chrome/browser/cocoa/task_manager_mac.h"
[email protected]2c47bc12009-04-10 20:14:0034#include "chrome/browser/command_updater.h"
[email protected]09729a552009-08-03 23:21:4135#include "chrome/browser/download/download_manager.h"
[email protected]4aee5652009-11-19 18:59:2136#include "chrome/browser/metrics/user_metrics.h"
[email protected]60ad3e22009-09-18 21:07:1937#include "chrome/browser/options_window.h"
[email protected]052313b2010-02-19 09:43:0838#include "chrome/browser/pref_service.h"
39#include "chrome/browser/profile_manager.h"
[email protected]fbc947b2009-06-19 13:28:2440#include "chrome/browser/sessions/tab_restore_service.h"
[email protected]f9bc9b92009-11-24 00:55:3541#include "chrome/browser/sync/profile_sync_service.h"
[email protected]40f047972009-11-25 03:54:4042#include "chrome/browser/sync/sync_ui_util.h"
43#include "chrome/browser/sync/sync_ui_util_mac.h"
[email protected]60ad3e22009-09-18 21:07:1944#include "chrome/browser/tab_contents/tab_contents.h"
[email protected]12dc3d42010-02-22 23:37:1245#include "chrome/common/app_mode_constants_mac.h"
46#include "chrome/common/chrome_paths_internal.h"
[email protected]f4acfae82009-09-11 00:33:3847#include "chrome/common/chrome_switches.h"
[email protected]a96ec6a2009-11-04 17:27:0848#include "chrome/common/notification_service.h"
[email protected]cd63ef62009-05-06 19:41:3749#include "chrome/common/pref_names.h"
[email protected]9dcb59662009-09-23 01:15:5050#include "grit/chromium_strings.h"
[email protected]09729a552009-08-03 23:21:4151#include "grit/generated_resources.h"
[email protected]caadfca2010-01-22 21:51:2152#include "net/base/net_util.h"
[email protected]88d74942009-01-21 22:04:4453
[email protected]f2c20fa2009-12-01 17:42:0254// 10.6 adds a public API for the Spotlight-backed search menu item in the Help
55// menu. Provide the declaration so it can be called below when building with
56// the 10.5 SDK.
57#if !defined(MAC_OS_X_VERSION_10_6) || \
58 MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6
59@interface NSApplication (SnowLeopardSDKDeclarations)
[email protected]caadfca2010-01-22 21:51:2160- (void)setHelpMenu:(NSMenu*)helpMenu;
[email protected]f2c20fa2009-12-01 17:42:0261@end
62#endif
63
[email protected]d630d7d52010-02-21 00:55:1164namespace {
65
66// True while AppController is calling Browser::OpenEmptyWindow(). We need a
67// global flag here, analogue to BrowserInit::InProcessStartup() because
68// otherwise the SessionService will try to restore sessions when we make a new
69// window while there are no other active windows.
70bool g_is_opening_new_window = false;
71
72// Activates a browser window having the given profile (the last one active) if
73// possible and returns a pointer to the activate |Browser| or NULL if this was
74// not possible. If the last active browser is minimized (in particular, if
75// there are only minimized windows), it will unminimize it.
76Browser* ActivateBrowser(Profile* profile) {
77 Browser* browser = BrowserList::GetLastActiveWithProfile(profile);
78 if (browser)
79 browser->window()->Activate();
80 return browser;
81}
82
83// Creates an empty browser window with the given profile and returns a pointer
84// to the new |Browser|.
85Browser* CreateBrowser(Profile* profile) {
86 {
87 AutoReset auto_reset_in_run(&g_is_opening_new_window, true);
88 Browser::OpenEmptyWindow(profile);
89 }
90
91 Browser* browser = BrowserList::GetLastActive();
92 CHECK(browser);
93 return browser;
94}
95
96// Activates a browser window having the given profile (the last one active) if
97// possible or creates an empty one if necessary. Returns a pointer to the
98// activated/new |Browser|.
99Browser* ActivateOrCreateBrowser(Profile* profile) {
100 if (Browser* browser = ActivateBrowser(profile))
101 return browser;
102 return CreateBrowser(profile);
103}
104
[email protected]12dc3d42010-02-22 23:37:12105// This task synchronizes preferences (under "org.chromium.Chromium" or
106// "com.google.Chrome"), in particular, writes them out to disk.
107class PrefsSyncTask : public Task {
108 public:
109 PrefsSyncTask() {}
110 virtual ~PrefsSyncTask() {}
111 virtual void Run() {
112 if (!CFPreferencesAppSynchronize(app_mode::kAppPrefsID))
113 LOG(WARNING) << "Error recording application bundle path.";
114 }
115};
116
117// Record the location of the application bundle (containing the main framework)
118// from which Chromium was loaded. This is used by app mode shims to find
119// Chromium.
120void RecordLastRunAppBundlePath() {
121 // Going up three levels from |chrome::GetVersionedDirectory()| gives the
122 // real, user-visible app bundle directory. (The alternatives give either the
123 // framework's path or the initial app's path, which may be an app mode shim
124 // or a unit test.)
125 FilePath appBundlePath =
126 chrome::GetVersionedDirectory().DirName().DirName().DirName();
127 CFPreferencesSetAppValue(app_mode::kLastRunAppBundlePathPrefsKey,
128 base::SysUTF8ToCFStringRef(appBundlePath.value()),
129 app_mode::kAppPrefsID);
130
131 // Sync after a delay avoid I/O contention on startup; 1500 ms is plenty.
132 ChromeThread::PostDelayedTask(ChromeThread::FILE, FROM_HERE,
133 new PrefsSyncTask(), 1500);
134}
135
[email protected]d630d7d52010-02-21 00:55:11136} // anonymous namespace
137
138@interface AppController(Private)
[email protected]88d74942009-01-21 22:04:44139- (void)initMenuState;
[email protected]7dc8c6b2010-04-09 17:02:50140- (void)handleQuitEvent:(NSAppleEventDescriptor*)event
141 withReply:(NSAppleEventDescriptor*)reply;
[email protected]d630d7d52010-02-21 00:55:11142- (void)openUrls:(const std::vector<GURL>&)urls;
[email protected]57750f822009-04-21 21:43:09143- (void)getUrl:(NSAppleEventDescriptor*)event
144 withReply:(NSAppleEventDescriptor*)reply;
[email protected]449dd2f2009-05-27 13:04:00145- (void)windowLayeringDidChange:(NSNotification*)inNotification;
[email protected]73f5b362009-08-10 23:58:21146- (BOOL)userWillWaitForInProgressDownloads:(int)downloadCount;
[email protected]09729a552009-08-03 23:21:41147- (BOOL)shouldQuitWithInProgressDownloads;
[email protected]d989f07c2009-11-14 00:35:46148- (void)showPreferencesWindow:(id)sender
149 page:(OptionsPage)page
150 profile:(Profile*)profile;
[email protected]88d74942009-01-21 22:04:44151@end
[email protected]1bcdb532009-01-16 17:47:57152
153@implementation AppController
154
[email protected]caadfca2010-01-22 21:51:21155@synthesize startupComplete = startupComplete_;
156
[email protected]cd63ef62009-05-06 19:41:37157// This method is called very early in application startup (ie, before
158// the profile is loaded or any preferences have been registered). Defer any
159// user-data initialization until -applicationDidFinishLaunching:.
[email protected]88d74942009-01-21 22:04:44160- (void)awakeFromNib {
[email protected]136140c2009-05-19 13:58:25161 // We need to register the handlers early to catch events fired on launch.
[email protected]57750f822009-04-21 21:43:09162 NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager];
163 [em setEventHandler:self
164 andSelector:@selector(getUrl:withReply:)
165 forEventClass:kInternetEventClass
166 andEventID:kAEGetURL];
167 [em setEventHandler:self
168 andSelector:@selector(getUrl:withReply:)
169 forEventClass:'WWW!' // A particularly ancient AppleEvent that dates
170 andEventID:'OURL']; // back to the Spyglass days.
[email protected]cd63ef62009-05-06 19:41:37171
[email protected]449dd2f2009-05-27 13:04:00172 // Register for various window layering changes. We use these to update
173 // various UI elements (command-key equivalents, etc) when the frontmost
174 // window changes.
175 NSNotificationCenter* notificationCenter =
176 [NSNotificationCenter defaultCenter];
[email protected]ce560f82009-06-03 09:39:44177 [notificationCenter
[email protected]449dd2f2009-05-27 13:04:00178 addObserver:self
179 selector:@selector(windowLayeringDidChange:)
180 name:NSWindowDidBecomeKeyNotification
181 object:nil];
182 [notificationCenter
183 addObserver:self
184 selector:@selector(windowLayeringDidChange:)
185 name:NSWindowDidResignKeyNotification
186 object:nil];
187 [notificationCenter
188 addObserver:self
189 selector:@selector(windowLayeringDidChange:)
190 name:NSWindowDidBecomeMainNotification
191 object:nil];
192 [notificationCenter
193 addObserver:self
194 selector:@selector(windowLayeringDidChange:)
195 name:NSWindowDidResignMainNotification
196 object:nil];
197
198 // Register for a notification that the number of tabs changes in windows
199 // so we can adjust the close tab/window command keys.
200 [notificationCenter
201 addObserver:self
202 selector:@selector(tabsChanged:)
203 name:kTabStripNumberOfTabsChanged
204 object:nil];
205
[email protected]136140c2009-05-19 13:58:25206 // Set up the command updater for when there are no windows open
207 [self initMenuState];
[email protected]f4acfae82009-09-11 00:33:38208
209 // Activate (bring to foreground) if asked to do so. On
210 // Windows this logic isn't necessary since
211 // BrowserWindow::Activate() calls ::SetForegroundWindow() which is
212 // adequate. On Mac, BrowserWindow::Activate() calls -[NSWindow
213 // makeKeyAndOrderFront:] which does not activate the application
214 // itself.
215 const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess();
216 if (parsed_command_line.HasSwitch(switches::kActivateOnLaunch)) {
217 [NSApp activateIgnoringOtherApps:YES];
218 }
[email protected]136140c2009-05-19 13:58:25219}
220
[email protected]7dc8c6b2010-04-09 17:02:50221// (NSApplicationDelegate protocol) This is the Apple-approved place to override
222// the default handlers.
223- (void)applicationWillFinishLaunching:(NSNotification*)notification {
224 NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager];
225 [em setEventHandler:self
226 andSelector:@selector(handleQuitEvent:withReply:)
227 forEventClass:kCoreEventClass
228 andEventID:kAEQuitApplication];
229}
[email protected]a9e8afc2009-08-11 22:03:17230
[email protected]7dc8c6b2010-04-09 17:02:50231// (NSApplicationDelegate protocol) Our mechanism for application termination
232// does not go through |-applicationShouldTerminate:|, so it should not be
233// called. In a release build, cancelling termination will prevent a crash (but
234// if things go really wrong, the user may have to force-terminate the
235// application).
236- (NSApplicationTerminateReply)applicationShouldTerminate:
237 (NSApplication*)sender {
238 NOTREACHED();
239 return NSTerminateCancel;
240}
241
242- (BOOL)tryToTerminateApplication:(NSApplication*)app {
243 // Set the state to "trying to quit", so that closing all browser windows will
244 // lead to termination.
245 browser_shutdown::SetTryingToQuit(true);
246
247 // TODO(viettrungluu): Remove Apple Event handlers here? (It's safe to leave
248 // them in, but I'm not sure about UX; we'd also want to disable other things
249 // though.) http://crbug.com/40861
250
251 if (!BrowserList::size())
252 return YES;
253
254 // Try to close all the windows.
255 BrowserList::CloseAllBrowsers(true);
256
257 return NO;
258}
259
260- (void)stopTryingToTerminateApplication:(NSApplication*)app {
261 if (browser_shutdown::IsTryingToQuit()) {
262 // Reset the "trying to quit" state, so that closing all browser windows
263 // will no longer lead to termination.
264 browser_shutdown::SetTryingToQuit(false);
265
266 // TODO(viettrungluu): Were we to remove Apple Event handlers above, we
267 // would have to reinstall them here. http://crbug.com/40861
268 }
[email protected]a9e8afc2009-08-11 22:03:17269}
270
[email protected]449dd2f2009-05-27 13:04:00271// Called when the app is shutting down. Clean-up as appropriate.
[email protected]7dc8c6b2010-04-09 17:02:50272- (void)applicationWillTerminate:(NSNotification*)aNotification {
[email protected]3b6aa8b62009-09-15 21:36:11273 NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager];
274 [em removeEventHandlerForEventClass:kInternetEventClass
275 andEventID:kAEGetURL];
276 [em removeEventHandlerForEventClass:'WWW!'
277 andEventID:'OURL'];
[email protected]3b6aa8b62009-09-15 21:36:11278
[email protected]7dc8c6b2010-04-09 17:02:50279 // There better be no browser windows left at this point.
280 CHECK_EQ(BrowserList::size(), 0u);
[email protected]3b6aa8b62009-09-15 21:36:11281
282 // Release the reference to the browser process. Once all the browsers get
283 // dealloc'd, it will stop the RunLoop and fall back into main().
284 g_browser_process->ReleaseModule();
[email protected]fbc947b2009-06-19 13:28:24285
[email protected]d4e18652009-11-19 21:52:16286 // Close these off if they have open windows.
287 [prefsController_ close];
288 [aboutController_ close];
289
[email protected]449dd2f2009-05-27 13:04:00290 [[NSNotificationCenter defaultCenter] removeObserver:self];
291}
292
[email protected]3b6aa8b62009-09-15 21:36:11293- (void)didEndMainMessageLoop {
294 DCHECK(!BrowserList::HasBrowserWithProfile([self defaultProfile]));
295 if (!BrowserList::HasBrowserWithProfile([self defaultProfile])) {
296 // As we're shutting down, we need to nuke the TabRestoreService, which
297 // will start the shutdown of the NavigationControllers and allow for
298 // proper shutdown. If we don't do this, Chrome won't shut down cleanly,
299 // and may end up crashing when some thread tries to use the IO thread (or
300 // another thread) that is no longer valid.
301 [self defaultProfile]->ResetTabRestoreService();
302 }
303}
304
[email protected]449dd2f2009-05-27 13:04:00305// Helper routine to get the window controller if the key window is a tabbed
306// window, or nil if not. Examples of non-tabbed windows are "about" or
307// "preferences".
308- (TabWindowController*)keyWindowTabController {
309 NSWindowController* keyWindowController =
[email protected]5dbabd02009-09-17 20:44:13310 [[NSApp keyWindow] windowController];
[email protected]449dd2f2009-05-27 13:04:00311 if ([keyWindowController isKindOfClass:[TabWindowController class]])
312 return (TabWindowController*)keyWindowController;
313
314 return nil;
315}
316
[email protected]74c0c642009-09-22 21:25:50317// Helper routine to get the window controller if the main window is a tabbed
318// window, or nil if not. Examples of non-tabbed windows are "about" or
319// "preferences".
320- (TabWindowController*)mainWindowTabController {
321 NSWindowController* mainWindowController =
322 [[NSApp mainWindow] windowController];
323 if ([mainWindowController isKindOfClass:[TabWindowController class]])
324 return (TabWindowController*)mainWindowController;
325
326 return nil;
327}
328
[email protected]449dd2f2009-05-27 13:04:00329// If the window has tabs, make "close window" be cmd-shift-w, otherwise leave
330// it as the normal cmd-w. Capitalization of the key equivalent affects whether
331// the shift modifer is used.
332- (void)adjustCloseWindowMenuItemKeyEquivalent:(BOOL)inHaveTabs {
333 [closeWindowMenuItem_ setKeyEquivalent:(inHaveTabs ? @"W" : @"w")];
[email protected]74c0c642009-09-22 21:25:50334 [closeWindowMenuItem_ setKeyEquivalentModifierMask:NSCommandKeyMask];
[email protected]449dd2f2009-05-27 13:04:00335}
336
337// If the window has tabs, make "close tab" take over cmd-w, otherwise it
338// shouldn't have any key-equivalent because it should be disabled.
339- (void)adjustCloseTabMenuItemKeyEquivalent:(BOOL)hasTabs {
340 if (hasTabs) {
341 [closeTabMenuItem_ setKeyEquivalent:@"w"];
342 [closeTabMenuItem_ setKeyEquivalentModifierMask:NSCommandKeyMask];
343 } else {
344 [closeTabMenuItem_ setKeyEquivalent:@""];
345 [closeTabMenuItem_ setKeyEquivalentModifierMask:0];
346 }
347}
348
[email protected]74c0c642009-09-22 21:25:50349// Explicitly remove any command-key equivalents from the close tab/window
350// menus so that nothing can go haywire if we get a user action during pending
351// updates.
352- (void)clearCloseMenuItemKeyEquivalents {
353 [closeTabMenuItem_ setKeyEquivalent:@""];
354 [closeTabMenuItem_ setKeyEquivalentModifierMask:0];
355 [closeWindowMenuItem_ setKeyEquivalent:@""];
356 [closeWindowMenuItem_ setKeyEquivalentModifierMask:0];
357}
358
[email protected]449dd2f2009-05-27 13:04:00359// See if we have a window with tabs open, and adjust the key equivalents for
[email protected]668c911c2010-03-08 17:05:25360// Close Tab/Close Window accordingly.
[email protected]449dd2f2009-05-27 13:04:00361- (void)fixCloseMenuItemKeyEquivalents {
[email protected]668c911c2010-03-08 17:05:25362 fileMenuUpdatePending_ = NO;
[email protected]449dd2f2009-05-27 13:04:00363 TabWindowController* tabController = [self keyWindowTabController];
[email protected]74c0c642009-09-22 21:25:50364 if (!tabController && ![NSApp keyWindow]) {
365 // There might be a small amount of time where there is no key window,
366 // so just use our main browser window if there is one.
367 tabController = [self mainWindowTabController];
368 }
[email protected]449dd2f2009-05-27 13:04:00369 BOOL windowWithMultipleTabs =
370 (tabController && [tabController numberOfTabs] > 1);
371 [self adjustCloseWindowMenuItemKeyEquivalent:windowWithMultipleTabs];
372 [self adjustCloseTabMenuItemKeyEquivalent:windowWithMultipleTabs];
[email protected]449dd2f2009-05-27 13:04:00373}
374
375// Fix up the "close tab/close window" command-key equivalents. We do this
376// after a delay to ensure that window layer state has been set by the time
[email protected]f2a5c342010-01-21 17:00:29377// we do the enabling. This should only be called on the main thread, code that
378// calls this (even as a side-effect) from other threads needs to be fixed.
[email protected]449dd2f2009-05-27 13:04:00379- (void)delayedFixCloseMenuItemKeyEquivalents {
[email protected]f2a5c342010-01-21 17:00:29380 DCHECK([NSThread isMainThread]);
[email protected]449dd2f2009-05-27 13:04:00381 if (!fileMenuUpdatePending_) {
[email protected]74c0c642009-09-22 21:25:50382 // The OS prefers keypresses to timers, so it's possible that a cmd-w
383 // can sneak in before this timer fires. In order to prevent that from
384 // having any bad consequences, just clear the keys combos altogether. They
385 // will be reset when the timer eventually fires.
[email protected]668c911c2010-03-08 17:05:25386 if ([NSThread isMainThread]) {
387 fileMenuUpdatePending_ = YES;
388 [self clearCloseMenuItemKeyEquivalents];
389 [self performSelector:@selector(fixCloseMenuItemKeyEquivalents)
390 withObject:nil
391 afterDelay:0];
392 } else {
393 // This shouldn't be happening, but if it does, force it to the main
394 // thread to avoid dropping the update. Don't mess with
395 // |fileMenuUpdatePending_| as it's not expected to be threadsafe and
396 // there could be a race between the selector finishing and setting the
397 // flag.
398 [self
399 performSelectorOnMainThread:@selector(fixCloseMenuItemKeyEquivalents)
[email protected]f2a5c342010-01-21 17:00:29400 withObject:nil
401 waitUntilDone:NO];
[email protected]668c911c2010-03-08 17:05:25402 }
[email protected]449dd2f2009-05-27 13:04:00403 }
404}
405
406// Called when we get a notification about the window layering changing to
407// update the UI based on the new main window.
408- (void)windowLayeringDidChange:(NSNotification*)notify {
409 [self delayedFixCloseMenuItemKeyEquivalents];
[email protected]449dd2f2009-05-27 13:04:00410}
411
412// Called when the number of tabs changes in one of the browser windows. The
413// object is the tab strip controller, but we don't currently care.
414- (void)tabsChanged:(NSNotification*)notify {
[email protected]e72746b12009-10-19 14:11:51415 // We don't need to do this on a delay, as in the method above, because the
416 // window layering isn't changing. As a result, there's no chance that a
417 // different window will sneak in as the key window and cause the problems
418 // we hacked around above by clearing the key equivalents.
419 [self fixCloseMenuItemKeyEquivalents];
[email protected]449dd2f2009-05-27 13:04:00420}
421
[email protected]414fde592009-05-21 16:14:43422// If the auto-update interval is not set, make it 5 hours.
423// This code is specific to Mac Chrome Dev Channel.
424// Placed here for 2 reasons:
425// 1) Same spot as other Pref stuff
426// 2) Try and be friendly by keeping this after app launch
427// TODO(jrg): remove once we go Beta.
428- (void)setUpdateCheckInterval {
429#if defined(GOOGLE_CHROME_BUILD)
430 CFStringRef app = (CFStringRef)@"com.google.Keystone.Agent";
431 CFStringRef checkInterval = (CFStringRef)@"checkInterval";
432 CFPropertyListRef plist = CFPreferencesCopyAppValue(checkInterval, app);
433 if (!plist) {
434 const float fiveHoursInSeconds = 5.0 * 60.0 * 60.0;
[email protected]7dc8c6b2010-04-09 17:02:50435 NSNumber* value = [NSNumber numberWithFloat:fiveHoursInSeconds];
[email protected]414fde592009-05-21 16:14:43436 CFPreferencesSetAppValue(checkInterval, value, app);
437 CFPreferencesAppSynchronize(app);
438 }
439#endif
440}
441
[email protected]136140c2009-05-19 13:58:25442// This is called after profiles have been loaded and preferences registered.
443// It is safe to access the default profile here.
444- (void)applicationDidFinishLaunching:(NSNotification*)notify {
445 // Hold an extra ref to the BrowserProcess singleton so it doesn't go away
446 // when all the browser windows get closed. We'll release it on quit which
447 // will be the signal to exit.
448 DCHECK(g_browser_process);
449 g_browser_process->AddRefModule();
450
[email protected]e5575022009-08-22 00:31:22451 bookmarkMenuBridge_.reset(new BookmarkMenuBridge([self defaultProfile]));
[email protected]1c84c82c2009-07-27 15:37:25452 historyMenuBridge_.reset(new HistoryMenuBridge([self defaultProfile]));
[email protected]cd63ef62009-05-06 19:41:37453
[email protected]414fde592009-05-21 16:14:43454 [self setUpdateCheckInterval];
[email protected]2bcec612009-05-14 17:50:53455
456 // Build up the encoding menu, the order of the items differs based on the
457 // current locale (see http://crbug.com/7647 for details).
458 // We need a valid g_browser_process to get the profile which is why we can't
459 // call this from awakeFromNib.
[email protected]fc349322009-09-23 22:28:23460 NSMenu* view_menu = [[[NSApp mainMenu] itemWithTag:IDC_VIEW_MENU] submenu];
461 NSMenuItem* encoding_menu_item = [view_menu itemWithTag:IDC_ENCODING_MENU];
[email protected]7dc8c6b2010-04-09 17:02:50462 NSMenu* encoding_menu = [encoding_menu_item submenu];
[email protected]fc349322009-09-23 22:28:23463 EncodingMenuControllerDelegate::BuildEncodingMenu([self defaultProfile],
464 encoding_menu);
[email protected]2bcec612009-05-14 17:50:53465
[email protected]f2c20fa2009-12-01 17:42:02466 // Since Chrome is localized to more languages than the OS, tell Cocoa which
467 // menu is the Help so it can add the search item to it.
[email protected]caadfca2010-01-22 21:51:21468 if (helpMenu_ && [NSApp respondsToSelector:@selector(setHelpMenu:)])
[email protected]f2c20fa2009-12-01 17:42:02469 [NSApp setHelpMenu:helpMenu_];
[email protected]f2c20fa2009-12-01 17:42:02470
[email protected]12dc3d42010-02-22 23:37:12471 // Record the path to the (browser) app bundle; this is used by the app mode
472 // shim.
473 RecordLastRunAppBundlePath();
474
[email protected]caadfca2010-01-22 21:51:21475 startupComplete_ = YES;
476
477 // TODO(viettrungluu): This is very temporary, since this should be done "in"
478 // |BrowserMain()|, i.e., this list of startup URLs should be appended to the
479 // (probably-empty) list of URLs from the command line.
[email protected]d630d7d52010-02-21 00:55:11480 if (startupUrls_.size()) {
481 [self openUrls:startupUrls_];
482 [self clearStartupUrls];
[email protected]caadfca2010-01-22 21:51:21483 }
[email protected]7c321082009-02-09 15:35:47484}
485
[email protected]a96ec6a2009-11-04 17:27:08486// This is called after profiles have been loaded and preferences registered.
487// It is safe to access the default profile here.
488- (void)applicationDidBecomeActive:(NSNotification*)notify {
489 NotificationService::current()->Notify(NotificationType::APP_ACTIVATED,
490 NotificationService::AllSources(),
491 NotificationService::NoDetails());
492}
493
[email protected]73f5b362009-08-10 23:58:21494// Helper function for populating and displaying the in progress downloads at
495// exit alert panel.
496- (BOOL)userWillWaitForInProgressDownloads:(int)downloadCount {
[email protected]e8b5f7882009-09-14 20:59:13497 NSString* warningText = nil;
498 NSString* explanationText = nil;
[email protected]73f5b362009-08-10 23:58:21499 NSString* waitTitle = nil;
500 NSString* exitTitle = nil;
501
[email protected]9dcb59662009-09-23 01:15:50502 std::wstring product_name = l10n_util::GetString(IDS_PRODUCT_NAME);
503
[email protected]73f5b362009-08-10 23:58:21504 // Set the dialog text based on whether or not there are multiple downloads.
505 if (downloadCount == 1) {
[email protected]e8b5f7882009-09-14 20:59:13506 // Dialog text: warning and explanation.
507 warningText =
[email protected]9dcb59662009-09-23 01:15:50508 base::SysWideToNSString(l10n_util::GetStringF(
509 IDS_SINGLE_DOWNLOAD_REMOVE_CONFIRM_WARNING, product_name));
[email protected]e8b5f7882009-09-14 20:59:13510 explanationText =
[email protected]9dcb59662009-09-23 01:15:50511 base::SysWideToNSString(l10n_util::GetStringF(
512 IDS_SINGLE_DOWNLOAD_REMOVE_CONFIRM_EXPLANATION, product_name));
[email protected]73f5b362009-08-10 23:58:21513
514 // Cancel download and exit button text.
515 exitTitle =
[email protected]e8b5f7882009-09-14 20:59:13516 base::SysWideToNSString(l10n_util::GetString(
517 IDS_SINGLE_DOWNLOAD_REMOVE_CONFIRM_OK_BUTTON_LABEL));
[email protected]73f5b362009-08-10 23:58:21518
519 // Wait for download button text.
520 waitTitle =
[email protected]e8b5f7882009-09-14 20:59:13521 base::SysWideToNSString(l10n_util::GetString(
522 IDS_SINGLE_DOWNLOAD_REMOVE_CONFIRM_CANCEL_BUTTON_LABEL));
[email protected]73f5b362009-08-10 23:58:21523 } else {
[email protected]e8b5f7882009-09-14 20:59:13524 // Dialog text: warning and explanation.
525 warningText =
526 base::SysWideToNSString(l10n_util::GetStringF(
[email protected]9dcb59662009-09-23 01:15:50527 IDS_MULTIPLE_DOWNLOADS_REMOVE_CONFIRM_WARNING, product_name,
528 IntToWString(downloadCount)));
[email protected]e8b5f7882009-09-14 20:59:13529 explanationText =
[email protected]9dcb59662009-09-23 01:15:50530 base::SysWideToNSString(l10n_util::GetStringF(
531 IDS_MULTIPLE_DOWNLOADS_REMOVE_CONFIRM_EXPLANATION, product_name));
[email protected]73f5b362009-08-10 23:58:21532
533 // Cancel downloads and exit button text.
534 exitTitle =
[email protected]e8b5f7882009-09-14 20:59:13535 base::SysWideToNSString(l10n_util::GetString(
536 IDS_MULTIPLE_DOWNLOADS_REMOVE_CONFIRM_OK_BUTTON_LABEL));
[email protected]73f5b362009-08-10 23:58:21537
538 // Wait for downloads button text.
539 waitTitle =
[email protected]e8b5f7882009-09-14 20:59:13540 base::SysWideToNSString(l10n_util::GetString(
541 IDS_MULTIPLE_DOWNLOADS_REMOVE_CONFIRM_CANCEL_BUTTON_LABEL));
[email protected]73f5b362009-08-10 23:58:21542 }
543
544 // 'waitButton' is the default choice.
[email protected]e8b5f7882009-09-14 20:59:13545 int choice = NSRunAlertPanel(warningText, explanationText,
546 waitTitle, exitTitle, nil);
[email protected]73f5b362009-08-10 23:58:21547 return choice == NSAlertDefaultReturn ? YES : NO;
548}
549
550// Check all profiles for in progress downloads, and if we find any, prompt the
[email protected]09729a552009-08-03 23:21:41551// user to see if we should continue to exit (and thus cancel the downloads), or
552// if we should wait.
553- (BOOL)shouldQuitWithInProgressDownloads {
[email protected]73f5b362009-08-10 23:58:21554 ProfileManager* profile_manager = g_browser_process->profile_manager();
555 if (!profile_manager)
556 return YES;
[email protected]09729a552009-08-03 23:21:41557
[email protected]73f5b362009-08-10 23:58:21558 ProfileManager::const_iterator it = profile_manager->begin();
559 for (; it != profile_manager->end(); ++it) {
560 Profile* profile = *it;
[email protected]09729a552009-08-03 23:21:41561 DownloadManager* download_manager = profile->GetDownloadManager();
[email protected]73f5b362009-08-10 23:58:21562 if (download_manager && download_manager->in_progress_count() > 0) {
563 int downloadCount = download_manager->in_progress_count();
564 if ([self userWillWaitForInProgressDownloads:downloadCount]) {
565 // Create a new browser window (if necessary) and navigate to the
566 // downloads page if the user chooses to wait.
567 Browser* browser = BrowserList::FindBrowserWithProfile(profile);
568 if (!browser) {
569 browser = Browser::Create(profile);
570 browser->window()->Show();
571 }
572 DCHECK(browser);
573 browser->ShowDownloadsTab();
574 return NO;
575 }
[email protected]09729a552009-08-03 23:21:41576
[email protected]73f5b362009-08-10 23:58:21577 // User wants to exit.
578 return YES;
[email protected]09729a552009-08-03 23:21:41579 }
[email protected]09729a552009-08-03 23:21:41580 }
581
[email protected]73f5b362009-08-10 23:58:21582 // No profiles or active downloads found, okay to exit.
[email protected]09729a552009-08-03 23:21:41583 return YES;
584}
585
[email protected]fbc947b2009-06-19 13:28:24586// Called to determine if we should enable the "restore tab" menu item.
587// Checks with the TabRestoreService to see if there's anything there to
588// restore and returns YES if so.
589- (BOOL)canRestoreTab {
590 TabRestoreService* service = [self defaultProfile]->GetTabRestoreService();
591 return service && !service->entries().empty();
592}
593
[email protected]6a281332009-10-13 17:41:06594// Returns true if there is no browser window, or if the active window is
595// blocked by a modal dialog.
596- (BOOL)keyWindowIsMissingOrBlocked {
597 Browser* browser = BrowserList::GetLastActive();
598 return browser == NULL ||
599 ![[browser->window()->GetNativeHandle() attachedSheet]
600 isKindOfClass:[NSWindow class]];
601}
602
[email protected]88d74942009-01-21 22:04:44603// Called to validate menu items when there are no key windows. All the
604// items we care about have been set with the |commandDispatch:| action and
605// a target of FirstResponder in IB. If it's not one of those, let it
606// continue up the responder chain to be handled elsewhere. We pull out the
607// tag as the cross-platform constant to differentiate and dispatch the
608// various commands.
609- (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item {
610 SEL action = [item action];
611 BOOL enable = NO;
612 if (action == @selector(commandDispatch:)) {
613 NSInteger tag = [item tag];
[email protected]fbc947b2009-06-19 13:28:24614 if (menuState_->SupportsCommand(tag)) {
615 switch (tag) {
[email protected]419eb2a2009-11-11 16:26:26616 // The File Menu commands are not automatically disabled by Cocoa when a
617 // dialog sheet obscures the browser window, so we disable several of
618 // them here. We don't need to include IDC_CLOSE_WINDOW, because
619 // app_controller is only activated when there are no key windows (see
620 // function comment).
[email protected]fbc947b2009-06-19 13:28:24621 case IDC_RESTORE_TAB:
[email protected]6a281332009-10-13 17:41:06622 enable = [self keyWindowIsMissingOrBlocked] && [self canRestoreTab];
623 break;
[email protected]6a281332009-10-13 17:41:06624 case IDC_OPEN_FILE:
[email protected]6a281332009-10-13 17:41:06625 case IDC_NEW_TAB:
[email protected]6a281332009-10-13 17:41:06626 enable = [self keyWindowIsMissingOrBlocked];
[email protected]fbc947b2009-06-19 13:28:24627 break;
[email protected]f9bc9b92009-11-24 00:55:35628 case IDC_SYNC_BOOKMARKS:
629 enable = ProfileSyncService::IsSyncEnabled();
[email protected]40f047972009-11-25 03:54:40630 sync_ui_util::UpdateSyncItem(item, enable, [self defaultProfile]);
[email protected]f9bc9b92009-11-24 00:55:35631 break;
[email protected]fbc947b2009-06-19 13:28:24632 default:
633 enable = menuState_->IsCommandEnabled(tag) ? YES : NO;
634 }
635 }
[email protected]3b6aa8b62009-09-15 21:36:11636 } else if (action == @selector(terminate:)) {
[email protected]88d74942009-01-21 22:04:44637 enable = YES;
[email protected]3111f08b2009-04-30 16:01:52638 } else if (action == @selector(showPreferences:)) {
639 enable = YES;
[email protected]bde3dda2009-05-20 22:13:07640 } else if (action == @selector(orderFrontStandardAboutPanel:)) {
641 enable = YES;
[email protected]3d4bd23f2009-10-06 03:58:38642 } else if (action == @selector(newWindowFromDock:)) {
643 enable = YES;
[email protected]88d74942009-01-21 22:04:44644 }
645 return enable;
646}
647
[email protected]947fc0d2010-01-14 22:33:05648// Called when the user picks a menu item when there are no key windows, or when
649// there is no foreground browser window. Calls through to the browser object to
650// execute the command. This assumes that the command is supported and doesn't
[email protected]d630d7d52010-02-21 00:55:11651// check, otherwise it should have been disabled in the UI in
652// |-validateUserInterfaceItem:|.
[email protected]88d74942009-01-21 22:04:44653- (void)commandDispatch:(id)sender {
[email protected]93e181762009-05-29 14:30:38654 Profile* defaultProfile = [self defaultProfile];
[email protected]f0a51fb52009-03-05 12:46:38655
[email protected]947fc0d2010-01-14 22:33:05656 // Handle the case where we're dispatching a command from a sender that's in a
657 // browser window. This means that the command came from a background window
658 // and is getting here because the foreground window is not a browser window.
659 if ([sender respondsToSelector:@selector(window)]) {
660 id delegate = [[sender window] windowController];
661 if ([delegate isKindOfClass:[BrowserWindowController class]]) {
662 [delegate commandDispatch:sender];
663 return;
664 }
665 }
666
[email protected]88d74942009-01-21 22:04:44667 NSInteger tag = [sender tag];
668 switch (tag) {
[email protected]fbc947b2009-06-19 13:28:24669 case IDC_NEW_TAB:
[email protected]d630d7d52010-02-21 00:55:11670 // Create a new tab in an existing browser window (which we activate) if
671 // possible.
672 if (Browser* browser = ActivateBrowser(defaultProfile)) {
673 browser->ExecuteCommand(IDC_NEW_TAB);
674 break;
675 }
676 // Else fall through to create new window.
[email protected]88d74942009-01-21 22:04:44677 case IDC_NEW_WINDOW:
[email protected]d630d7d52010-02-21 00:55:11678 CreateBrowser(defaultProfile);
679 break;
[email protected]91a5b3e2009-10-30 19:32:22680 case IDC_FOCUS_LOCATION:
[email protected]d630d7d52010-02-21 00:55:11681 ActivateOrCreateBrowser(defaultProfile)->
682 ExecuteCommand(IDC_FOCUS_LOCATION);
[email protected]88d74942009-01-21 22:04:44683 break;
[email protected]cddc5242009-12-07 18:54:33684 case IDC_FOCUS_SEARCH:
[email protected]d630d7d52010-02-21 00:55:11685 ActivateOrCreateBrowser(defaultProfile)->ExecuteCommand(IDC_FOCUS_SEARCH);
[email protected]cddc5242009-12-07 18:54:33686 break;
[email protected]863ff662009-01-26 20:18:18687 case IDC_NEW_INCOGNITO_WINDOW:
[email protected]e9e211272009-09-22 16:39:11688 Browser::OpenEmptyWindow(defaultProfile->GetOffTheRecordProfile());
[email protected]863ff662009-01-26 20:18:18689 break;
[email protected]fbc947b2009-06-19 13:28:24690 case IDC_RESTORE_TAB:
691 Browser::OpenWindowWithRestoredTabs(defaultProfile);
692 break;
[email protected]e19516d2009-04-28 17:15:19693 case IDC_OPEN_FILE:
[email protected]d630d7d52010-02-21 00:55:11694 CreateBrowser(defaultProfile)->ExecuteCommand(IDC_OPEN_FILE);
[email protected]e19516d2009-04-28 17:15:19695 break;
[email protected]1fdff702009-10-22 00:36:18696 case IDC_CLEAR_BROWSING_DATA: {
[email protected]93e181762009-05-29 14:30:38697 // There may not be a browser open, so use the default profile.
[email protected]049ac832009-12-15 16:26:54698 [ClearBrowsingDataController
699 showClearBrowsingDialogForProfile:defaultProfile];
[email protected]93e181762009-05-29 14:30:38700 break;
[email protected]1fdff702009-10-22 00:36:18701 }
[email protected]4aee5652009-11-19 18:59:21702 case IDC_IMPORT_SETTINGS: {
[email protected]896220042010-03-23 18:14:28703 UserMetrics::RecordAction(UserMetricsAction("Import_ShowDlg"),
704 defaultProfile);
[email protected]2989cce12010-02-01 10:56:53705 [ImportSettingsDialogController
706 showImportSettingsDialogForProfile:defaultProfile];
[email protected]4aee5652009-11-19 18:59:21707 break;
708 }
[email protected]af7c25e2010-01-23 05:46:45709 case IDC_SHOW_BOOKMARK_MANAGER:
[email protected]896220042010-03-23 18:14:28710 UserMetrics::RecordAction(UserMetricsAction("ShowBookmarkManager"),
711 defaultProfile);
[email protected]90a8bf252010-03-12 00:09:47712 if (Browser* browser = ActivateBrowser(defaultProfile)) {
[email protected]47e532b2010-04-23 17:46:40713 // Open a bookmark manager tab.
[email protected]90a8bf252010-03-12 00:09:47714 browser->OpenBookmarkManager();
715 } else {
[email protected]47e532b2010-04-23 17:46:40716 // No browser window, so create one for the bookmark manager tab.
717 Browser::OpenBookmarkManagerWindow(defaultProfile);
[email protected]90a8bf252010-03-12 00:09:47718 }
[email protected]af7c25e2010-01-23 05:46:45719 break;
[email protected]91a5b3e2009-10-30 19:32:22720 case IDC_SHOW_HISTORY:
[email protected]d630d7d52010-02-21 00:55:11721 if (Browser* browser = ActivateBrowser(defaultProfile))
722 browser->ShowHistoryTab();
723 else
724 Browser::OpenHistoryWindow(defaultProfile);
[email protected]91a5b3e2009-10-30 19:32:22725 break;
726 case IDC_SHOW_DOWNLOADS:
[email protected]d630d7d52010-02-21 00:55:11727 if (Browser* browser = ActivateBrowser(defaultProfile))
728 browser->ShowDownloadsTab();
729 else
730 Browser::OpenDownloadsWindow(defaultProfile);
[email protected]91a5b3e2009-10-30 19:32:22731 break;
[email protected]bb37e6ec2009-11-18 17:59:36732 case IDC_MANAGE_EXTENSIONS:
[email protected]d630d7d52010-02-21 00:55:11733 if (Browser* browser = ActivateBrowser(defaultProfile))
734 browser->ShowExtensionsTab();
735 else
736 Browser::OpenExtensionsWindow(defaultProfile);
[email protected]bb37e6ec2009-11-18 17:59:36737 break;
[email protected]1fdff702009-10-22 00:36:18738 case IDC_HELP_PAGE:
[email protected]d630d7d52010-02-21 00:55:11739 if (Browser* browser = ActivateBrowser(defaultProfile))
740 browser->OpenHelpTab();
741 else
742 Browser::OpenHelpWindow(defaultProfile);
[email protected]1fdff702009-10-22 00:36:18743 break;
[email protected]3896b182009-11-03 16:15:32744 case IDC_REPORT_BUG: {
745 Browser* browser = BrowserList::GetLastActive();
[email protected]d630d7d52010-02-21 00:55:11746 TabContents* currentTab =
747 browser ? browser->GetSelectedTabContents() : NULL;
[email protected]3896b182009-11-03 16:15:32748 BugReportWindowController* controller =
749 [[BugReportWindowController alloc]
[email protected]d630d7d52010-02-21 00:55:11750 initWithTabContents:currentTab
751 profile:[self defaultProfile]];
[email protected]3896b182009-11-03 16:15:32752 [controller runModalDialog];
753 break;
754 }
[email protected]f9bc9b92009-11-24 00:55:35755 case IDC_SYNC_BOOKMARKS:
756 // TODO(akalin): Add a constant to denote starting sync from the
757 // main menu and use that instead of START_FROM_WRENCH.
[email protected]40f047972009-11-25 03:54:40758 sync_ui_util::OpenSyncMyBookmarksDialog(
[email protected]f9bc9b92009-11-24 00:55:35759 defaultProfile, ProfileSyncService::START_FROM_WRENCH);
760 break;
[email protected]ceaaf1812010-01-20 22:13:09761 case IDC_TASK_MANAGER:
[email protected]896220042010-03-23 18:14:28762 UserMetrics::RecordAction(UserMetricsAction("TaskManager"),
763 defaultProfile);
[email protected]ceaaf1812010-01-20 22:13:09764 TaskManagerMac::Show();
765 break;
[email protected]d630d7d52010-02-21 00:55:11766 }
[email protected]88d74942009-01-21 22:04:44767}
768
[email protected]947fc0d2010-01-14 22:33:05769// Same as |-commandDispatch:|, but executes commands using a disposition
770// determined by the key flags. This will get called in the case where the
771// frontmost window is not a browser window, and the user has command-clicked
772// a button in a background browser window whose action is
773// |-commandDispatchUsingKeyModifiers:|
774- (void)commandDispatchUsingKeyModifiers:(id)sender {
775 DCHECK(sender);
776 if ([sender respondsToSelector:@selector(window)]) {
777 id delegate = [[sender window] windowController];
778 if ([delegate isKindOfClass:[BrowserWindowController class]]) {
779 [delegate commandDispatchUsingKeyModifiers:sender];
780 }
781 }
782}
783
[email protected]ff81e0a2009-04-13 14:58:54784// NSApplication delegate method called when someone clicks on the
785// dock icon and there are no open windows. To match standard mac
786// behavior, we should open a new window.
787- (BOOL)applicationShouldHandleReopen:(NSApplication*)theApplication
788 hasVisibleWindows:(BOOL)flag {
[email protected]70631402010-04-26 20:16:54789 // If the browser is currently trying to quit, don't do anything and return NO
790 // to prevent AppKit from doing anything.
791 // TODO(rohitrao): Remove this code when http://crbug.com/40861 is resolved.
792 if (browser_shutdown::IsTryingToQuit())
793 return NO;
794
[email protected]ff81e0a2009-04-13 14:58:54795 // Don't do anything if there are visible windows. This will cause
796 // AppKit to unminimize the most recently minimized window.
797 if (flag)
798 return YES;
799
800 // Otherwise open a new window.
[email protected]d630d7d52010-02-21 00:55:11801 {
802 AutoReset auto_reset_in_run(&g_is_opening_new_window, true);
803 Browser::OpenEmptyWindow([self defaultProfile]);
804 }
[email protected]ff81e0a2009-04-13 14:58:54805
806 // We've handled the reopen event, so return NO to tell AppKit not
807 // to do anything.
808 return NO;
809}
810
[email protected]88d74942009-01-21 22:04:44811- (void)initMenuState {
[email protected]3111f08b2009-04-30 16:01:52812 menuState_.reset(new CommandUpdater(NULL));
[email protected]fbc947b2009-06-19 13:28:24813 menuState_->UpdateCommandEnabled(IDC_NEW_TAB, true);
[email protected]88d74942009-01-21 22:04:44814 menuState_->UpdateCommandEnabled(IDC_NEW_WINDOW, true);
[email protected]863ff662009-01-26 20:18:18815 menuState_->UpdateCommandEnabled(IDC_NEW_INCOGNITO_WINDOW, true);
[email protected]e19516d2009-04-28 17:15:19816 menuState_->UpdateCommandEnabled(IDC_OPEN_FILE, true);
[email protected]93e181762009-05-29 14:30:38817 menuState_->UpdateCommandEnabled(IDC_CLEAR_BROWSING_DATA, true);
[email protected]fbc947b2009-06-19 13:28:24818 menuState_->UpdateCommandEnabled(IDC_RESTORE_TAB, false);
[email protected]91a5b3e2009-10-30 19:32:22819 menuState_->UpdateCommandEnabled(IDC_FOCUS_LOCATION, true);
[email protected]cddc5242009-12-07 18:54:33820 menuState_->UpdateCommandEnabled(IDC_FOCUS_SEARCH, true);
[email protected]af7c25e2010-01-23 05:46:45821 menuState_->UpdateCommandEnabled(IDC_SHOW_BOOKMARK_MANAGER, true);
[email protected]91a5b3e2009-10-30 19:32:22822 menuState_->UpdateCommandEnabled(IDC_SHOW_HISTORY, true);
823 menuState_->UpdateCommandEnabled(IDC_SHOW_DOWNLOADS, true);
[email protected]bb37e6ec2009-11-18 17:59:36824 menuState_->UpdateCommandEnabled(IDC_MANAGE_EXTENSIONS, true);
[email protected]1fdff702009-10-22 00:36:18825 menuState_->UpdateCommandEnabled(IDC_HELP_PAGE, true);
[email protected]4aee5652009-11-19 18:59:21826 menuState_->UpdateCommandEnabled(IDC_IMPORT_SETTINGS, true);
[email protected]3896b182009-11-03 16:15:32827 menuState_->UpdateCommandEnabled(IDC_REPORT_BUG, true);
[email protected]f9bc9b92009-11-24 00:55:35828 menuState_->UpdateCommandEnabled(IDC_SYNC_BOOKMARKS,
829 ProfileSyncService::IsSyncEnabled());
[email protected]ceaaf1812010-01-20 22:13:09830 menuState_->UpdateCommandEnabled(IDC_TASK_MANAGER, true);
[email protected]88d74942009-01-21 22:04:44831}
[email protected]1bcdb532009-01-16 17:47:57832
[email protected]3f34599d2009-03-25 22:11:43833- (Profile*)defaultProfile {
[email protected]3f34599d2009-03-25 22:11:43834 // TODO(jrg): Find a better way to get the "default" profile.
835 if (g_browser_process->profile_manager())
[email protected]065566b2010-01-20 20:38:26836 return *g_browser_process->profile_manager()->begin();
[email protected]3f34599d2009-03-25 22:11:43837
[email protected]f6314002009-04-23 01:18:13838 return NULL;
[email protected]3f34599d2009-03-25 22:11:43839}
840
[email protected]7dc8c6b2010-04-09 17:02:50841// (Private) Never call |-applicationShouldTerminate:|; just make everything go
842// through |-terminate:|.
843- (void)handleQuitEvent:(NSAppleEventDescriptor*)event
844 withReply:(NSAppleEventDescriptor*)reply {
845 [NSApp terminate:nil];
846}
847
[email protected]57750f822009-04-21 21:43:09848// Various methods to open URLs that we get in a native fashion. We use
849// BrowserInit here because on the other platforms, URLs to open come through
850// the ProcessSingleton, and it calls BrowserInit. It's best to bottleneck the
851// openings through that for uniform handling.
852
[email protected]d630d7d52010-02-21 00:55:11853- (void)openUrls:(const std::vector<GURL>&)urls {
[email protected]caadfca2010-01-22 21:51:21854 // If the browser hasn't started yet, just queue up the URLs.
855 if (!startupComplete_) {
[email protected]d630d7d52010-02-21 00:55:11856 startupUrls_.insert(startupUrls_.end(), urls.begin(), urls.end());
[email protected]136140c2009-05-19 13:58:25857 return;
858 }
[email protected]57750f822009-04-21 21:43:09859
[email protected]0e863512009-05-28 19:45:07860 Browser* browser = BrowserList::GetLastActive();
861 // if no browser window exists then create one with no tabs to be filled in
862 if (!browser) {
863 browser = Browser::Create([self defaultProfile]);
[email protected]ce560f82009-06-03 09:39:44864 browser->window()->Show();
[email protected]0e863512009-05-28 19:45:07865 }
866
[email protected]51343d5a2009-10-26 22:39:33867 CommandLine dummy(CommandLine::ARGUMENTS_ONLY);
[email protected]57750f822009-04-21 21:43:09868 BrowserInit::LaunchWithProfile launch(std::wstring(), dummy);
[email protected]0e863512009-05-28 19:45:07869 launch.OpenURLsInBrowser(browser, false, urls);
[email protected]57750f822009-04-21 21:43:09870}
871
[email protected]57750f822009-04-21 21:43:09872- (void)getUrl:(NSAppleEventDescriptor*)event
873 withReply:(NSAppleEventDescriptor*)reply {
874 NSString* urlStr = [[event paramDescriptorForKeyword:keyDirectObject]
875 stringValue];
876
877 GURL gurl(base::SysNSStringToUTF8(urlStr));
878 std::vector<GURL> gurlVector;
879 gurlVector.push_back(gurl);
880
[email protected]d630d7d52010-02-21 00:55:11881 [self openUrls:gurlVector];
[email protected]57750f822009-04-21 21:43:09882}
883
[email protected]caadfca2010-01-22 21:51:21884- (void)application:(NSApplication*)sender
885 openFiles:(NSArray*)filenames {
[email protected]57750f822009-04-21 21:43:09886 std::vector<GURL> gurlVector;
[email protected]caadfca2010-01-22 21:51:21887 for (NSString* file in filenames) {
888 GURL gurl = net::FilePathToFileURL(FilePath(base::SysNSStringToUTF8(file)));
[email protected]57750f822009-04-21 21:43:09889 gurlVector.push_back(gurl);
890 }
[email protected]caadfca2010-01-22 21:51:21891 if (!gurlVector.empty())
[email protected]d630d7d52010-02-21 00:55:11892 [self openUrls:gurlVector];
[email protected]caadfca2010-01-22 21:51:21893 else
894 NOTREACHED() << "Nothing to open!";
[email protected]57750f822009-04-21 21:43:09895
[email protected]caadfca2010-01-22 21:51:21896 [sender replyToOpenOrPrint:NSApplicationDelegateReplySuccess];
[email protected]57750f822009-04-21 21:43:09897}
[email protected]3f34599d2009-03-25 22:11:43898
[email protected]767543d2009-04-30 19:23:58899// Called when the preferences window is closed. We use this to release the
900// window controller.
[email protected]d4e18652009-11-19 21:52:16901- (void)prefsWindowClosed:(NSNotification*)notification {
902 NSWindow* window = [prefsController_ window];
903 DCHECK([notification object] == window);
904 NSNotificationCenter* defaultCenter = [NSNotificationCenter defaultCenter];
905 [defaultCenter removeObserver:self
906 name:NSWindowWillCloseNotification
907 object:window];
908 // PreferencesWindowControllers are autoreleased in
909 // -[PreferencesWindowController windowWillClose:].
910 prefsController_ = nil;
[email protected]767543d2009-04-30 19:23:58911}
912
[email protected]3111f08b2009-04-30 16:01:52913// Show the preferences window, or bring it to the front if it's already
914// visible.
915- (IBAction)showPreferences:(id)sender {
[email protected]d989f07c2009-11-14 00:35:46916 [self showPreferencesWindow:sender
917 page:OPTIONS_PAGE_DEFAULT
918 profile:[self defaultProfile]];
919}
920
921- (void)showPreferencesWindow:(id)sender
922 page:(OptionsPage)page
923 profile:(Profile*)profile {
[email protected]d4e18652009-11-19 21:52:16924 if (prefsController_) {
[email protected]d989f07c2009-11-14 00:35:46925 [prefsController_ switchToPage:page animate:YES];
926 } else {
[email protected]d4e18652009-11-19 21:52:16927 prefsController_ =
928 [[PreferencesWindowController alloc] initWithProfile:profile
929 initialPage:page];
[email protected]767543d2009-04-30 19:23:58930 // Watch for a notification of when it goes away so that we can destroy
931 // the controller.
932 [[NSNotificationCenter defaultCenter]
933 addObserver:self
934 selector:@selector(prefsWindowClosed:)
[email protected]d4e18652009-11-19 21:52:16935 name:NSWindowWillCloseNotification
936 object:[prefsController_ window]];
[email protected]42404382009-04-30 17:59:24937 }
938 [prefsController_ showPreferences:sender];
[email protected]3111f08b2009-04-30 16:01:52939}
940
[email protected]bde3dda2009-05-20 22:13:07941// Called when the about window is closed. We use this to release the
942// window controller.
[email protected]d4e18652009-11-19 21:52:16943- (void)aboutWindowClosed:(NSNotification*)notification {
944 NSWindow* window = [aboutController_ window];
945 DCHECK(window == [notification object]);
[email protected]bde3dda2009-05-20 22:13:07946 [[NSNotificationCenter defaultCenter]
[email protected]49aeee52009-10-26 19:58:13947 removeObserver:self
[email protected]d4e18652009-11-19 21:52:16948 name:NSWindowWillCloseNotification
949 object:window];
950 // AboutWindowControllers are autoreleased in
951 // -[AboutWindowController windowWillClose:].
952 aboutController_ = nil;
[email protected]bde3dda2009-05-20 22:13:07953}
954
955- (IBAction)orderFrontStandardAboutPanel:(id)sender {
[email protected]bde3dda2009-05-20 22:13:07956 if (!aboutController_) {
[email protected]d4e18652009-11-19 21:52:16957 aboutController_ =
958 [[AboutWindowController alloc] initWithProfile:[self defaultProfile]];
[email protected]49aeee52009-10-26 19:58:13959
[email protected]bde3dda2009-05-20 22:13:07960 // Watch for a notification of when it goes away so that we can destroy
961 // the controller.
962 [[NSNotificationCenter defaultCenter]
963 addObserver:self
964 selector:@selector(aboutWindowClosed:)
[email protected]d4e18652009-11-19 21:52:16965 name:NSWindowWillCloseNotification
966 object:[aboutController_ window]];
[email protected]bde3dda2009-05-20 22:13:07967 }
[email protected]49aeee52009-10-26 19:58:13968
[email protected]bde3dda2009-05-20 22:13:07969 [aboutController_ showWindow:self];
[email protected]bde3dda2009-05-20 22:13:07970}
971
[email protected]3d4bd23f2009-10-06 03:58:38972// Explicitly bring to the foreground when creating new windows from the dock.
973- (void)newWindowFromDock:(id)sender {
974 [NSApp activateIgnoringOtherApps:YES];
975 [self commandDispatch:sender];
976}
977
[email protected]288bfcd32009-09-14 18:14:46978- (NSMenu*)applicationDockMenu:(id)sender {
[email protected]018a3962009-09-17 22:23:44979 NSMenu* dockMenu = [[[NSMenu alloc] initWithTitle: @""] autorelease];
980 NSString* titleStr = l10n_util::GetNSStringWithFixup(IDS_NEW_WINDOW_MAC);
981 scoped_nsobject<NSMenuItem> item([[NSMenuItem alloc]
982 initWithTitle:titleStr
[email protected]3d4bd23f2009-10-06 03:58:38983 action:@selector(newWindowFromDock:)
[email protected]018a3962009-09-17 22:23:44984 keyEquivalent:@""]);
[email protected]288bfcd32009-09-14 18:14:46985 [item setTarget:self];
986 [item setTag:IDC_NEW_WINDOW];
[email protected]018a3962009-09-17 22:23:44987 [dockMenu addItem:item];
[email protected]288bfcd32009-09-14 18:14:46988
989 titleStr = l10n_util::GetNSStringWithFixup(IDS_NEW_INCOGNITO_WINDOW_MAC);
[email protected]018a3962009-09-17 22:23:44990 item.reset([[NSMenuItem alloc] initWithTitle:titleStr
[email protected]3d4bd23f2009-10-06 03:58:38991 action:@selector(newWindowFromDock:)
[email protected]018a3962009-09-17 22:23:44992 keyEquivalent:@""]);
[email protected]288bfcd32009-09-14 18:14:46993 [item setTarget:self];
994 [item setTag:IDC_NEW_INCOGNITO_WINDOW];
[email protected]018a3962009-09-17 22:23:44995 [dockMenu addItem:item];
[email protected]288bfcd32009-09-14 18:14:46996
[email protected]018a3962009-09-17 22:23:44997 return dockMenu;
[email protected]288bfcd32009-09-14 18:14:46998}
999
[email protected]d630d7d52010-02-21 00:55:111000- (const std::vector<GURL>&)startupUrls {
1001 return startupUrls_;
[email protected]caadfca2010-01-22 21:51:211002}
1003
[email protected]d630d7d52010-02-21 00:55:111004- (void)clearStartupUrls {
1005 startupUrls_.clear();
[email protected]caadfca2010-01-22 21:51:211006}
1007
[email protected]d630d7d52010-02-21 00:55:111008@end // @implementation AppController
[email protected]60ad3e22009-09-18 21:07:191009
1010//---------------------------------------------------------------------------
1011
[email protected]60ad3e22009-09-18 21:07:191012void ShowOptionsWindow(OptionsPage page,
1013 OptionsGroup highlight_group,
1014 Profile* profile) {
[email protected]d989f07c2009-11-14 00:35:461015 // TODO(akalin): Use highlight_group.
1016 AppController* appController = [NSApp delegate];
1017 [appController showPreferencesWindow:nil page:page profile:profile];
[email protected]60ad3e22009-09-18 21:07:191018}
[email protected]216e9042009-10-29 17:05:231019
1020namespace app_controller_mac {
1021
1022bool IsOpeningNewWindow() {
1023 return g_is_opening_new_window;
1024}
1025
1026} // namespace app_controller_mac