blob: 428f3758ba8d929bd68883709272cf1ee7bb1bf0 [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]1a3aba82010-11-08 23:52:5416#include "chrome/app/chrome_command_ids.h"
[email protected]93b59fc2010-12-21 20:00:4717#include "chrome/browser/background_application_list_model.h"
[email protected]5c238752009-06-13 10:29:0718#include "chrome/browser/browser_process.h"
[email protected]41741a962009-02-18 21:51:3919#include "chrome/browser/browser_shutdown.h"
[email protected]ed7e6dd2010-10-12 02:02:4520#include "chrome/browser/browser_thread.h"
[email protected]2c47bc12009-04-10 20:14:0021#include "chrome/browser/command_updater.h"
[email protected]09729a552009-08-03 23:21:4122#include "chrome/browser/download/download_manager.h"
[email protected]4aee5652009-11-19 18:59:2123#include "chrome/browser/metrics/user_metrics.h"
[email protected]37858e52010-08-26 00:22:0224#include "chrome/browser/prefs/pref_service.h"
[email protected]e7dd6d82010-10-07 23:21:1225#include "chrome/browser/printing/print_job_manager.h"
[email protected]8ecad5e2010-12-02 21:18:3326#include "chrome/browser/profiles/profile_manager.h"
[email protected]fbc947b2009-06-19 13:28:2427#include "chrome/browser/sessions/tab_restore_service.h"
[email protected]f9bc9b92009-11-24 00:55:3528#include "chrome/browser/sync/profile_sync_service.h"
[email protected]40f047972009-11-25 03:54:4029#include "chrome/browser/sync/sync_ui_util.h"
30#include "chrome/browser/sync/sync_ui_util_mac.h"
[email protected]60ad3e22009-09-18 21:07:1931#include "chrome/browser/tab_contents/tab_contents.h"
[email protected]ae04f592010-11-18 20:41:3532#include "chrome/browser/ui/browser.h"
[email protected]f7002802010-11-12 19:50:2833#include "chrome/browser/ui/browser_init.h"
[email protected]ae04f592010-11-18 20:41:3534#include "chrome/browser/ui/browser_list.h"
35#include "chrome/browser/ui/browser_window.h"
[email protected]7d791652010-12-01 16:34:4936#import "chrome/browser/ui/cocoa/about_window_controller.h"
37#import "chrome/browser/ui/cocoa/bookmarks/bookmark_menu_bridge.h"
38#import "chrome/browser/ui/cocoa/browser_window_cocoa.h"
39#import "chrome/browser/ui/cocoa/browser_window_controller.h"
40#import "chrome/browser/ui/cocoa/bug_report_window_controller.h"
41#import "chrome/browser/ui/cocoa/clear_browsing_data_controller.h"
42#import "chrome/browser/ui/cocoa/confirm_quit_panel_controller.h"
43#import "chrome/browser/ui/cocoa/encoding_menu_controller_delegate_mac.h"
44#import "chrome/browser/ui/cocoa/history_menu_bridge.h"
45#import "chrome/browser/ui/cocoa/import_settings_dialog.h"
46#import "chrome/browser/ui/cocoa/preferences_window_controller.h"
47#import "chrome/browser/ui/cocoa/tab_strip_controller.h"
48#import "chrome/browser/ui/cocoa/tab_window_controller.h"
49#include "chrome/browser/ui/cocoa/task_manager_mac.h"
[email protected]6a3ec2312010-12-02 19:30:1950#include "chrome/browser/ui/options/options_window.h"
[email protected]646b9b92010-04-30 21:51:5851#include "chrome/common/app_mode_common_mac.h"
[email protected]12dc3d42010-02-22 23:37:1252#include "chrome/common/chrome_paths_internal.h"
[email protected]f4acfae82009-09-11 00:33:3853#include "chrome/common/chrome_switches.h"
[email protected]a96ec6a2009-11-04 17:27:0854#include "chrome/common/notification_service.h"
[email protected]cd63ef62009-05-06 19:41:3755#include "chrome/common/pref_names.h"
[email protected]024617a2010-08-20 05:08:0556#include "chrome/common/url_constants.h"
[email protected]9dcb59662009-09-23 01:15:5057#include "grit/chromium_strings.h"
[email protected]09729a552009-08-03 23:21:4158#include "grit/generated_resources.h"
[email protected]caadfca2010-01-22 21:51:2159#include "net/base/net_util.h"
[email protected]88d74942009-01-21 22:04:4460
[email protected]f2c20fa2009-12-01 17:42:0261// 10.6 adds a public API for the Spotlight-backed search menu item in the Help
62// menu. Provide the declaration so it can be called below when building with
63// the 10.5 SDK.
64#if !defined(MAC_OS_X_VERSION_10_6) || \
65 MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6
66@interface NSApplication (SnowLeopardSDKDeclarations)
[email protected]caadfca2010-01-22 21:51:2167- (void)setHelpMenu:(NSMenu*)helpMenu;
[email protected]f2c20fa2009-12-01 17:42:0268@end
69#endif
70
[email protected]d630d7d52010-02-21 00:55:1171namespace {
72
73// True while AppController is calling Browser::OpenEmptyWindow(). We need a
74// global flag here, analogue to BrowserInit::InProcessStartup() because
75// otherwise the SessionService will try to restore sessions when we make a new
76// window while there are no other active windows.
77bool g_is_opening_new_window = false;
78
79// Activates a browser window having the given profile (the last one active) if
80// possible and returns a pointer to the activate |Browser| or NULL if this was
81// not possible. If the last active browser is minimized (in particular, if
82// there are only minimized windows), it will unminimize it.
83Browser* ActivateBrowser(Profile* profile) {
84 Browser* browser = BrowserList::GetLastActiveWithProfile(profile);
85 if (browser)
86 browser->window()->Activate();
87 return browser;
88}
89
90// Creates an empty browser window with the given profile and returns a pointer
91// to the new |Browser|.
92Browser* CreateBrowser(Profile* profile) {
93 {
[email protected]0fbd70332010-06-01 19:28:3494 AutoReset<bool> auto_reset_in_run(&g_is_opening_new_window, true);
[email protected]d630d7d52010-02-21 00:55:1195 Browser::OpenEmptyWindow(profile);
96 }
97
98 Browser* browser = BrowserList::GetLastActive();
99 CHECK(browser);
100 return browser;
101}
102
103// Activates a browser window having the given profile (the last one active) if
104// possible or creates an empty one if necessary. Returns a pointer to the
105// activated/new |Browser|.
106Browser* ActivateOrCreateBrowser(Profile* profile) {
107 if (Browser* browser = ActivateBrowser(profile))
108 return browser;
109 return CreateBrowser(profile);
110}
111
[email protected]12dc3d42010-02-22 23:37:12112// This task synchronizes preferences (under "org.chromium.Chromium" or
113// "com.google.Chrome"), in particular, writes them out to disk.
114class PrefsSyncTask : public Task {
115 public:
116 PrefsSyncTask() {}
117 virtual ~PrefsSyncTask() {}
118 virtual void Run() {
119 if (!CFPreferencesAppSynchronize(app_mode::kAppPrefsID))
120 LOG(WARNING) << "Error recording application bundle path.";
121 }
122};
123
124// Record the location of the application bundle (containing the main framework)
125// from which Chromium was loaded. This is used by app mode shims to find
126// Chromium.
127void RecordLastRunAppBundlePath() {
128 // Going up three levels from |chrome::GetVersionedDirectory()| gives the
129 // real, user-visible app bundle directory. (The alternatives give either the
130 // framework's path or the initial app's path, which may be an app mode shim
131 // or a unit test.)
132 FilePath appBundlePath =
133 chrome::GetVersionedDirectory().DirName().DirName().DirName();
134 CFPreferencesSetAppValue(app_mode::kLastRunAppBundlePathPrefsKey,
135 base::SysUTF8ToCFStringRef(appBundlePath.value()),
136 app_mode::kAppPrefsID);
137
138 // Sync after a delay avoid I/O contention on startup; 1500 ms is plenty.
[email protected]d04e7662010-10-10 22:24:48139 BrowserThread::PostDelayedTask(BrowserThread::FILE, FROM_HERE,
140 new PrefsSyncTask(), 1500);
[email protected]12dc3d42010-02-22 23:37:12141}
142
[email protected]d630d7d52010-02-21 00:55:11143} // anonymous namespace
144
145@interface AppController(Private)
[email protected]88d74942009-01-21 22:04:44146- (void)initMenuState;
[email protected]eef99c22010-08-17 05:55:16147- (void)registerServicesMenuTypesTo:(NSApplication*)app;
[email protected]d630d7d52010-02-21 00:55:11148- (void)openUrls:(const std::vector<GURL>&)urls;
[email protected]57750f822009-04-21 21:43:09149- (void)getUrl:(NSAppleEventDescriptor*)event
150 withReply:(NSAppleEventDescriptor*)reply;
[email protected]449dd2f2009-05-27 13:04:00151- (void)windowLayeringDidChange:(NSNotification*)inNotification;
[email protected]dd6ab1282010-07-20 01:07:32152- (void)checkForAnyKeyWindows;
[email protected]73f5b362009-08-10 23:58:21153- (BOOL)userWillWaitForInProgressDownloads:(int)downloadCount;
[email protected]09729a552009-08-03 23:21:41154- (BOOL)shouldQuitWithInProgressDownloads;
[email protected]d989f07c2009-11-14 00:35:46155- (void)showPreferencesWindow:(id)sender
156 page:(OptionsPage)page
157 profile:(Profile*)profile;
[email protected]93b59fc2010-12-21 20:00:47158- (void)executeApplication:(id)sender;
[email protected]88d74942009-01-21 22:04:44159@end
[email protected]1bcdb532009-01-16 17:47:57160
161@implementation AppController
162
[email protected]caadfca2010-01-22 21:51:21163@synthesize startupComplete = startupComplete_;
164
[email protected]cd63ef62009-05-06 19:41:37165// This method is called very early in application startup (ie, before
166// the profile is loaded or any preferences have been registered). Defer any
167// user-data initialization until -applicationDidFinishLaunching:.
[email protected]88d74942009-01-21 22:04:44168- (void)awakeFromNib {
[email protected]136140c2009-05-19 13:58:25169 // We need to register the handlers early to catch events fired on launch.
[email protected]57750f822009-04-21 21:43:09170 NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager];
171 [em setEventHandler:self
172 andSelector:@selector(getUrl:withReply:)
173 forEventClass:kInternetEventClass
174 andEventID:kAEGetURL];
175 [em setEventHandler:self
176 andSelector:@selector(getUrl:withReply:)
177 forEventClass:'WWW!' // A particularly ancient AppleEvent that dates
178 andEventID:'OURL']; // back to the Spyglass days.
[email protected]cd63ef62009-05-06 19:41:37179
[email protected]449dd2f2009-05-27 13:04:00180 // Register for various window layering changes. We use these to update
181 // various UI elements (command-key equivalents, etc) when the frontmost
182 // window changes.
183 NSNotificationCenter* notificationCenter =
184 [NSNotificationCenter defaultCenter];
[email protected]ce560f82009-06-03 09:39:44185 [notificationCenter
[email protected]449dd2f2009-05-27 13:04:00186 addObserver:self
187 selector:@selector(windowLayeringDidChange:)
188 name:NSWindowDidBecomeKeyNotification
189 object:nil];
190 [notificationCenter
191 addObserver:self
192 selector:@selector(windowLayeringDidChange:)
193 name:NSWindowDidResignKeyNotification
194 object:nil];
195 [notificationCenter
196 addObserver:self
197 selector:@selector(windowLayeringDidChange:)
198 name:NSWindowDidBecomeMainNotification
199 object:nil];
200 [notificationCenter
201 addObserver:self
202 selector:@selector(windowLayeringDidChange:)
203 name:NSWindowDidResignMainNotification
204 object:nil];
205
206 // Register for a notification that the number of tabs changes in windows
207 // so we can adjust the close tab/window command keys.
208 [notificationCenter
209 addObserver:self
210 selector:@selector(tabsChanged:)
211 name:kTabStripNumberOfTabsChanged
212 object:nil];
213
[email protected]136140c2009-05-19 13:58:25214 // Set up the command updater for when there are no windows open
215 [self initMenuState];
[email protected]f4acfae82009-09-11 00:33:38216
217 // Activate (bring to foreground) if asked to do so. On
218 // Windows this logic isn't necessary since
219 // BrowserWindow::Activate() calls ::SetForegroundWindow() which is
220 // adequate. On Mac, BrowserWindow::Activate() calls -[NSWindow
221 // makeKeyAndOrderFront:] which does not activate the application
222 // itself.
223 const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess();
224 if (parsed_command_line.HasSwitch(switches::kActivateOnLaunch)) {
225 [NSApp activateIgnoringOtherApps:YES];
226 }
[email protected]136140c2009-05-19 13:58:25227}
228
[email protected]7dc8c6b2010-04-09 17:02:50229// (NSApplicationDelegate protocol) This is the Apple-approved place to override
230// the default handlers.
231- (void)applicationWillFinishLaunching:(NSNotification*)notification {
[email protected]58877932010-05-25 05:43:11232 // Nothing here right now.
[email protected]7dc8c6b2010-04-09 17:02:50233}
234
235- (BOOL)tryToTerminateApplication:(NSApplication*)app {
[email protected]3076fad2010-04-28 18:11:45236 // Check for in-process downloads, and prompt the user if they really want
237 // to quit (and thus cancel downloads). Only check if we're not already
238 // shutting down, else the user might be prompted multiple times if the
239 // download isn't stopped before terminate is called again.
240 if (!browser_shutdown::IsTryingToQuit() &&
241 ![self shouldQuitWithInProgressDownloads])
242 return NO;
243
[email protected]7dc8c6b2010-04-09 17:02:50244 // TODO(viettrungluu): Remove Apple Event handlers here? (It's safe to leave
245 // them in, but I'm not sure about UX; we'd also want to disable other things
246 // though.) http://crbug.com/40861
247
[email protected]177aceb2010-11-03 16:17:41248 // Check if the user really wants to quit by employing the confirm-to-quit
249 // mechanism.
250 if (!browser_shutdown::IsTryingToQuit() &&
251 [self applicationShouldTerminate:app] != NSTerminateNow)
252 return NO;
253
[email protected]c984d9f2010-07-20 20:52:20254 size_t num_browsers = BrowserList::size();
[email protected]7dc8c6b2010-04-09 17:02:50255
[email protected]e7dd6d82010-10-07 23:21:12256 // Give any print jobs in progress time to finish.
257 if (!browser_shutdown::IsTryingToQuit())
258 g_browser_process->print_job_manager()->StopJobs(true);
259
[email protected]c984d9f2010-07-20 20:52:20260 // Initiate a shutdown (via BrowserList::CloseAllBrowsers()) if we aren't
261 // already shutting down.
262 if (!browser_shutdown::IsTryingToQuit())
[email protected]c6032e82010-09-13 20:06:05263 BrowserList::CloseAllBrowsers();
[email protected]7dc8c6b2010-04-09 17:02:50264
[email protected]c984d9f2010-07-20 20:52:20265 return num_browsers == 0 ? YES : NO;
[email protected]7dc8c6b2010-04-09 17:02:50266}
267
268- (void)stopTryingToTerminateApplication:(NSApplication*)app {
269 if (browser_shutdown::IsTryingToQuit()) {
270 // Reset the "trying to quit" state, so that closing all browser windows
271 // will no longer lead to termination.
272 browser_shutdown::SetTryingToQuit(false);
273
274 // TODO(viettrungluu): Were we to remove Apple Event handlers above, we
275 // would have to reinstall them here. http://crbug.com/40861
276 }
[email protected]a9e8afc2009-08-11 22:03:17277}
278
[email protected]177aceb2010-11-03 16:17:41279- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)app {
280 // Check if the experiment is enabled.
281 const CommandLine* commandLine(CommandLine::ForCurrentProcess());
282 if (!commandLine->HasSwitch(switches::kEnableConfirmToQuit))
283 return NSTerminateNow;
284
285 // If the application is going to terminate as the result of a Cmd+Q
286 // invocation, use the special sauce to prevent accidental quitting.
287 // http://dev.chromium.org/developers/design-documents/confirm-to-quit-experiment
[email protected]177aceb2010-11-03 16:17:41288
[email protected]2020fd0b2010-12-10 01:16:50289 // How long the user must hold down Cmd+Q to confirm the quit.
290 const NSTimeInterval kTimeToConfirmQuit = 1.5;
291 // Leeway between the |targetDate| and the current time that will confirm a
292 // quit.
293 const NSTimeInterval kTimeDeltaFuzzFactor = 1.0;
294 // Duration of the window fade out animation.
295 const NSTimeInterval kWindowFadeAnimationDuration = 0.2;
[email protected]177aceb2010-11-03 16:17:41296
[email protected]2020fd0b2010-12-10 01:16:50297 // This logic is only for keyboard-initiated quits.
298 if ([[app currentEvent] type] != NSKeyDown)
299 return NSTerminateNow;
[email protected]177aceb2010-11-03 16:17:41300
[email protected]2020fd0b2010-12-10 01:16:50301 // If this is the second of two such attempts to quit within a certain time
302 // interval, then just quit.
303 // Time of last quit attempt, if any.
304 static NSDate* lastQuitAttempt; // Initially nil, as it's static.
305 NSDate* timeNow = [NSDate date];
306 if (lastQuitAttempt &&
307 [timeNow timeIntervalSinceDate:lastQuitAttempt] < kTimeDeltaFuzzFactor) {
308 return NSTerminateNow;
309 } else {
310 [lastQuitAttempt release]; // Harmless if already nil.
311 lastQuitAttempt = [timeNow retain]; // Record this attempt for next time.
312 }
[email protected]177aceb2010-11-03 16:17:41313
[email protected]2020fd0b2010-12-10 01:16:50314 // Show the info panel that explains what the user must to do confirm quit.
315 [[ConfirmQuitPanelController sharedController] showWindow:self];
316
317 // Spin a nested run loop until the |targetDate| is reached or a KeyUp event
318 // is sent.
319 NSDate* targetDate =
320 [NSDate dateWithTimeIntervalSinceNow:kTimeToConfirmQuit];
321 BOOL willQuit = NO;
322 NSEvent* nextEvent = nil;
323 do {
324 // Dequeue events until a key up is received.
325 nextEvent = [app nextEventMatchingMask:NSKeyUpMask
326 untilDate:nil
327 inMode:NSEventTrackingRunLoopMode
328 dequeue:YES];
329
330 // Wait for the time expiry to happen. Once past the hold threshold,
331 // commit to quitting and hide all the open windows.
332 if (!willQuit) {
333 NSDate* now = [NSDate date];
334 NSTimeInterval difference = [targetDate timeIntervalSinceDate:now];
335 if (difference < kTimeDeltaFuzzFactor) {
336 willQuit = YES;
337
338 // At this point, the quit has been confirmed and windows should all
339 // fade out to convince the user to release the key combo to finalize
340 // the quit.
341 [NSAnimationContext beginGrouping];
342 [[NSAnimationContext currentContext] setDuration:
343 kWindowFadeAnimationDuration];
344 for (NSWindow* aWindow in [app windows]) {
345 // Windows that are set to animate and have a delegate do not
346 // expect to be animated by other things and could result in an
347 // invalid state. If a window is set up like so, just force the
348 // alpha value to 0. Otherwise, animate all pretty and stuff.
349 if (![[aWindow animationForKey:@"alphaValue"] delegate]) {
350 [[aWindow animator] setAlphaValue:0.0];
351 } else {
352 [aWindow setAlphaValue:0.0];
[email protected]177aceb2010-11-03 16:17:41353 }
[email protected]177aceb2010-11-03 16:17:41354 }
[email protected]2020fd0b2010-12-10 01:16:50355 [NSAnimationContext endGrouping];
[email protected]177aceb2010-11-03 16:17:41356 }
[email protected]177aceb2010-11-03 16:17:41357 }
[email protected]2020fd0b2010-12-10 01:16:50358 } while (!nextEvent);
359
360 // The user has released the key combo. Discard any events (i.e. the
361 // repeated KeyDown Cmd+Q).
362 [app discardEventsMatchingMask:NSAnyEventMask beforeEvent:nextEvent];
363
364 if (willQuit) {
365 // The user held down the combination long enough that quitting should
366 // happen.
367 return NSTerminateNow;
368 } else {
369 // Slowly fade the confirm window out in case the user doesn't
370 // understand what they have to do to quit.
371 [[ConfirmQuitPanelController sharedController] dismissPanel];
372 return NSTerminateCancel;
373 }
[email protected]177aceb2010-11-03 16:17:41374
375 // Default case: terminate.
376 return NSTerminateNow;
377}
378
[email protected]449dd2f2009-05-27 13:04:00379// Called when the app is shutting down. Clean-up as appropriate.
[email protected]7dc8c6b2010-04-09 17:02:50380- (void)applicationWillTerminate:(NSNotification*)aNotification {
[email protected]3b6aa8b62009-09-15 21:36:11381 NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager];
382 [em removeEventHandlerForEventClass:kInternetEventClass
383 andEventID:kAEGetURL];
384 [em removeEventHandlerForEventClass:'WWW!'
385 andEventID:'OURL'];
[email protected]3b6aa8b62009-09-15 21:36:11386
[email protected]7dc8c6b2010-04-09 17:02:50387 // There better be no browser windows left at this point.
388 CHECK_EQ(BrowserList::size(), 0u);
[email protected]3b6aa8b62009-09-15 21:36:11389
[email protected]c984d9f2010-07-20 20:52:20390 // Tell BrowserList not to keep the browser process alive. Once all the
391 // browsers get dealloc'd, it will stop the RunLoop and fall back into main().
392 BrowserList::EndKeepAlive();
[email protected]fbc947b2009-06-19 13:28:24393
[email protected]d4e18652009-11-19 21:52:16394 // Close these off if they have open windows.
395 [prefsController_ close];
396 [aboutController_ close];
397
[email protected]449dd2f2009-05-27 13:04:00398 [[NSNotificationCenter defaultCenter] removeObserver:self];
399}
400
[email protected]3b6aa8b62009-09-15 21:36:11401- (void)didEndMainMessageLoop {
402 DCHECK(!BrowserList::HasBrowserWithProfile([self defaultProfile]));
403 if (!BrowserList::HasBrowserWithProfile([self defaultProfile])) {
404 // As we're shutting down, we need to nuke the TabRestoreService, which
405 // will start the shutdown of the NavigationControllers and allow for
406 // proper shutdown. If we don't do this, Chrome won't shut down cleanly,
407 // and may end up crashing when some thread tries to use the IO thread (or
408 // another thread) that is no longer valid.
409 [self defaultProfile]->ResetTabRestoreService();
410 }
411}
412
[email protected]449dd2f2009-05-27 13:04:00413// Helper routine to get the window controller if the key window is a tabbed
414// window, or nil if not. Examples of non-tabbed windows are "about" or
415// "preferences".
416- (TabWindowController*)keyWindowTabController {
417 NSWindowController* keyWindowController =
[email protected]5dbabd02009-09-17 20:44:13418 [[NSApp keyWindow] windowController];
[email protected]449dd2f2009-05-27 13:04:00419 if ([keyWindowController isKindOfClass:[TabWindowController class]])
420 return (TabWindowController*)keyWindowController;
421
422 return nil;
423}
424
[email protected]74c0c642009-09-22 21:25:50425// Helper routine to get the window controller if the main window is a tabbed
426// window, or nil if not. Examples of non-tabbed windows are "about" or
427// "preferences".
428- (TabWindowController*)mainWindowTabController {
429 NSWindowController* mainWindowController =
430 [[NSApp mainWindow] windowController];
431 if ([mainWindowController isKindOfClass:[TabWindowController class]])
432 return (TabWindowController*)mainWindowController;
433
434 return nil;
435}
436
[email protected]449dd2f2009-05-27 13:04:00437// If the window has tabs, make "close window" be cmd-shift-w, otherwise leave
438// it as the normal cmd-w. Capitalization of the key equivalent affects whether
439// the shift modifer is used.
440- (void)adjustCloseWindowMenuItemKeyEquivalent:(BOOL)inHaveTabs {
441 [closeWindowMenuItem_ setKeyEquivalent:(inHaveTabs ? @"W" : @"w")];
[email protected]74c0c642009-09-22 21:25:50442 [closeWindowMenuItem_ setKeyEquivalentModifierMask:NSCommandKeyMask];
[email protected]449dd2f2009-05-27 13:04:00443}
444
445// If the window has tabs, make "close tab" take over cmd-w, otherwise it
446// shouldn't have any key-equivalent because it should be disabled.
447- (void)adjustCloseTabMenuItemKeyEquivalent:(BOOL)hasTabs {
448 if (hasTabs) {
449 [closeTabMenuItem_ setKeyEquivalent:@"w"];
450 [closeTabMenuItem_ setKeyEquivalentModifierMask:NSCommandKeyMask];
451 } else {
452 [closeTabMenuItem_ setKeyEquivalent:@""];
453 [closeTabMenuItem_ setKeyEquivalentModifierMask:0];
454 }
455}
456
[email protected]74c0c642009-09-22 21:25:50457// Explicitly remove any command-key equivalents from the close tab/window
458// menus so that nothing can go haywire if we get a user action during pending
459// updates.
460- (void)clearCloseMenuItemKeyEquivalents {
461 [closeTabMenuItem_ setKeyEquivalent:@""];
462 [closeTabMenuItem_ setKeyEquivalentModifierMask:0];
463 [closeWindowMenuItem_ setKeyEquivalent:@""];
464 [closeWindowMenuItem_ setKeyEquivalentModifierMask:0];
465}
466
[email protected]449dd2f2009-05-27 13:04:00467// See if we have a window with tabs open, and adjust the key equivalents for
[email protected]668c911c2010-03-08 17:05:25468// Close Tab/Close Window accordingly.
[email protected]449dd2f2009-05-27 13:04:00469- (void)fixCloseMenuItemKeyEquivalents {
[email protected]668c911c2010-03-08 17:05:25470 fileMenuUpdatePending_ = NO;
[email protected]449dd2f2009-05-27 13:04:00471 TabWindowController* tabController = [self keyWindowTabController];
[email protected]74c0c642009-09-22 21:25:50472 if (!tabController && ![NSApp keyWindow]) {
473 // There might be a small amount of time where there is no key window,
474 // so just use our main browser window if there is one.
475 tabController = [self mainWindowTabController];
476 }
[email protected]449dd2f2009-05-27 13:04:00477 BOOL windowWithMultipleTabs =
478 (tabController && [tabController numberOfTabs] > 1);
479 [self adjustCloseWindowMenuItemKeyEquivalent:windowWithMultipleTabs];
480 [self adjustCloseTabMenuItemKeyEquivalent:windowWithMultipleTabs];
[email protected]449dd2f2009-05-27 13:04:00481}
482
483// Fix up the "close tab/close window" command-key equivalents. We do this
484// after a delay to ensure that window layer state has been set by the time
[email protected]f2a5c342010-01-21 17:00:29485// we do the enabling. This should only be called on the main thread, code that
486// calls this (even as a side-effect) from other threads needs to be fixed.
[email protected]449dd2f2009-05-27 13:04:00487- (void)delayedFixCloseMenuItemKeyEquivalents {
[email protected]f2a5c342010-01-21 17:00:29488 DCHECK([NSThread isMainThread]);
[email protected]449dd2f2009-05-27 13:04:00489 if (!fileMenuUpdatePending_) {
[email protected]74c0c642009-09-22 21:25:50490 // The OS prefers keypresses to timers, so it's possible that a cmd-w
491 // can sneak in before this timer fires. In order to prevent that from
492 // having any bad consequences, just clear the keys combos altogether. They
493 // will be reset when the timer eventually fires.
[email protected]668c911c2010-03-08 17:05:25494 if ([NSThread isMainThread]) {
495 fileMenuUpdatePending_ = YES;
496 [self clearCloseMenuItemKeyEquivalents];
497 [self performSelector:@selector(fixCloseMenuItemKeyEquivalents)
498 withObject:nil
499 afterDelay:0];
500 } else {
501 // This shouldn't be happening, but if it does, force it to the main
502 // thread to avoid dropping the update. Don't mess with
503 // |fileMenuUpdatePending_| as it's not expected to be threadsafe and
504 // there could be a race between the selector finishing and setting the
505 // flag.
506 [self
507 performSelectorOnMainThread:@selector(fixCloseMenuItemKeyEquivalents)
[email protected]f2a5c342010-01-21 17:00:29508 withObject:nil
509 waitUntilDone:NO];
[email protected]668c911c2010-03-08 17:05:25510 }
[email protected]449dd2f2009-05-27 13:04:00511 }
512}
513
514// Called when we get a notification about the window layering changing to
515// update the UI based on the new main window.
516- (void)windowLayeringDidChange:(NSNotification*)notify {
517 [self delayedFixCloseMenuItemKeyEquivalents];
[email protected]dd6ab1282010-07-20 01:07:32518
519 if ([notify name] == NSWindowDidResignKeyNotification) {
520 // If a window is closed, this notification is fired but |[NSApp keyWindow]|
521 // returns nil regardless of whether any suitable candidates for the key
522 // window remain. It seems that the new key window for the app is not set
523 // until after this notification is fired, so a check is performed after the
524 // run loop is allowed to spin.
525 [self performSelector:@selector(checkForAnyKeyWindows)
526 withObject:nil
527 afterDelay:0.0];
528 }
529}
530
531- (void)checkForAnyKeyWindows {
532 if ([NSApp keyWindow])
533 return;
534
535 NotificationService::current()->Notify(
536 NotificationType::NO_KEY_WINDOW,
537 NotificationService::AllSources(),
538 NotificationService::NoDetails());
[email protected]449dd2f2009-05-27 13:04:00539}
540
541// Called when the number of tabs changes in one of the browser windows. The
542// object is the tab strip controller, but we don't currently care.
543- (void)tabsChanged:(NSNotification*)notify {
[email protected]e72746b12009-10-19 14:11:51544 // We don't need to do this on a delay, as in the method above, because the
545 // window layering isn't changing. As a result, there's no chance that a
546 // different window will sneak in as the key window and cause the problems
547 // we hacked around above by clearing the key equivalents.
548 [self fixCloseMenuItemKeyEquivalents];
[email protected]449dd2f2009-05-27 13:04:00549}
550
[email protected]414fde592009-05-21 16:14:43551// If the auto-update interval is not set, make it 5 hours.
552// This code is specific to Mac Chrome Dev Channel.
553// Placed here for 2 reasons:
554// 1) Same spot as other Pref stuff
555// 2) Try and be friendly by keeping this after app launch
556// TODO(jrg): remove once we go Beta.
557- (void)setUpdateCheckInterval {
558#if defined(GOOGLE_CHROME_BUILD)
559 CFStringRef app = (CFStringRef)@"com.google.Keystone.Agent";
560 CFStringRef checkInterval = (CFStringRef)@"checkInterval";
561 CFPropertyListRef plist = CFPreferencesCopyAppValue(checkInterval, app);
562 if (!plist) {
563 const float fiveHoursInSeconds = 5.0 * 60.0 * 60.0;
[email protected]7dc8c6b2010-04-09 17:02:50564 NSNumber* value = [NSNumber numberWithFloat:fiveHoursInSeconds];
[email protected]414fde592009-05-21 16:14:43565 CFPreferencesSetAppValue(checkInterval, value, app);
566 CFPreferencesAppSynchronize(app);
567 }
568#endif
569}
570
[email protected]136140c2009-05-19 13:58:25571// This is called after profiles have been loaded and preferences registered.
572// It is safe to access the default profile here.
573- (void)applicationDidFinishLaunching:(NSNotification*)notify {
[email protected]c984d9f2010-07-20 20:52:20574 // Notify BrowserList to keep the application running so it doesn't go away
575 // when all the browser windows get closed.
576 BrowserList::StartKeepAlive();
[email protected]136140c2009-05-19 13:58:25577
[email protected]e5575022009-08-22 00:31:22578 bookmarkMenuBridge_.reset(new BookmarkMenuBridge([self defaultProfile]));
[email protected]1c84c82c2009-07-27 15:37:25579 historyMenuBridge_.reset(new HistoryMenuBridge([self defaultProfile]));
[email protected]cd63ef62009-05-06 19:41:37580
[email protected]414fde592009-05-21 16:14:43581 [self setUpdateCheckInterval];
[email protected]2bcec612009-05-14 17:50:53582
583 // Build up the encoding menu, the order of the items differs based on the
584 // current locale (see http://crbug.com/7647 for details).
585 // We need a valid g_browser_process to get the profile which is why we can't
586 // call this from awakeFromNib.
[email protected]fc349322009-09-23 22:28:23587 NSMenu* view_menu = [[[NSApp mainMenu] itemWithTag:IDC_VIEW_MENU] submenu];
588 NSMenuItem* encoding_menu_item = [view_menu itemWithTag:IDC_ENCODING_MENU];
[email protected]7dc8c6b2010-04-09 17:02:50589 NSMenu* encoding_menu = [encoding_menu_item submenu];
[email protected]fc349322009-09-23 22:28:23590 EncodingMenuControllerDelegate::BuildEncodingMenu([self defaultProfile],
591 encoding_menu);
[email protected]2bcec612009-05-14 17:50:53592
[email protected]f2c20fa2009-12-01 17:42:02593 // Since Chrome is localized to more languages than the OS, tell Cocoa which
594 // menu is the Help so it can add the search item to it.
[email protected]caadfca2010-01-22 21:51:21595 if (helpMenu_ && [NSApp respondsToSelector:@selector(setHelpMenu:)])
[email protected]f2c20fa2009-12-01 17:42:02596 [NSApp setHelpMenu:helpMenu_];
[email protected]f2c20fa2009-12-01 17:42:02597
[email protected]12dc3d42010-02-22 23:37:12598 // Record the path to the (browser) app bundle; this is used by the app mode
599 // shim.
600 RecordLastRunAppBundlePath();
601
[email protected]eef99c22010-08-17 05:55:16602 // Makes "Services" menu items available.
603 [self registerServicesMenuTypesTo:[notify object]];
604
[email protected]caadfca2010-01-22 21:51:21605 startupComplete_ = YES;
606
607 // TODO(viettrungluu): This is very temporary, since this should be done "in"
608 // |BrowserMain()|, i.e., this list of startup URLs should be appended to the
609 // (probably-empty) list of URLs from the command line.
[email protected]d630d7d52010-02-21 00:55:11610 if (startupUrls_.size()) {
611 [self openUrls:startupUrls_];
612 [self clearStartupUrls];
[email protected]caadfca2010-01-22 21:51:21613 }
[email protected]ad2a3ded2010-08-27 13:19:05614
615 const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess();
616 if (!parsed_command_line.HasSwitch(switches::kEnableExposeForTabs)) {
617 [tabposeMenuItem_ setHidden:YES];
618 }
[email protected]7c321082009-02-09 15:35:47619}
620
[email protected]a96ec6a2009-11-04 17:27:08621// This is called after profiles have been loaded and preferences registered.
622// It is safe to access the default profile here.
623- (void)applicationDidBecomeActive:(NSNotification*)notify {
624 NotificationService::current()->Notify(NotificationType::APP_ACTIVATED,
625 NotificationService::AllSources(),
626 NotificationService::NoDetails());
627}
628
[email protected]73f5b362009-08-10 23:58:21629// Helper function for populating and displaying the in progress downloads at
630// exit alert panel.
631- (BOOL)userWillWaitForInProgressDownloads:(int)downloadCount {
[email protected]e8b5f7882009-09-14 20:59:13632 NSString* warningText = nil;
633 NSString* explanationText = nil;
[email protected]73f5b362009-08-10 23:58:21634 NSString* waitTitle = nil;
635 NSString* exitTitle = nil;
636
[email protected]757127052010-04-27 14:42:26637 string16 product_name = l10n_util::GetStringUTF16(IDS_PRODUCT_NAME);
[email protected]9dcb59662009-09-23 01:15:50638
[email protected]73f5b362009-08-10 23:58:21639 // Set the dialog text based on whether or not there are multiple downloads.
640 if (downloadCount == 1) {
[email protected]e8b5f7882009-09-14 20:59:13641 // Dialog text: warning and explanation.
[email protected]757127052010-04-27 14:42:26642 warningText = l10n_util::GetNSStringF(
643 IDS_SINGLE_DOWNLOAD_REMOVE_CONFIRM_WARNING, product_name);
644 explanationText = l10n_util::GetNSStringF(
645 IDS_SINGLE_DOWNLOAD_REMOVE_CONFIRM_EXPLANATION, product_name);
[email protected]73f5b362009-08-10 23:58:21646
647 // Cancel download and exit button text.
[email protected]757127052010-04-27 14:42:26648 exitTitle = l10n_util::GetNSString(
649 IDS_SINGLE_DOWNLOAD_REMOVE_CONFIRM_OK_BUTTON_LABEL);
[email protected]73f5b362009-08-10 23:58:21650
651 // Wait for download button text.
[email protected]757127052010-04-27 14:42:26652 waitTitle = l10n_util::GetNSString(
653 IDS_SINGLE_DOWNLOAD_REMOVE_CONFIRM_CANCEL_BUTTON_LABEL);
[email protected]73f5b362009-08-10 23:58:21654 } else {
[email protected]e8b5f7882009-09-14 20:59:13655 // Dialog text: warning and explanation.
[email protected]757127052010-04-27 14:42:26656 warningText = l10n_util::GetNSStringF(
657 IDS_MULTIPLE_DOWNLOADS_REMOVE_CONFIRM_WARNING, product_name,
[email protected]528c56d2010-07-30 19:28:44658 base::IntToString16(downloadCount));
[email protected]757127052010-04-27 14:42:26659 explanationText = l10n_util::GetNSStringF(
660 IDS_MULTIPLE_DOWNLOADS_REMOVE_CONFIRM_EXPLANATION, product_name);
[email protected]73f5b362009-08-10 23:58:21661
662 // Cancel downloads and exit button text.
[email protected]757127052010-04-27 14:42:26663 exitTitle = l10n_util::GetNSString(
664 IDS_MULTIPLE_DOWNLOADS_REMOVE_CONFIRM_OK_BUTTON_LABEL);
[email protected]73f5b362009-08-10 23:58:21665
666 // Wait for downloads button text.
[email protected]757127052010-04-27 14:42:26667 waitTitle = l10n_util::GetNSString(
668 IDS_MULTIPLE_DOWNLOADS_REMOVE_CONFIRM_CANCEL_BUTTON_LABEL);
[email protected]73f5b362009-08-10 23:58:21669 }
670
671 // 'waitButton' is the default choice.
[email protected]e8b5f7882009-09-14 20:59:13672 int choice = NSRunAlertPanel(warningText, explanationText,
673 waitTitle, exitTitle, nil);
[email protected]73f5b362009-08-10 23:58:21674 return choice == NSAlertDefaultReturn ? YES : NO;
675}
676
677// Check all profiles for in progress downloads, and if we find any, prompt the
[email protected]09729a552009-08-03 23:21:41678// user to see if we should continue to exit (and thus cancel the downloads), or
679// if we should wait.
680- (BOOL)shouldQuitWithInProgressDownloads {
[email protected]73f5b362009-08-10 23:58:21681 ProfileManager* profile_manager = g_browser_process->profile_manager();
682 if (!profile_manager)
683 return YES;
[email protected]09729a552009-08-03 23:21:41684
[email protected]73f5b362009-08-10 23:58:21685 ProfileManager::const_iterator it = profile_manager->begin();
686 for (; it != profile_manager->end(); ++it) {
687 Profile* profile = *it;
[email protected]09729a552009-08-03 23:21:41688 DownloadManager* download_manager = profile->GetDownloadManager();
[email protected]73f5b362009-08-10 23:58:21689 if (download_manager && download_manager->in_progress_count() > 0) {
690 int downloadCount = download_manager->in_progress_count();
691 if ([self userWillWaitForInProgressDownloads:downloadCount]) {
692 // Create a new browser window (if necessary) and navigate to the
693 // downloads page if the user chooses to wait.
694 Browser* browser = BrowserList::FindBrowserWithProfile(profile);
695 if (!browser) {
696 browser = Browser::Create(profile);
697 browser->window()->Show();
698 }
699 DCHECK(browser);
700 browser->ShowDownloadsTab();
701 return NO;
702 }
[email protected]09729a552009-08-03 23:21:41703
[email protected]73f5b362009-08-10 23:58:21704 // User wants to exit.
705 return YES;
[email protected]09729a552009-08-03 23:21:41706 }
[email protected]09729a552009-08-03 23:21:41707 }
708
[email protected]73f5b362009-08-10 23:58:21709 // No profiles or active downloads found, okay to exit.
[email protected]09729a552009-08-03 23:21:41710 return YES;
711}
712
[email protected]fbc947b2009-06-19 13:28:24713// Called to determine if we should enable the "restore tab" menu item.
714// Checks with the TabRestoreService to see if there's anything there to
715// restore and returns YES if so.
716- (BOOL)canRestoreTab {
717 TabRestoreService* service = [self defaultProfile]->GetTabRestoreService();
718 return service && !service->entries().empty();
719}
720
[email protected]f6c15fa2010-06-04 20:08:21721// Returns true if there is not a modal window (either window- or application-
722// modal) blocking the active browser. Note that tab modal dialogs (HTTP auth
723// sheets) will not count as blocking the browser. But things like open/save
724// dialogs that are window modal will block the browser.
725- (BOOL)keyWindowIsNotModal {
[email protected]6a281332009-10-13 17:41:06726 Browser* browser = BrowserList::GetLastActive();
[email protected]dc76e8362010-06-11 16:29:52727 return [NSApp modalWindow] == nil && (!browser ||
[email protected]6a281332009-10-13 17:41:06728 ![[browser->window()->GetNativeHandle() attachedSheet]
[email protected]f6c15fa2010-06-04 20:08:21729 isKindOfClass:[NSWindow class]]);
[email protected]6a281332009-10-13 17:41:06730}
731
[email protected]88d74942009-01-21 22:04:44732// Called to validate menu items when there are no key windows. All the
733// items we care about have been set with the |commandDispatch:| action and
734// a target of FirstResponder in IB. If it's not one of those, let it
735// continue up the responder chain to be handled elsewhere. We pull out the
736// tag as the cross-platform constant to differentiate and dispatch the
737// various commands.
738- (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item {
739 SEL action = [item action];
740 BOOL enable = NO;
741 if (action == @selector(commandDispatch:)) {
742 NSInteger tag = [item tag];
[email protected]fbc947b2009-06-19 13:28:24743 if (menuState_->SupportsCommand(tag)) {
744 switch (tag) {
[email protected]419eb2a2009-11-11 16:26:26745 // The File Menu commands are not automatically disabled by Cocoa when a
746 // dialog sheet obscures the browser window, so we disable several of
747 // them here. We don't need to include IDC_CLOSE_WINDOW, because
748 // app_controller is only activated when there are no key windows (see
749 // function comment).
[email protected]fbc947b2009-06-19 13:28:24750 case IDC_RESTORE_TAB:
[email protected]f6c15fa2010-06-04 20:08:21751 enable = [self keyWindowIsNotModal] && [self canRestoreTab];
[email protected]6a281332009-10-13 17:41:06752 break;
[email protected]f6c15fa2010-06-04 20:08:21753 // Browser-level items that open in new tabs should not open if there's
754 // a window- or app-modal dialog.
[email protected]6a281332009-10-13 17:41:06755 case IDC_OPEN_FILE:
[email protected]6a281332009-10-13 17:41:06756 case IDC_NEW_TAB:
[email protected]f6c15fa2010-06-04 20:08:21757 case IDC_SHOW_HISTORY:
758 case IDC_SHOW_BOOKMARK_MANAGER:
759 enable = [self keyWindowIsNotModal];
760 break;
761 // Browser-level items that open in new windows.
762 case IDC_NEW_WINDOW:
763 case IDC_TASK_MANAGER:
764 // Allow the user to open a new window if there's a window-modal
765 // dialog.
766 enable = [self keyWindowIsNotModal] || ([NSApp modalWindow] == nil);
[email protected]fbc947b2009-06-19 13:28:24767 break;
[email protected]446e16a2010-05-03 21:23:58768 case IDC_SYNC_BOOKMARKS: {
769 Profile* defaultProfile = [self defaultProfile];
770 // The profile may be NULL during shutdown -- see
771 // http://code.google.com/p/chromium/issues/detail?id=43048 .
772 //
773 // TODO(akalin,viettrungluu): Figure out whether this method
774 // can be prevented from being called if defaultProfile is
775 // NULL.
776 if (!defaultProfile) {
777 LOG(WARNING)
778 << "NULL defaultProfile detected -- not doing anything";
779 break;
780 }
[email protected]d3b98c82010-07-14 07:45:59781 enable = defaultProfile->IsSyncAccessible() &&
782 [self keyWindowIsNotModal];
[email protected]446e16a2010-05-03 21:23:58783 sync_ui_util::UpdateSyncItem(item, enable, defaultProfile);
[email protected]f9bc9b92009-11-24 00:55:35784 break;
[email protected]446e16a2010-05-03 21:23:58785 }
[email protected]fbc947b2009-06-19 13:28:24786 default:
[email protected]f6c15fa2010-06-04 20:08:21787 enable = menuState_->IsCommandEnabled(tag) ?
788 [self keyWindowIsNotModal] : NO;
[email protected]fbc947b2009-06-19 13:28:24789 }
790 }
[email protected]3b6aa8b62009-09-15 21:36:11791 } else if (action == @selector(terminate:)) {
[email protected]88d74942009-01-21 22:04:44792 enable = YES;
[email protected]3111f08b2009-04-30 16:01:52793 } else if (action == @selector(showPreferences:)) {
794 enable = YES;
[email protected]bde3dda2009-05-20 22:13:07795 } else if (action == @selector(orderFrontStandardAboutPanel:)) {
796 enable = YES;
[email protected]93b59fc2010-12-21 20:00:47797 } else if (action == @selector(commandFromDock:)) {
[email protected]3d4bd23f2009-10-06 03:58:38798 enable = YES;
[email protected]88d74942009-01-21 22:04:44799 }
800 return enable;
801}
802
[email protected]947fc0d2010-01-14 22:33:05803// Called when the user picks a menu item when there are no key windows, or when
804// there is no foreground browser window. Calls through to the browser object to
805// execute the command. This assumes that the command is supported and doesn't
[email protected]d630d7d52010-02-21 00:55:11806// check, otherwise it should have been disabled in the UI in
807// |-validateUserInterfaceItem:|.
[email protected]88d74942009-01-21 22:04:44808- (void)commandDispatch:(id)sender {
[email protected]93e181762009-05-29 14:30:38809 Profile* defaultProfile = [self defaultProfile];
[email protected]f0a51fb52009-03-05 12:46:38810
[email protected]947fc0d2010-01-14 22:33:05811 // Handle the case where we're dispatching a command from a sender that's in a
812 // browser window. This means that the command came from a background window
813 // and is getting here because the foreground window is not a browser window.
814 if ([sender respondsToSelector:@selector(window)]) {
815 id delegate = [[sender window] windowController];
816 if ([delegate isKindOfClass:[BrowserWindowController class]]) {
817 [delegate commandDispatch:sender];
818 return;
819 }
820 }
821
[email protected]88d74942009-01-21 22:04:44822 NSInteger tag = [sender tag];
823 switch (tag) {
[email protected]fbc947b2009-06-19 13:28:24824 case IDC_NEW_TAB:
[email protected]d630d7d52010-02-21 00:55:11825 // Create a new tab in an existing browser window (which we activate) if
826 // possible.
827 if (Browser* browser = ActivateBrowser(defaultProfile)) {
828 browser->ExecuteCommand(IDC_NEW_TAB);
829 break;
830 }
831 // Else fall through to create new window.
[email protected]88d74942009-01-21 22:04:44832 case IDC_NEW_WINDOW:
[email protected]d630d7d52010-02-21 00:55:11833 CreateBrowser(defaultProfile);
834 break;
[email protected]91a5b3e2009-10-30 19:32:22835 case IDC_FOCUS_LOCATION:
[email protected]d630d7d52010-02-21 00:55:11836 ActivateOrCreateBrowser(defaultProfile)->
837 ExecuteCommand(IDC_FOCUS_LOCATION);
[email protected]88d74942009-01-21 22:04:44838 break;
[email protected]cddc5242009-12-07 18:54:33839 case IDC_FOCUS_SEARCH:
[email protected]d630d7d52010-02-21 00:55:11840 ActivateOrCreateBrowser(defaultProfile)->ExecuteCommand(IDC_FOCUS_SEARCH);
[email protected]cddc5242009-12-07 18:54:33841 break;
[email protected]863ff662009-01-26 20:18:18842 case IDC_NEW_INCOGNITO_WINDOW:
[email protected]e9e211272009-09-22 16:39:11843 Browser::OpenEmptyWindow(defaultProfile->GetOffTheRecordProfile());
[email protected]863ff662009-01-26 20:18:18844 break;
[email protected]fbc947b2009-06-19 13:28:24845 case IDC_RESTORE_TAB:
846 Browser::OpenWindowWithRestoredTabs(defaultProfile);
847 break;
[email protected]e19516d2009-04-28 17:15:19848 case IDC_OPEN_FILE:
[email protected]d630d7d52010-02-21 00:55:11849 CreateBrowser(defaultProfile)->ExecuteCommand(IDC_OPEN_FILE);
[email protected]e19516d2009-04-28 17:15:19850 break;
[email protected]1fdff702009-10-22 00:36:18851 case IDC_CLEAR_BROWSING_DATA: {
[email protected]93e181762009-05-29 14:30:38852 // There may not be a browser open, so use the default profile.
[email protected]049ac832009-12-15 16:26:54853 [ClearBrowsingDataController
854 showClearBrowsingDialogForProfile:defaultProfile];
[email protected]93e181762009-05-29 14:30:38855 break;
[email protected]1fdff702009-10-22 00:36:18856 }
[email protected]4aee5652009-11-19 18:59:21857 case IDC_IMPORT_SETTINGS: {
[email protected]896220042010-03-23 18:14:28858 UserMetrics::RecordAction(UserMetricsAction("Import_ShowDlg"),
859 defaultProfile);
[email protected]2989cce12010-02-01 10:56:53860 [ImportSettingsDialogController
861 showImportSettingsDialogForProfile:defaultProfile];
[email protected]4aee5652009-11-19 18:59:21862 break;
863 }
[email protected]af7c25e2010-01-23 05:46:45864 case IDC_SHOW_BOOKMARK_MANAGER:
[email protected]896220042010-03-23 18:14:28865 UserMetrics::RecordAction(UserMetricsAction("ShowBookmarkManager"),
866 defaultProfile);
[email protected]90a8bf252010-03-12 00:09:47867 if (Browser* browser = ActivateBrowser(defaultProfile)) {
[email protected]47e532b2010-04-23 17:46:40868 // Open a bookmark manager tab.
[email protected]90a8bf252010-03-12 00:09:47869 browser->OpenBookmarkManager();
870 } else {
[email protected]47e532b2010-04-23 17:46:40871 // No browser window, so create one for the bookmark manager tab.
872 Browser::OpenBookmarkManagerWindow(defaultProfile);
[email protected]90a8bf252010-03-12 00:09:47873 }
[email protected]af7c25e2010-01-23 05:46:45874 break;
[email protected]91a5b3e2009-10-30 19:32:22875 case IDC_SHOW_HISTORY:
[email protected]d630d7d52010-02-21 00:55:11876 if (Browser* browser = ActivateBrowser(defaultProfile))
877 browser->ShowHistoryTab();
878 else
879 Browser::OpenHistoryWindow(defaultProfile);
[email protected]91a5b3e2009-10-30 19:32:22880 break;
881 case IDC_SHOW_DOWNLOADS:
[email protected]d630d7d52010-02-21 00:55:11882 if (Browser* browser = ActivateBrowser(defaultProfile))
883 browser->ShowDownloadsTab();
884 else
885 Browser::OpenDownloadsWindow(defaultProfile);
[email protected]91a5b3e2009-10-30 19:32:22886 break;
[email protected]bb37e6ec2009-11-18 17:59:36887 case IDC_MANAGE_EXTENSIONS:
[email protected]d630d7d52010-02-21 00:55:11888 if (Browser* browser = ActivateBrowser(defaultProfile))
889 browser->ShowExtensionsTab();
890 else
891 Browser::OpenExtensionsWindow(defaultProfile);
[email protected]bb37e6ec2009-11-18 17:59:36892 break;
[email protected]1fdff702009-10-22 00:36:18893 case IDC_HELP_PAGE:
[email protected]d630d7d52010-02-21 00:55:11894 if (Browser* browser = ActivateBrowser(defaultProfile))
895 browser->OpenHelpTab();
896 else
897 Browser::OpenHelpWindow(defaultProfile);
[email protected]1fdff702009-10-22 00:36:18898 break;
[email protected]1c41a932010-11-22 23:41:23899 case IDC_FEEDBACK: {
[email protected]3896b182009-11-03 16:15:32900 Browser* browser = BrowserList::GetLastActive();
[email protected]d630d7d52010-02-21 00:55:11901 TabContents* currentTab =
902 browser ? browser->GetSelectedTabContents() : NULL;
[email protected]3896b182009-11-03 16:15:32903 BugReportWindowController* controller =
904 [[BugReportWindowController alloc]
[email protected]d630d7d52010-02-21 00:55:11905 initWithTabContents:currentTab
906 profile:[self defaultProfile]];
[email protected]3896b182009-11-03 16:15:32907 [controller runModalDialog];
908 break;
909 }
[email protected]f9bc9b92009-11-24 00:55:35910 case IDC_SYNC_BOOKMARKS:
[email protected]446e16a2010-05-03 21:23:58911 // The profile may be NULL during shutdown -- see
912 // http://code.google.com/p/chromium/issues/detail?id=43048 .
913 //
914 // TODO(akalin,viettrungluu): Figure out whether this method can
915 // be prevented from being called if defaultProfile is NULL.
916 if (!defaultProfile) {
917 LOG(WARNING) << "NULL defaultProfile detected -- not doing anything";
918 break;
919 }
[email protected]f9bc9b92009-11-24 00:55:35920 // TODO(akalin): Add a constant to denote starting sync from the
921 // main menu and use that instead of START_FROM_WRENCH.
[email protected]40f047972009-11-25 03:54:40922 sync_ui_util::OpenSyncMyBookmarksDialog(
[email protected]f9bc9b92009-11-24 00:55:35923 defaultProfile, ProfileSyncService::START_FROM_WRENCH);
924 break;
[email protected]ceaaf1812010-01-20 22:13:09925 case IDC_TASK_MANAGER:
[email protected]896220042010-03-23 18:14:28926 UserMetrics::RecordAction(UserMetricsAction("TaskManager"),
927 defaultProfile);
[email protected]ceaaf1812010-01-20 22:13:09928 TaskManagerMac::Show();
929 break;
[email protected]4a42d272010-06-18 01:29:42930 case IDC_OPTIONS:
931 [self showPreferences:sender];
932 break;
[email protected]93b59fc2010-12-21 20:00:47933 default:
934 // Background Applications use dynamic values that must be less than the
935 // smallest value among the predefined IDC_* labels.
936 if ([sender tag] < IDC_MinimumLabelValue)
937 [self executeApplication:sender];
938 break;
[email protected]d630d7d52010-02-21 00:55:11939 }
[email protected]88d74942009-01-21 22:04:44940}
941
[email protected]93b59fc2010-12-21 20:00:47942// Run a (background) application in a new tab.
943- (void)executeApplication:(id)sender {
944 NSInteger tag = [sender tag];
945 Profile* profile = [self defaultProfile];
946 DCHECK(profile);
947 BackgroundApplicationListModel applications(profile);
948 DCHECK(tag >= 0 &&
949 tag < static_cast<int>(applications.size()));
950 Browser* browser = BrowserList::GetLastActive();
951 if (!browser) {
952 Browser::OpenEmptyWindow(profile);
953 browser = BrowserList::GetLastActive();
954 }
955 const Extension* extension = applications.GetExtension(tag);
956 browser->OpenApplicationTab(profile, extension, NULL);
957}
958
[email protected]947fc0d2010-01-14 22:33:05959// Same as |-commandDispatch:|, but executes commands using a disposition
960// determined by the key flags. This will get called in the case where the
961// frontmost window is not a browser window, and the user has command-clicked
962// a button in a background browser window whose action is
963// |-commandDispatchUsingKeyModifiers:|
964- (void)commandDispatchUsingKeyModifiers:(id)sender {
965 DCHECK(sender);
966 if ([sender respondsToSelector:@selector(window)]) {
967 id delegate = [[sender window] windowController];
968 if ([delegate isKindOfClass:[BrowserWindowController class]]) {
969 [delegate commandDispatchUsingKeyModifiers:sender];
970 }
971 }
972}
973
[email protected]ff81e0a2009-04-13 14:58:54974// NSApplication delegate method called when someone clicks on the
975// dock icon and there are no open windows. To match standard mac
976// behavior, we should open a new window.
977- (BOOL)applicationShouldHandleReopen:(NSApplication*)theApplication
978 hasVisibleWindows:(BOOL)flag {
[email protected]70631402010-04-26 20:16:54979 // If the browser is currently trying to quit, don't do anything and return NO
980 // to prevent AppKit from doing anything.
981 // TODO(rohitrao): Remove this code when http://crbug.com/40861 is resolved.
982 if (browser_shutdown::IsTryingToQuit())
983 return NO;
984
[email protected]ff81e0a2009-04-13 14:58:54985 // Don't do anything if there are visible windows. This will cause
986 // AppKit to unminimize the most recently minimized window.
987 if (flag)
988 return YES;
989
[email protected]0d1210d62010-09-07 17:31:38990 // If launched as a hidden login item (due to installation of a persistent app
991 // or by the user, for example in System Preferenecs->Accounts->Login Items),
992 // allow session to be restored first time the user clicks on a Dock icon.
993 // Normally, it'd just open a new empty page.
994 {
995 static BOOL doneOnce = NO;
996 if (!doneOnce) {
997 doneOnce = YES;
998 if (mac_util::WasLaunchedAsHiddenLoginItem()) {
999 SessionService* sessionService =
1000 [self defaultProfile]->GetSessionService();
1001 if (sessionService &&
1002 sessionService->RestoreIfNecessary(std::vector<GURL>()))
1003 return NO;
1004 }
1005 }
1006 }
[email protected]ff81e0a2009-04-13 14:58:541007 // Otherwise open a new window.
[email protected]d630d7d52010-02-21 00:55:111008 {
[email protected]0fbd70332010-06-01 19:28:341009 AutoReset<bool> auto_reset_in_run(&g_is_opening_new_window, true);
[email protected]d630d7d52010-02-21 00:55:111010 Browser::OpenEmptyWindow([self defaultProfile]);
1011 }
[email protected]ff81e0a2009-04-13 14:58:541012
1013 // We've handled the reopen event, so return NO to tell AppKit not
1014 // to do anything.
1015 return NO;
1016}
1017
[email protected]88d74942009-01-21 22:04:441018- (void)initMenuState {
[email protected]3111f08b2009-04-30 16:01:521019 menuState_.reset(new CommandUpdater(NULL));
[email protected]fbc947b2009-06-19 13:28:241020 menuState_->UpdateCommandEnabled(IDC_NEW_TAB, true);
[email protected]88d74942009-01-21 22:04:441021 menuState_->UpdateCommandEnabled(IDC_NEW_WINDOW, true);
[email protected]863ff662009-01-26 20:18:181022 menuState_->UpdateCommandEnabled(IDC_NEW_INCOGNITO_WINDOW, true);
[email protected]e19516d2009-04-28 17:15:191023 menuState_->UpdateCommandEnabled(IDC_OPEN_FILE, true);
[email protected]93e181762009-05-29 14:30:381024 menuState_->UpdateCommandEnabled(IDC_CLEAR_BROWSING_DATA, true);
[email protected]fbc947b2009-06-19 13:28:241025 menuState_->UpdateCommandEnabled(IDC_RESTORE_TAB, false);
[email protected]91a5b3e2009-10-30 19:32:221026 menuState_->UpdateCommandEnabled(IDC_FOCUS_LOCATION, true);
[email protected]cddc5242009-12-07 18:54:331027 menuState_->UpdateCommandEnabled(IDC_FOCUS_SEARCH, true);
[email protected]af7c25e2010-01-23 05:46:451028 menuState_->UpdateCommandEnabled(IDC_SHOW_BOOKMARK_MANAGER, true);
[email protected]91a5b3e2009-10-30 19:32:221029 menuState_->UpdateCommandEnabled(IDC_SHOW_HISTORY, true);
1030 menuState_->UpdateCommandEnabled(IDC_SHOW_DOWNLOADS, true);
[email protected]bb37e6ec2009-11-18 17:59:361031 menuState_->UpdateCommandEnabled(IDC_MANAGE_EXTENSIONS, true);
[email protected]1fdff702009-10-22 00:36:181032 menuState_->UpdateCommandEnabled(IDC_HELP_PAGE, true);
[email protected]4aee5652009-11-19 18:59:211033 menuState_->UpdateCommandEnabled(IDC_IMPORT_SETTINGS, true);
[email protected]1c41a932010-11-22 23:41:231034 menuState_->UpdateCommandEnabled(IDC_FEEDBACK, true);
[email protected]d3b98c82010-07-14 07:45:591035 menuState_->UpdateCommandEnabled(IDC_SYNC_BOOKMARKS, true);
[email protected]ceaaf1812010-01-20 22:13:091036 menuState_->UpdateCommandEnabled(IDC_TASK_MANAGER, true);
[email protected]88d74942009-01-21 22:04:441037}
[email protected]1bcdb532009-01-16 17:47:571038
[email protected]eef99c22010-08-17 05:55:161039- (void)registerServicesMenuTypesTo:(NSApplication*)app {
[email protected]09ea24c2010-08-23 19:05:411040 // Note that RenderWidgetHostViewCocoa implements NSServicesRequests which
1041 // handles requests from services.
1042 NSArray* types = [NSArray arrayWithObjects:NSStringPboardType, nil];
1043 [app registerServicesMenuSendTypes:types returnTypes:types];
[email protected]eef99c22010-08-17 05:55:161044}
1045
[email protected]3f34599d2009-03-25 22:11:431046- (Profile*)defaultProfile {
[email protected]3f34599d2009-03-25 22:11:431047 // TODO(jrg): Find a better way to get the "default" profile.
1048 if (g_browser_process->profile_manager())
[email protected]065566b2010-01-20 20:38:261049 return *g_browser_process->profile_manager()->begin();
[email protected]3f34599d2009-03-25 22:11:431050
[email protected]f6314002009-04-23 01:18:131051 return NULL;
[email protected]3f34599d2009-03-25 22:11:431052}
1053
[email protected]57750f822009-04-21 21:43:091054// Various methods to open URLs that we get in a native fashion. We use
1055// BrowserInit here because on the other platforms, URLs to open come through
1056// the ProcessSingleton, and it calls BrowserInit. It's best to bottleneck the
1057// openings through that for uniform handling.
1058
[email protected]d630d7d52010-02-21 00:55:111059- (void)openUrls:(const std::vector<GURL>&)urls {
[email protected]caadfca2010-01-22 21:51:211060 // If the browser hasn't started yet, just queue up the URLs.
1061 if (!startupComplete_) {
[email protected]d630d7d52010-02-21 00:55:111062 startupUrls_.insert(startupUrls_.end(), urls.begin(), urls.end());
[email protected]136140c2009-05-19 13:58:251063 return;
1064 }
[email protected]57750f822009-04-21 21:43:091065
[email protected]0e863512009-05-28 19:45:071066 Browser* browser = BrowserList::GetLastActive();
1067 // if no browser window exists then create one with no tabs to be filled in
1068 if (!browser) {
1069 browser = Browser::Create([self defaultProfile]);
[email protected]ce560f82009-06-03 09:39:441070 browser->window()->Show();
[email protected]0e863512009-05-28 19:45:071071 }
1072
[email protected]947446b2010-10-21 03:36:311073 CommandLine dummy(CommandLine::NO_PROGRAM);
[email protected]f805fe82010-08-03 22:47:101074 BrowserInit::LaunchWithProfile launch(FilePath(), dummy);
[email protected]0e863512009-05-28 19:45:071075 launch.OpenURLsInBrowser(browser, false, urls);
[email protected]57750f822009-04-21 21:43:091076}
1077
[email protected]57750f822009-04-21 21:43:091078- (void)getUrl:(NSAppleEventDescriptor*)event
1079 withReply:(NSAppleEventDescriptor*)reply {
1080 NSString* urlStr = [[event paramDescriptorForKeyword:keyDirectObject]
1081 stringValue];
1082
1083 GURL gurl(base::SysNSStringToUTF8(urlStr));
1084 std::vector<GURL> gurlVector;
1085 gurlVector.push_back(gurl);
1086
[email protected]d630d7d52010-02-21 00:55:111087 [self openUrls:gurlVector];
[email protected]57750f822009-04-21 21:43:091088}
1089
[email protected]caadfca2010-01-22 21:51:211090- (void)application:(NSApplication*)sender
1091 openFiles:(NSArray*)filenames {
[email protected]57750f822009-04-21 21:43:091092 std::vector<GURL> gurlVector;
[email protected]caadfca2010-01-22 21:51:211093 for (NSString* file in filenames) {
1094 GURL gurl = net::FilePathToFileURL(FilePath(base::SysNSStringToUTF8(file)));
[email protected]57750f822009-04-21 21:43:091095 gurlVector.push_back(gurl);
1096 }
[email protected]caadfca2010-01-22 21:51:211097 if (!gurlVector.empty())
[email protected]d630d7d52010-02-21 00:55:111098 [self openUrls:gurlVector];
[email protected]caadfca2010-01-22 21:51:211099 else
1100 NOTREACHED() << "Nothing to open!";
[email protected]57750f822009-04-21 21:43:091101
[email protected]caadfca2010-01-22 21:51:211102 [sender replyToOpenOrPrint:NSApplicationDelegateReplySuccess];
[email protected]57750f822009-04-21 21:43:091103}
[email protected]3f34599d2009-03-25 22:11:431104
[email protected]767543d2009-04-30 19:23:581105// Called when the preferences window is closed. We use this to release the
1106// window controller.
[email protected]d4e18652009-11-19 21:52:161107- (void)prefsWindowClosed:(NSNotification*)notification {
1108 NSWindow* window = [prefsController_ window];
1109 DCHECK([notification object] == window);
1110 NSNotificationCenter* defaultCenter = [NSNotificationCenter defaultCenter];
1111 [defaultCenter removeObserver:self
1112 name:NSWindowWillCloseNotification
1113 object:window];
1114 // PreferencesWindowControllers are autoreleased in
1115 // -[PreferencesWindowController windowWillClose:].
1116 prefsController_ = nil;
[email protected]767543d2009-04-30 19:23:581117}
1118
[email protected]3111f08b2009-04-30 16:01:521119// Show the preferences window, or bring it to the front if it's already
1120// visible.
1121- (IBAction)showPreferences:(id)sender {
[email protected]4a42d272010-06-18 01:29:421122 const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess();
1123 if (parsed_command_line.HasSwitch(switches::kEnableTabbedOptions)) {
1124 if (Browser* browser = ActivateBrowser([self defaultProfile])) {
1125 // Show options tab in the active browser window.
[email protected]024617a2010-08-20 05:08:051126 browser->ShowOptionsTab(chrome::kDefaultOptionsSubPage);
[email protected]4a42d272010-06-18 01:29:421127 } else {
1128 // No browser window, so create one for the options tab.
1129 Browser::OpenOptionsWindow([self defaultProfile]);
1130 }
1131 } else {
1132 [self showPreferencesWindow:sender
1133 page:OPTIONS_PAGE_DEFAULT
1134 profile:[self defaultProfile]];
1135 }
[email protected]d989f07c2009-11-14 00:35:461136}
1137
1138- (void)showPreferencesWindow:(id)sender
1139 page:(OptionsPage)page
1140 profile:(Profile*)profile {
[email protected]d4e18652009-11-19 21:52:161141 if (prefsController_) {
[email protected]d989f07c2009-11-14 00:35:461142 [prefsController_ switchToPage:page animate:YES];
1143 } else {
[email protected]d4e18652009-11-19 21:52:161144 prefsController_ =
1145 [[PreferencesWindowController alloc] initWithProfile:profile
1146 initialPage:page];
[email protected]767543d2009-04-30 19:23:581147 // Watch for a notification of when it goes away so that we can destroy
1148 // the controller.
1149 [[NSNotificationCenter defaultCenter]
1150 addObserver:self
1151 selector:@selector(prefsWindowClosed:)
[email protected]d4e18652009-11-19 21:52:161152 name:NSWindowWillCloseNotification
1153 object:[prefsController_ window]];
[email protected]42404382009-04-30 17:59:241154 }
1155 [prefsController_ showPreferences:sender];
[email protected]3111f08b2009-04-30 16:01:521156}
1157
[email protected]bde3dda2009-05-20 22:13:071158// Called when the about window is closed. We use this to release the
1159// window controller.
[email protected]d4e18652009-11-19 21:52:161160- (void)aboutWindowClosed:(NSNotification*)notification {
1161 NSWindow* window = [aboutController_ window];
1162 DCHECK(window == [notification object]);
[email protected]bde3dda2009-05-20 22:13:071163 [[NSNotificationCenter defaultCenter]
[email protected]49aeee52009-10-26 19:58:131164 removeObserver:self
[email protected]d4e18652009-11-19 21:52:161165 name:NSWindowWillCloseNotification
1166 object:window];
1167 // AboutWindowControllers are autoreleased in
1168 // -[AboutWindowController windowWillClose:].
1169 aboutController_ = nil;
[email protected]bde3dda2009-05-20 22:13:071170}
1171
1172- (IBAction)orderFrontStandardAboutPanel:(id)sender {
[email protected]bde3dda2009-05-20 22:13:071173 if (!aboutController_) {
[email protected]d4e18652009-11-19 21:52:161174 aboutController_ =
1175 [[AboutWindowController alloc] initWithProfile:[self defaultProfile]];
[email protected]49aeee52009-10-26 19:58:131176
[email protected]bde3dda2009-05-20 22:13:071177 // Watch for a notification of when it goes away so that we can destroy
1178 // the controller.
1179 [[NSNotificationCenter defaultCenter]
1180 addObserver:self
1181 selector:@selector(aboutWindowClosed:)
[email protected]d4e18652009-11-19 21:52:161182 name:NSWindowWillCloseNotification
1183 object:[aboutController_ window]];
[email protected]bde3dda2009-05-20 22:13:071184 }
[email protected]49aeee52009-10-26 19:58:131185
[email protected]bde3dda2009-05-20 22:13:071186 [aboutController_ showWindow:self];
[email protected]bde3dda2009-05-20 22:13:071187}
1188
[email protected]3d4bd23f2009-10-06 03:58:381189// Explicitly bring to the foreground when creating new windows from the dock.
[email protected]93b59fc2010-12-21 20:00:471190- (void)commandFromDock:(id)sender {
[email protected]3d4bd23f2009-10-06 03:58:381191 [NSApp activateIgnoringOtherApps:YES];
1192 [self commandDispatch:sender];
1193}
1194
[email protected]227fcee2010-06-11 19:19:371195- (NSMenu*)applicationDockMenu:(NSApplication*)sender {
[email protected]018a3962009-09-17 22:23:441196 NSMenu* dockMenu = [[[NSMenu alloc] initWithTitle: @""] autorelease];
[email protected]93b59fc2010-12-21 20:00:471197 Profile* profile = [self defaultProfile];
1198
1199 // TODO(rickcam): Mock out BackgroundApplicationListModel, then add unit
1200 // tests which use the mock in place of the profile-initialized model.
1201
1202 // Avoid breaking unit tests which have no profile.
1203 if (profile) {
1204 int position = 0;
1205 BackgroundApplicationListModel applications(profile);
1206 for (ExtensionList::const_iterator cursor = applications.begin();
1207 cursor != applications.end();
1208 ++cursor, ++position) {
1209 DCHECK(position == applications.GetPosition(*cursor));
1210 scoped_nsobject<NSMenuItem> appItem([[NSMenuItem alloc]
1211 initWithTitle:base::SysUTF16ToNSString(UTF8ToUTF16((*cursor)->name()))
1212 action:@selector(commandFromDock:)
1213 keyEquivalent:@""]);
1214 [appItem setTarget:self];
1215 [appItem setTag:position];
1216 [dockMenu addItem:appItem];
1217 }
1218 if (applications.begin() != applications.end()) {
1219 NSMenuItem* sepItem = [[NSMenuItem separatorItem] init];
1220 [dockMenu addItem:sepItem];
1221 }
1222 }
1223
[email protected]018a3962009-09-17 22:23:441224 NSString* titleStr = l10n_util::GetNSStringWithFixup(IDS_NEW_WINDOW_MAC);
1225 scoped_nsobject<NSMenuItem> item([[NSMenuItem alloc]
[email protected]93b59fc2010-12-21 20:00:471226 initWithTitle:titleStr
1227 action:@selector(commandFromDock:)
1228 keyEquivalent:@""]);
[email protected]288bfcd32009-09-14 18:14:461229 [item setTarget:self];
1230 [item setTag:IDC_NEW_WINDOW];
[email protected]018a3962009-09-17 22:23:441231 [dockMenu addItem:item];
[email protected]288bfcd32009-09-14 18:14:461232
1233 titleStr = l10n_util::GetNSStringWithFixup(IDS_NEW_INCOGNITO_WINDOW_MAC);
[email protected]018a3962009-09-17 22:23:441234 item.reset([[NSMenuItem alloc] initWithTitle:titleStr
[email protected]93b59fc2010-12-21 20:00:471235 action:@selector(commandFromDock:)
[email protected]018a3962009-09-17 22:23:441236 keyEquivalent:@""]);
[email protected]288bfcd32009-09-14 18:14:461237 [item setTarget:self];
1238 [item setTag:IDC_NEW_INCOGNITO_WINDOW];
[email protected]018a3962009-09-17 22:23:441239 [dockMenu addItem:item];
[email protected]288bfcd32009-09-14 18:14:461240
[email protected]018a3962009-09-17 22:23:441241 return dockMenu;
[email protected]288bfcd32009-09-14 18:14:461242}
1243
[email protected]d630d7d52010-02-21 00:55:111244- (const std::vector<GURL>&)startupUrls {
1245 return startupUrls_;
[email protected]caadfca2010-01-22 21:51:211246}
1247
[email protected]d630d7d52010-02-21 00:55:111248- (void)clearStartupUrls {
1249 startupUrls_.clear();
[email protected]caadfca2010-01-22 21:51:211250}
1251
[email protected]d630d7d52010-02-21 00:55:111252@end // @implementation AppController
[email protected]60ad3e22009-09-18 21:07:191253
1254//---------------------------------------------------------------------------
1255
[email protected]60ad3e22009-09-18 21:07:191256void ShowOptionsWindow(OptionsPage page,
1257 OptionsGroup highlight_group,
1258 Profile* profile) {
[email protected]d989f07c2009-11-14 00:35:461259 // TODO(akalin): Use highlight_group.
1260 AppController* appController = [NSApp delegate];
1261 [appController showPreferencesWindow:nil page:page profile:profile];
[email protected]60ad3e22009-09-18 21:07:191262}
[email protected]216e9042009-10-29 17:05:231263
1264namespace app_controller_mac {
1265
1266bool IsOpeningNewWindow() {
1267 return g_is_opening_new_window;
1268}
1269
1270} // namespace app_controller_mac