blob: c2d284bf0d17d04a719273d7b9db265282a0ce23 [file] [log] [blame]
[email protected]c8de64a2011-01-25 17:10:231// Copyright (c) 2011 The Chromium Authors. All rights reserved.
[email protected]1bcdb532009-01-16 17:47:572// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]2c47bc12009-04-10 20:14:005#import "chrome/browser/app_controller_mac.h"
[email protected]1bcdb532009-01-16 17:47:576
[email protected]d630d7d52010-02-21 00:55:117#include "base/auto_reset.h"
[email protected]57750f822009-04-21 21:43:098#include "base/command_line.h"
[email protected]f805fe82010-08-03 22:47:109#include "base/file_path.h"
[email protected]151c4a62011-04-22 04:15:1310#include "base/mac/foundation_util.h"
[email protected]0378bf42011-01-01 18:20:1411#include "base/mac/mac_util.h"
[email protected]2c47bc12009-04-10 20:14:0012#include "base/message_loop.h"
[email protected]528c56d2010-07-30 19:28:4413#include "base/string_number_conversions.h"
[email protected]57750f822009-04-21 21:43:0914#include "base/sys_string_conversions.h"
[email protected]1a3aba82010-11-08 23:52:5415#include "chrome/app/chrome_command_ids.h"
[email protected]93b59fc2010-12-21 20:00:4716#include "chrome/browser/background_application_list_model.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]2c47bc12009-04-10 20:14:0019#include "chrome/browser/command_updater.h"
[email protected]09729a552009-08-03 23:21:4120#include "chrome/browser/download/download_manager.h"
[email protected]0934de82011-03-09 18:42:1621#include "chrome/browser/instant/instant_confirm_dialog.h"
[email protected]4aee5652009-11-19 18:59:2122#include "chrome/browser/metrics/user_metrics.h"
[email protected]a99fce0e2011-03-21 20:58:4823#include "chrome/browser/prefs/pref_service.h"
[email protected]e7dd6d82010-10-07 23:21:1224#include "chrome/browser/printing/print_job_manager.h"
[email protected]8ecad5e2010-12-02 21:18:3325#include "chrome/browser/profiles/profile_manager.h"
[email protected]d2912a22011-03-15 15:20:5026#include "chrome/browser/sessions/session_service.h"
[email protected]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]ae04f592010-11-18 20:41:3531#include "chrome/browser/ui/browser.h"
[email protected]f7002802010-11-12 19:50:2832#include "chrome/browser/ui/browser_init.h"
[email protected]ae04f592010-11-18 20:41:3533#include "chrome/browser/ui/browser_list.h"
34#include "chrome/browser/ui/browser_window.h"
[email protected]7d791652010-12-01 16:34:4935#import "chrome/browser/ui/cocoa/about_window_controller.h"
36#import "chrome/browser/ui/cocoa/bookmarks/bookmark_menu_bridge.h"
37#import "chrome/browser/ui/cocoa/browser_window_cocoa.h"
38#import "chrome/browser/ui/cocoa/browser_window_controller.h"
39#import "chrome/browser/ui/cocoa/bug_report_window_controller.h"
[email protected]7d791652010-12-01 16:34:4940#import "chrome/browser/ui/cocoa/confirm_quit_panel_controller.h"
41#import "chrome/browser/ui/cocoa/encoding_menu_controller_delegate_mac.h"
42#import "chrome/browser/ui/cocoa/history_menu_bridge.h"
[email protected]cfe1d612011-01-19 20:06:4743#import "chrome/browser/ui/cocoa/tabs/tab_strip_controller.h"
44#import "chrome/browser/ui/cocoa/tabs/tab_window_controller.h"
[email protected]7d791652010-12-01 16:34:4945#include "chrome/browser/ui/cocoa/task_manager_mac.h"
[email protected]6a3ec2312010-12-02 19:30:1946#include "chrome/browser/ui/options/options_window.h"
[email protected]646b9b92010-04-30 21:51:5847#include "chrome/common/app_mode_common_mac.h"
[email protected]12dc3d42010-02-22 23:37:1248#include "chrome/common/chrome_paths_internal.h"
[email protected]f4acfae82009-09-11 00:33:3849#include "chrome/common/chrome_switches.h"
[email protected]a99fce0e2011-03-21 20:58:4850#include "chrome/common/pref_names.h"
[email protected]024617a2010-08-20 05:08:0551#include "chrome/common/url_constants.h"
[email protected]7324d1d2011-03-01 05:02:1652#include "content/browser/browser_thread.h"
53#include "content/browser/tab_contents/tab_contents.h"
[email protected]f20d7332011-03-08 21:11:5354#include "content/common/notification_service.h"
[email protected]9dcb59662009-09-23 01:15:5055#include "grit/chromium_strings.h"
[email protected]09729a552009-08-03 23:21:4156#include "grit/generated_resources.h"
[email protected]caadfca2010-01-22 21:51:2157#include "net/base/net_util.h"
[email protected]c051a1b2011-01-21 23:30:1758#include "ui/base/l10n/l10n_util.h"
59#include "ui/base/l10n/l10n_util_mac.h"
[email protected]ddd653f42011-03-02 15:14:3860#include "ui/base/models/accelerator_cocoa.h"
[email protected]88d74942009-01-21 22:04:4461
[email protected]f2c20fa2009-12-01 17:42:0262// 10.6 adds a public API for the Spotlight-backed search menu item in the Help
63// menu. Provide the declaration so it can be called below when building with
64// the 10.5 SDK.
65#if !defined(MAC_OS_X_VERSION_10_6) || \
66 MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6
67@interface NSApplication (SnowLeopardSDKDeclarations)
[email protected]caadfca2010-01-22 21:51:2168- (void)setHelpMenu:(NSMenu*)helpMenu;
[email protected]f2c20fa2009-12-01 17:42:0269@end
70#endif
71
[email protected]d630d7d52010-02-21 00:55:1172namespace {
73
74// True while AppController is calling Browser::OpenEmptyWindow(). We need a
75// global flag here, analogue to BrowserInit::InProcessStartup() because
76// otherwise the SessionService will try to restore sessions when we make a new
77// window while there are no other active windows.
78bool g_is_opening_new_window = false;
79
80// Activates a browser window having the given profile (the last one active) if
81// possible and returns a pointer to the activate |Browser| or NULL if this was
82// not possible. If the last active browser is minimized (in particular, if
83// there are only minimized windows), it will unminimize it.
84Browser* ActivateBrowser(Profile* profile) {
85 Browser* browser = BrowserList::GetLastActiveWithProfile(profile);
86 if (browser)
87 browser->window()->Activate();
88 return browser;
89}
90
91// Creates an empty browser window with the given profile and returns a pointer
92// to the new |Browser|.
93Browser* CreateBrowser(Profile* profile) {
94 {
[email protected]0fbd70332010-06-01 19:28:3495 AutoReset<bool> auto_reset_in_run(&g_is_opening_new_window, true);
[email protected]d630d7d52010-02-21 00:55:1196 Browser::OpenEmptyWindow(profile);
97 }
98
99 Browser* browser = BrowserList::GetLastActive();
100 CHECK(browser);
101 return browser;
102}
103
104// Activates a browser window having the given profile (the last one active) if
105// possible or creates an empty one if necessary. Returns a pointer to the
106// activated/new |Browser|.
107Browser* ActivateOrCreateBrowser(Profile* profile) {
108 if (Browser* browser = ActivateBrowser(profile))
109 return browser;
110 return CreateBrowser(profile);
111}
112
[email protected]151c4a62011-04-22 04:15:13113CFStringRef BaseBundleID_CFString() {
114 NSString* base_bundle_id =
115 [NSString stringWithUTF8String:base::mac::BaseBundleID()];
116 return base::mac::NSToCFCast(base_bundle_id);
117}
118
[email protected]12dc3d42010-02-22 23:37:12119// This task synchronizes preferences (under "org.chromium.Chromium" or
120// "com.google.Chrome"), in particular, writes them out to disk.
121class PrefsSyncTask : public Task {
122 public:
123 PrefsSyncTask() {}
124 virtual ~PrefsSyncTask() {}
125 virtual void Run() {
[email protected]151c4a62011-04-22 04:15:13126 if (!CFPreferencesAppSynchronize(BaseBundleID_CFString()))
[email protected]12dc3d42010-02-22 23:37:12127 LOG(WARNING) << "Error recording application bundle path.";
128 }
129};
130
131// Record the location of the application bundle (containing the main framework)
132// from which Chromium was loaded. This is used by app mode shims to find
133// Chromium.
134void RecordLastRunAppBundlePath() {
135 // Going up three levels from |chrome::GetVersionedDirectory()| gives the
136 // real, user-visible app bundle directory. (The alternatives give either the
137 // framework's path or the initial app's path, which may be an app mode shim
138 // or a unit test.)
139 FilePath appBundlePath =
140 chrome::GetVersionedDirectory().DirName().DirName().DirName();
141 CFPreferencesSetAppValue(app_mode::kLastRunAppBundlePathPrefsKey,
142 base::SysUTF8ToCFStringRef(appBundlePath.value()),
[email protected]151c4a62011-04-22 04:15:13143 BaseBundleID_CFString());
[email protected]12dc3d42010-02-22 23:37:12144
145 // Sync after a delay avoid I/O contention on startup; 1500 ms is plenty.
[email protected]d04e7662010-10-10 22:24:48146 BrowserThread::PostDelayedTask(BrowserThread::FILE, FROM_HERE,
147 new PrefsSyncTask(), 1500);
[email protected]12dc3d42010-02-22 23:37:12148}
149
[email protected]d630d7d52010-02-21 00:55:11150} // anonymous namespace
151
[email protected]a99fce0e2011-03-21 20:58:48152@interface AppController (Private)
[email protected]88d74942009-01-21 22:04:44153- (void)initMenuState;
[email protected]a99fce0e2011-03-21 20:58:48154- (void)updateConfirmToQuitPrefMenuItem:(NSMenuItem*)item;
[email protected]eef99c22010-08-17 05:55:16155- (void)registerServicesMenuTypesTo:(NSApplication*)app;
[email protected]d630d7d52010-02-21 00:55:11156- (void)openUrls:(const std::vector<GURL>&)urls;
[email protected]57750f822009-04-21 21:43:09157- (void)getUrl:(NSAppleEventDescriptor*)event
158 withReply:(NSAppleEventDescriptor*)reply;
[email protected]449dd2f2009-05-27 13:04:00159- (void)windowLayeringDidChange:(NSNotification*)inNotification;
[email protected]dd6ab1282010-07-20 01:07:32160- (void)checkForAnyKeyWindows;
[email protected]73f5b362009-08-10 23:58:21161- (BOOL)userWillWaitForInProgressDownloads:(int)downloadCount;
[email protected]09729a552009-08-03 23:21:41162- (BOOL)shouldQuitWithInProgressDownloads;
[email protected]93b59fc2010-12-21 20:00:47163- (void)executeApplication:(id)sender;
[email protected]88d74942009-01-21 22:04:44164@end
[email protected]1bcdb532009-01-16 17:47:57165
166@implementation AppController
167
[email protected]caadfca2010-01-22 21:51:21168@synthesize startupComplete = startupComplete_;
169
[email protected]cd63ef62009-05-06 19:41:37170// This method is called very early in application startup (ie, before
171// the profile is loaded or any preferences have been registered). Defer any
172// user-data initialization until -applicationDidFinishLaunching:.
[email protected]88d74942009-01-21 22:04:44173- (void)awakeFromNib {
[email protected]136140c2009-05-19 13:58:25174 // We need to register the handlers early to catch events fired on launch.
[email protected]57750f822009-04-21 21:43:09175 NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager];
176 [em setEventHandler:self
177 andSelector:@selector(getUrl:withReply:)
178 forEventClass:kInternetEventClass
179 andEventID:kAEGetURL];
180 [em setEventHandler:self
181 andSelector:@selector(getUrl:withReply:)
182 forEventClass:'WWW!' // A particularly ancient AppleEvent that dates
183 andEventID:'OURL']; // back to the Spyglass days.
[email protected]cd63ef62009-05-06 19:41:37184
[email protected]449dd2f2009-05-27 13:04:00185 // Register for various window layering changes. We use these to update
186 // various UI elements (command-key equivalents, etc) when the frontmost
187 // window changes.
188 NSNotificationCenter* notificationCenter =
189 [NSNotificationCenter defaultCenter];
[email protected]ce560f82009-06-03 09:39:44190 [notificationCenter
[email protected]449dd2f2009-05-27 13:04:00191 addObserver:self
192 selector:@selector(windowLayeringDidChange:)
193 name:NSWindowDidBecomeKeyNotification
194 object:nil];
195 [notificationCenter
196 addObserver:self
197 selector:@selector(windowLayeringDidChange:)
198 name:NSWindowDidResignKeyNotification
199 object:nil];
200 [notificationCenter
201 addObserver:self
202 selector:@selector(windowLayeringDidChange:)
203 name:NSWindowDidBecomeMainNotification
204 object:nil];
205 [notificationCenter
206 addObserver:self
207 selector:@selector(windowLayeringDidChange:)
208 name:NSWindowDidResignMainNotification
209 object:nil];
210
211 // Register for a notification that the number of tabs changes in windows
212 // so we can adjust the close tab/window command keys.
213 [notificationCenter
214 addObserver:self
215 selector:@selector(tabsChanged:)
216 name:kTabStripNumberOfTabsChanged
217 object:nil];
218
[email protected]136140c2009-05-19 13:58:25219 // Set up the command updater for when there are no windows open
220 [self initMenuState];
[email protected]f4acfae82009-09-11 00:33:38221
222 // Activate (bring to foreground) if asked to do so. On
223 // Windows this logic isn't necessary since
224 // BrowserWindow::Activate() calls ::SetForegroundWindow() which is
225 // adequate. On Mac, BrowserWindow::Activate() calls -[NSWindow
226 // makeKeyAndOrderFront:] which does not activate the application
227 // itself.
228 const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess();
229 if (parsed_command_line.HasSwitch(switches::kActivateOnLaunch)) {
230 [NSApp activateIgnoringOtherApps:YES];
231 }
[email protected]136140c2009-05-19 13:58:25232}
233
[email protected]7dc8c6b2010-04-09 17:02:50234// (NSApplicationDelegate protocol) This is the Apple-approved place to override
235// the default handlers.
236- (void)applicationWillFinishLaunching:(NSNotification*)notification {
[email protected]58877932010-05-25 05:43:11237 // Nothing here right now.
[email protected]7dc8c6b2010-04-09 17:02:50238}
239
240- (BOOL)tryToTerminateApplication:(NSApplication*)app {
[email protected]3076fad2010-04-28 18:11:45241 // Check for in-process downloads, and prompt the user if they really want
242 // to quit (and thus cancel downloads). Only check if we're not already
243 // shutting down, else the user might be prompted multiple times if the
244 // download isn't stopped before terminate is called again.
245 if (!browser_shutdown::IsTryingToQuit() &&
246 ![self shouldQuitWithInProgressDownloads])
247 return NO;
248
[email protected]7dc8c6b2010-04-09 17:02:50249 // TODO(viettrungluu): Remove Apple Event handlers here? (It's safe to leave
250 // them in, but I'm not sure about UX; we'd also want to disable other things
251 // though.) http://crbug.com/40861
252
[email protected]177aceb2010-11-03 16:17:41253 // Check if the user really wants to quit by employing the confirm-to-quit
254 // mechanism.
255 if (!browser_shutdown::IsTryingToQuit() &&
256 [self applicationShouldTerminate:app] != NSTerminateNow)
257 return NO;
258
[email protected]c984d9f2010-07-20 20:52:20259 size_t num_browsers = BrowserList::size();
[email protected]7dc8c6b2010-04-09 17:02:50260
[email protected]e7dd6d82010-10-07 23:21:12261 // Give any print jobs in progress time to finish.
262 if (!browser_shutdown::IsTryingToQuit())
263 g_browser_process->print_job_manager()->StopJobs(true);
264
[email protected]c984d9f2010-07-20 20:52:20265 // Initiate a shutdown (via BrowserList::CloseAllBrowsers()) if we aren't
266 // already shutting down.
267 if (!browser_shutdown::IsTryingToQuit())
[email protected]c6032e82010-09-13 20:06:05268 BrowserList::CloseAllBrowsers();
[email protected]7dc8c6b2010-04-09 17:02:50269
[email protected]c984d9f2010-07-20 20:52:20270 return num_browsers == 0 ? YES : NO;
[email protected]7dc8c6b2010-04-09 17:02:50271}
272
273- (void)stopTryingToTerminateApplication:(NSApplication*)app {
274 if (browser_shutdown::IsTryingToQuit()) {
275 // Reset the "trying to quit" state, so that closing all browser windows
276 // will no longer lead to termination.
277 browser_shutdown::SetTryingToQuit(false);
278
279 // TODO(viettrungluu): Were we to remove Apple Event handlers above, we
280 // would have to reinstall them here. http://crbug.com/40861
281 }
[email protected]a9e8afc2009-08-11 22:03:17282}
283
[email protected]177aceb2010-11-03 16:17:41284- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)app {
[email protected]a99fce0e2011-03-21 20:58:48285 // Check if the preference is turned on.
286 const PrefService* prefs = [self defaultProfile]->GetPrefs();
[email protected]1eaddf32011-04-05 15:09:23287 if (!prefs->GetBoolean(prefs::kConfirmToQuitEnabled)) {
288 confirm_quit::RecordHistogram(confirm_quit::kNoConfirm);
[email protected]177aceb2010-11-03 16:17:41289 return NSTerminateNow;
[email protected]1eaddf32011-04-05 15:09:23290 }
[email protected]177aceb2010-11-03 16:17:41291
292 // If the application is going to terminate as the result of a Cmd+Q
293 // invocation, use the special sauce to prevent accidental quitting.
294 // http://dev.chromium.org/developers/design-documents/confirm-to-quit-experiment
[email protected]177aceb2010-11-03 16:17:41295
[email protected]2020fd0b2010-12-10 01:16:50296 // This logic is only for keyboard-initiated quits.
[email protected]5df54fb2011-03-07 18:15:26297 if (![ConfirmQuitPanelController eventTriggersFeature:[app currentEvent]])
[email protected]2020fd0b2010-12-10 01:16:50298 return NSTerminateNow;
[email protected]177aceb2010-11-03 16:17:41299
[email protected]5df54fb2011-03-07 18:15:26300 return [[ConfirmQuitPanelController sharedController]
301 runModalLoopForApplication:app];
[email protected]177aceb2010-11-03 16:17:41302}
303
[email protected]449dd2f2009-05-27 13:04:00304// Called when the app is shutting down. Clean-up as appropriate.
[email protected]7dc8c6b2010-04-09 17:02:50305- (void)applicationWillTerminate:(NSNotification*)aNotification {
[email protected]3b6aa8b62009-09-15 21:36:11306 NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager];
307 [em removeEventHandlerForEventClass:kInternetEventClass
308 andEventID:kAEGetURL];
309 [em removeEventHandlerForEventClass:'WWW!'
310 andEventID:'OURL'];
[email protected]3b6aa8b62009-09-15 21:36:11311
[email protected]7dc8c6b2010-04-09 17:02:50312 // There better be no browser windows left at this point.
313 CHECK_EQ(BrowserList::size(), 0u);
[email protected]3b6aa8b62009-09-15 21:36:11314
[email protected]c984d9f2010-07-20 20:52:20315 // Tell BrowserList not to keep the browser process alive. Once all the
316 // browsers get dealloc'd, it will stop the RunLoop and fall back into main().
317 BrowserList::EndKeepAlive();
[email protected]fbc947b2009-06-19 13:28:24318
[email protected]d4e18652009-11-19 21:52:16319 // Close these off if they have open windows.
[email protected]d4e18652009-11-19 21:52:16320 [aboutController_ close];
321
[email protected]449dd2f2009-05-27 13:04:00322 [[NSNotificationCenter defaultCenter] removeObserver:self];
323}
324
[email protected]3b6aa8b62009-09-15 21:36:11325- (void)didEndMainMessageLoop {
326 DCHECK(!BrowserList::HasBrowserWithProfile([self defaultProfile]));
327 if (!BrowserList::HasBrowserWithProfile([self defaultProfile])) {
328 // As we're shutting down, we need to nuke the TabRestoreService, which
329 // will start the shutdown of the NavigationControllers and allow for
330 // proper shutdown. If we don't do this, Chrome won't shut down cleanly,
331 // and may end up crashing when some thread tries to use the IO thread (or
332 // another thread) that is no longer valid.
333 [self defaultProfile]->ResetTabRestoreService();
334 }
335}
336
[email protected]449dd2f2009-05-27 13:04:00337// Helper routine to get the window controller if the key window is a tabbed
338// window, or nil if not. Examples of non-tabbed windows are "about" or
339// "preferences".
340- (TabWindowController*)keyWindowTabController {
341 NSWindowController* keyWindowController =
[email protected]5dbabd02009-09-17 20:44:13342 [[NSApp keyWindow] windowController];
[email protected]449dd2f2009-05-27 13:04:00343 if ([keyWindowController isKindOfClass:[TabWindowController class]])
344 return (TabWindowController*)keyWindowController;
345
346 return nil;
347}
348
[email protected]74c0c642009-09-22 21:25:50349// Helper routine to get the window controller if the main window is a tabbed
350// window, or nil if not. Examples of non-tabbed windows are "about" or
351// "preferences".
352- (TabWindowController*)mainWindowTabController {
353 NSWindowController* mainWindowController =
354 [[NSApp mainWindow] windowController];
355 if ([mainWindowController isKindOfClass:[TabWindowController class]])
356 return (TabWindowController*)mainWindowController;
357
358 return nil;
359}
360
[email protected]449dd2f2009-05-27 13:04:00361// If the window has tabs, make "close window" be cmd-shift-w, otherwise leave
362// it as the normal cmd-w. Capitalization of the key equivalent affects whether
363// the shift modifer is used.
364- (void)adjustCloseWindowMenuItemKeyEquivalent:(BOOL)inHaveTabs {
365 [closeWindowMenuItem_ setKeyEquivalent:(inHaveTabs ? @"W" : @"w")];
[email protected]74c0c642009-09-22 21:25:50366 [closeWindowMenuItem_ setKeyEquivalentModifierMask:NSCommandKeyMask];
[email protected]449dd2f2009-05-27 13:04:00367}
368
369// If the window has tabs, make "close tab" take over cmd-w, otherwise it
370// shouldn't have any key-equivalent because it should be disabled.
371- (void)adjustCloseTabMenuItemKeyEquivalent:(BOOL)hasTabs {
372 if (hasTabs) {
373 [closeTabMenuItem_ setKeyEquivalent:@"w"];
374 [closeTabMenuItem_ setKeyEquivalentModifierMask:NSCommandKeyMask];
375 } else {
376 [closeTabMenuItem_ setKeyEquivalent:@""];
377 [closeTabMenuItem_ setKeyEquivalentModifierMask:0];
378 }
379}
380
[email protected]74c0c642009-09-22 21:25:50381// Explicitly remove any command-key equivalents from the close tab/window
382// menus so that nothing can go haywire if we get a user action during pending
383// updates.
384- (void)clearCloseMenuItemKeyEquivalents {
385 [closeTabMenuItem_ setKeyEquivalent:@""];
386 [closeTabMenuItem_ setKeyEquivalentModifierMask:0];
387 [closeWindowMenuItem_ setKeyEquivalent:@""];
388 [closeWindowMenuItem_ setKeyEquivalentModifierMask:0];
389}
390
[email protected]449dd2f2009-05-27 13:04:00391// See if we have a window with tabs open, and adjust the key equivalents for
[email protected]668c911c2010-03-08 17:05:25392// Close Tab/Close Window accordingly.
[email protected]449dd2f2009-05-27 13:04:00393- (void)fixCloseMenuItemKeyEquivalents {
[email protected]668c911c2010-03-08 17:05:25394 fileMenuUpdatePending_ = NO;
[email protected]449dd2f2009-05-27 13:04:00395 TabWindowController* tabController = [self keyWindowTabController];
[email protected]74c0c642009-09-22 21:25:50396 if (!tabController && ![NSApp keyWindow]) {
397 // There might be a small amount of time where there is no key window,
398 // so just use our main browser window if there is one.
399 tabController = [self mainWindowTabController];
400 }
[email protected]449dd2f2009-05-27 13:04:00401 BOOL windowWithMultipleTabs =
402 (tabController && [tabController numberOfTabs] > 1);
403 [self adjustCloseWindowMenuItemKeyEquivalent:windowWithMultipleTabs];
404 [self adjustCloseTabMenuItemKeyEquivalent:windowWithMultipleTabs];
[email protected]449dd2f2009-05-27 13:04:00405}
406
407// Fix up the "close tab/close window" command-key equivalents. We do this
408// after a delay to ensure that window layer state has been set by the time
[email protected]f2a5c342010-01-21 17:00:29409// we do the enabling. This should only be called on the main thread, code that
410// calls this (even as a side-effect) from other threads needs to be fixed.
[email protected]449dd2f2009-05-27 13:04:00411- (void)delayedFixCloseMenuItemKeyEquivalents {
[email protected]f2a5c342010-01-21 17:00:29412 DCHECK([NSThread isMainThread]);
[email protected]449dd2f2009-05-27 13:04:00413 if (!fileMenuUpdatePending_) {
[email protected]74c0c642009-09-22 21:25:50414 // The OS prefers keypresses to timers, so it's possible that a cmd-w
415 // can sneak in before this timer fires. In order to prevent that from
416 // having any bad consequences, just clear the keys combos altogether. They
417 // will be reset when the timer eventually fires.
[email protected]668c911c2010-03-08 17:05:25418 if ([NSThread isMainThread]) {
419 fileMenuUpdatePending_ = YES;
420 [self clearCloseMenuItemKeyEquivalents];
421 [self performSelector:@selector(fixCloseMenuItemKeyEquivalents)
422 withObject:nil
423 afterDelay:0];
424 } else {
425 // This shouldn't be happening, but if it does, force it to the main
426 // thread to avoid dropping the update. Don't mess with
427 // |fileMenuUpdatePending_| as it's not expected to be threadsafe and
428 // there could be a race between the selector finishing and setting the
429 // flag.
430 [self
431 performSelectorOnMainThread:@selector(fixCloseMenuItemKeyEquivalents)
[email protected]f2a5c342010-01-21 17:00:29432 withObject:nil
433 waitUntilDone:NO];
[email protected]668c911c2010-03-08 17:05:25434 }
[email protected]449dd2f2009-05-27 13:04:00435 }
436}
437
438// Called when we get a notification about the window layering changing to
439// update the UI based on the new main window.
440- (void)windowLayeringDidChange:(NSNotification*)notify {
441 [self delayedFixCloseMenuItemKeyEquivalents];
[email protected]dd6ab1282010-07-20 01:07:32442
443 if ([notify name] == NSWindowDidResignKeyNotification) {
444 // If a window is closed, this notification is fired but |[NSApp keyWindow]|
445 // returns nil regardless of whether any suitable candidates for the key
446 // window remain. It seems that the new key window for the app is not set
447 // until after this notification is fired, so a check is performed after the
448 // run loop is allowed to spin.
449 [self performSelector:@selector(checkForAnyKeyWindows)
450 withObject:nil
451 afterDelay:0.0];
452 }
453}
454
455- (void)checkForAnyKeyWindows {
456 if ([NSApp keyWindow])
457 return;
458
459 NotificationService::current()->Notify(
460 NotificationType::NO_KEY_WINDOW,
461 NotificationService::AllSources(),
462 NotificationService::NoDetails());
[email protected]449dd2f2009-05-27 13:04:00463}
464
465// Called when the number of tabs changes in one of the browser windows. The
466// object is the tab strip controller, but we don't currently care.
467- (void)tabsChanged:(NSNotification*)notify {
[email protected]e72746b12009-10-19 14:11:51468 // We don't need to do this on a delay, as in the method above, because the
469 // window layering isn't changing. As a result, there's no chance that a
470 // different window will sneak in as the key window and cause the problems
471 // we hacked around above by clearing the key equivalents.
472 [self fixCloseMenuItemKeyEquivalents];
[email protected]449dd2f2009-05-27 13:04:00473}
474
[email protected]414fde592009-05-21 16:14:43475// If the auto-update interval is not set, make it 5 hours.
476// This code is specific to Mac Chrome Dev Channel.
477// Placed here for 2 reasons:
478// 1) Same spot as other Pref stuff
479// 2) Try and be friendly by keeping this after app launch
480// TODO(jrg): remove once we go Beta.
481- (void)setUpdateCheckInterval {
482#if defined(GOOGLE_CHROME_BUILD)
483 CFStringRef app = (CFStringRef)@"com.google.Keystone.Agent";
484 CFStringRef checkInterval = (CFStringRef)@"checkInterval";
485 CFPropertyListRef plist = CFPreferencesCopyAppValue(checkInterval, app);
486 if (!plist) {
487 const float fiveHoursInSeconds = 5.0 * 60.0 * 60.0;
[email protected]7dc8c6b2010-04-09 17:02:50488 NSNumber* value = [NSNumber numberWithFloat:fiveHoursInSeconds];
[email protected]414fde592009-05-21 16:14:43489 CFPreferencesSetAppValue(checkInterval, value, app);
490 CFPreferencesAppSynchronize(app);
491 }
492#endif
493}
494
[email protected]136140c2009-05-19 13:58:25495// This is called after profiles have been loaded and preferences registered.
496// It is safe to access the default profile here.
497- (void)applicationDidFinishLaunching:(NSNotification*)notify {
[email protected]c984d9f2010-07-20 20:52:20498 // Notify BrowserList to keep the application running so it doesn't go away
499 // when all the browser windows get closed.
500 BrowserList::StartKeepAlive();
[email protected]136140c2009-05-19 13:58:25501
[email protected]e5575022009-08-22 00:31:22502 bookmarkMenuBridge_.reset(new BookmarkMenuBridge([self defaultProfile]));
[email protected]1c84c82c2009-07-27 15:37:25503 historyMenuBridge_.reset(new HistoryMenuBridge([self defaultProfile]));
[email protected]cd63ef62009-05-06 19:41:37504
[email protected]414fde592009-05-21 16:14:43505 [self setUpdateCheckInterval];
[email protected]2bcec612009-05-14 17:50:53506
507 // Build up the encoding menu, the order of the items differs based on the
508 // current locale (see http://crbug.com/7647 for details).
509 // We need a valid g_browser_process to get the profile which is why we can't
510 // call this from awakeFromNib.
[email protected]6f91e5ae2011-03-21 19:53:14511 NSMenu* viewMenu = [[[NSApp mainMenu] itemWithTag:IDC_VIEW_MENU] submenu];
512 NSMenuItem* encodingMenuItem = [viewMenu itemWithTag:IDC_ENCODING_MENU];
513 NSMenu* encodingMenu = [encodingMenuItem submenu];
[email protected]fc349322009-09-23 22:28:23514 EncodingMenuControllerDelegate::BuildEncodingMenu([self defaultProfile],
[email protected]6f91e5ae2011-03-21 19:53:14515 encodingMenu);
[email protected]2bcec612009-05-14 17:50:53516
[email protected]f2c20fa2009-12-01 17:42:02517 // Since Chrome is localized to more languages than the OS, tell Cocoa which
518 // menu is the Help so it can add the search item to it.
[email protected]caadfca2010-01-22 21:51:21519 if (helpMenu_ && [NSApp respondsToSelector:@selector(setHelpMenu:)])
[email protected]f2c20fa2009-12-01 17:42:02520 [NSApp setHelpMenu:helpMenu_];
[email protected]f2c20fa2009-12-01 17:42:02521
[email protected]12dc3d42010-02-22 23:37:12522 // Record the path to the (browser) app bundle; this is used by the app mode
523 // shim.
524 RecordLastRunAppBundlePath();
525
[email protected]eef99c22010-08-17 05:55:16526 // Makes "Services" menu items available.
527 [self registerServicesMenuTypesTo:[notify object]];
528
[email protected]caadfca2010-01-22 21:51:21529 startupComplete_ = YES;
530
531 // TODO(viettrungluu): This is very temporary, since this should be done "in"
532 // |BrowserMain()|, i.e., this list of startup URLs should be appended to the
533 // (probably-empty) list of URLs from the command line.
[email protected]d630d7d52010-02-21 00:55:11534 if (startupUrls_.size()) {
535 [self openUrls:startupUrls_];
536 [self clearStartupUrls];
[email protected]caadfca2010-01-22 21:51:21537 }
[email protected]aac169d2011-03-18 19:53:03538
539 const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess();
540 if (!parsed_command_line.HasSwitch(switches::kEnableExposeForTabs)) {
541 [tabposeMenuItem_ setHidden:YES];
542 }
[email protected]7c321082009-02-09 15:35:47543}
544
[email protected]a96ec6a2009-11-04 17:27:08545// This is called after profiles have been loaded and preferences registered.
546// It is safe to access the default profile here.
547- (void)applicationDidBecomeActive:(NSNotification*)notify {
548 NotificationService::current()->Notify(NotificationType::APP_ACTIVATED,
549 NotificationService::AllSources(),
550 NotificationService::NoDetails());
551}
552
[email protected]73f5b362009-08-10 23:58:21553// Helper function for populating and displaying the in progress downloads at
554// exit alert panel.
555- (BOOL)userWillWaitForInProgressDownloads:(int)downloadCount {
[email protected]e8b5f7882009-09-14 20:59:13556 NSString* warningText = nil;
557 NSString* explanationText = nil;
[email protected]73f5b362009-08-10 23:58:21558 NSString* waitTitle = nil;
559 NSString* exitTitle = nil;
560
[email protected]757127052010-04-27 14:42:26561 string16 product_name = l10n_util::GetStringUTF16(IDS_PRODUCT_NAME);
[email protected]9dcb59662009-09-23 01:15:50562
[email protected]73f5b362009-08-10 23:58:21563 // Set the dialog text based on whether or not there are multiple downloads.
564 if (downloadCount == 1) {
[email protected]e8b5f7882009-09-14 20:59:13565 // Dialog text: warning and explanation.
[email protected]757127052010-04-27 14:42:26566 warningText = l10n_util::GetNSStringF(
567 IDS_SINGLE_DOWNLOAD_REMOVE_CONFIRM_WARNING, product_name);
568 explanationText = l10n_util::GetNSStringF(
569 IDS_SINGLE_DOWNLOAD_REMOVE_CONFIRM_EXPLANATION, product_name);
[email protected]73f5b362009-08-10 23:58:21570
571 // Cancel download and exit button text.
[email protected]757127052010-04-27 14:42:26572 exitTitle = l10n_util::GetNSString(
573 IDS_SINGLE_DOWNLOAD_REMOVE_CONFIRM_OK_BUTTON_LABEL);
[email protected]73f5b362009-08-10 23:58:21574
575 // Wait for download button text.
[email protected]757127052010-04-27 14:42:26576 waitTitle = l10n_util::GetNSString(
577 IDS_SINGLE_DOWNLOAD_REMOVE_CONFIRM_CANCEL_BUTTON_LABEL);
[email protected]73f5b362009-08-10 23:58:21578 } else {
[email protected]e8b5f7882009-09-14 20:59:13579 // Dialog text: warning and explanation.
[email protected]757127052010-04-27 14:42:26580 warningText = l10n_util::GetNSStringF(
581 IDS_MULTIPLE_DOWNLOADS_REMOVE_CONFIRM_WARNING, product_name,
[email protected]528c56d2010-07-30 19:28:44582 base::IntToString16(downloadCount));
[email protected]757127052010-04-27 14:42:26583 explanationText = l10n_util::GetNSStringF(
584 IDS_MULTIPLE_DOWNLOADS_REMOVE_CONFIRM_EXPLANATION, product_name);
[email protected]73f5b362009-08-10 23:58:21585
586 // Cancel downloads and exit button text.
[email protected]757127052010-04-27 14:42:26587 exitTitle = l10n_util::GetNSString(
588 IDS_MULTIPLE_DOWNLOADS_REMOVE_CONFIRM_OK_BUTTON_LABEL);
[email protected]73f5b362009-08-10 23:58:21589
590 // Wait for downloads button text.
[email protected]757127052010-04-27 14:42:26591 waitTitle = l10n_util::GetNSString(
592 IDS_MULTIPLE_DOWNLOADS_REMOVE_CONFIRM_CANCEL_BUTTON_LABEL);
[email protected]73f5b362009-08-10 23:58:21593 }
594
595 // 'waitButton' is the default choice.
[email protected]e8b5f7882009-09-14 20:59:13596 int choice = NSRunAlertPanel(warningText, explanationText,
597 waitTitle, exitTitle, nil);
[email protected]73f5b362009-08-10 23:58:21598 return choice == NSAlertDefaultReturn ? YES : NO;
599}
600
601// Check all profiles for in progress downloads, and if we find any, prompt the
[email protected]09729a552009-08-03 23:21:41602// user to see if we should continue to exit (and thus cancel the downloads), or
603// if we should wait.
604- (BOOL)shouldQuitWithInProgressDownloads {
[email protected]73f5b362009-08-10 23:58:21605 ProfileManager* profile_manager = g_browser_process->profile_manager();
606 if (!profile_manager)
607 return YES;
[email protected]09729a552009-08-03 23:21:41608
[email protected]844a1002011-04-19 11:37:21609 std::vector<Profile*> profiles(profile_manager->GetLoadedProfiles());
610 for (size_t i = 0; i < profiles.size(); ++i) {
611 DownloadManager* download_manager = profiles[i]->GetDownloadManager();
[email protected]73f5b362009-08-10 23:58:21612 if (download_manager && download_manager->in_progress_count() > 0) {
613 int downloadCount = download_manager->in_progress_count();
614 if ([self userWillWaitForInProgressDownloads:downloadCount]) {
615 // Create a new browser window (if necessary) and navigate to the
616 // downloads page if the user chooses to wait.
[email protected]844a1002011-04-19 11:37:21617 Browser* browser = BrowserList::FindBrowserWithProfile(profiles[i]);
[email protected]73f5b362009-08-10 23:58:21618 if (!browser) {
[email protected]844a1002011-04-19 11:37:21619 browser = Browser::Create(profiles[i]);
[email protected]73f5b362009-08-10 23:58:21620 browser->window()->Show();
621 }
622 DCHECK(browser);
623 browser->ShowDownloadsTab();
624 return NO;
625 }
[email protected]09729a552009-08-03 23:21:41626
[email protected]73f5b362009-08-10 23:58:21627 // User wants to exit.
628 return YES;
[email protected]09729a552009-08-03 23:21:41629 }
[email protected]09729a552009-08-03 23:21:41630 }
631
[email protected]73f5b362009-08-10 23:58:21632 // No profiles or active downloads found, okay to exit.
[email protected]09729a552009-08-03 23:21:41633 return YES;
634}
635
[email protected]fbc947b2009-06-19 13:28:24636// Called to determine if we should enable the "restore tab" menu item.
637// Checks with the TabRestoreService to see if there's anything there to
638// restore and returns YES if so.
639- (BOOL)canRestoreTab {
640 TabRestoreService* service = [self defaultProfile]->GetTabRestoreService();
641 return service && !service->entries().empty();
642}
643
[email protected]f6c15fa2010-06-04 20:08:21644// Returns true if there is not a modal window (either window- or application-
645// modal) blocking the active browser. Note that tab modal dialogs (HTTP auth
646// sheets) will not count as blocking the browser. But things like open/save
647// dialogs that are window modal will block the browser.
648- (BOOL)keyWindowIsNotModal {
[email protected]6a281332009-10-13 17:41:06649 Browser* browser = BrowserList::GetLastActive();
[email protected]dc76e8362010-06-11 16:29:52650 return [NSApp modalWindow] == nil && (!browser ||
[email protected]6a281332009-10-13 17:41:06651 ![[browser->window()->GetNativeHandle() attachedSheet]
[email protected]f6c15fa2010-06-04 20:08:21652 isKindOfClass:[NSWindow class]]);
[email protected]6a281332009-10-13 17:41:06653}
654
[email protected]88d74942009-01-21 22:04:44655// Called to validate menu items when there are no key windows. All the
656// items we care about have been set with the |commandDispatch:| action and
657// a target of FirstResponder in IB. If it's not one of those, let it
658// continue up the responder chain to be handled elsewhere. We pull out the
659// tag as the cross-platform constant to differentiate and dispatch the
660// various commands.
661- (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item {
662 SEL action = [item action];
663 BOOL enable = NO;
664 if (action == @selector(commandDispatch:)) {
665 NSInteger tag = [item tag];
[email protected]fbc947b2009-06-19 13:28:24666 if (menuState_->SupportsCommand(tag)) {
667 switch (tag) {
[email protected]419eb2a2009-11-11 16:26:26668 // The File Menu commands are not automatically disabled by Cocoa when a
669 // dialog sheet obscures the browser window, so we disable several of
670 // them here. We don't need to include IDC_CLOSE_WINDOW, because
671 // app_controller is only activated when there are no key windows (see
672 // function comment).
[email protected]fbc947b2009-06-19 13:28:24673 case IDC_RESTORE_TAB:
[email protected]f6c15fa2010-06-04 20:08:21674 enable = [self keyWindowIsNotModal] && [self canRestoreTab];
[email protected]6a281332009-10-13 17:41:06675 break;
[email protected]f6c15fa2010-06-04 20:08:21676 // Browser-level items that open in new tabs should not open if there's
677 // a window- or app-modal dialog.
[email protected]6a281332009-10-13 17:41:06678 case IDC_OPEN_FILE:
[email protected]6a281332009-10-13 17:41:06679 case IDC_NEW_TAB:
[email protected]f6c15fa2010-06-04 20:08:21680 case IDC_SHOW_HISTORY:
681 case IDC_SHOW_BOOKMARK_MANAGER:
682 enable = [self keyWindowIsNotModal];
683 break;
684 // Browser-level items that open in new windows.
685 case IDC_NEW_WINDOW:
686 case IDC_TASK_MANAGER:
687 // Allow the user to open a new window if there's a window-modal
688 // dialog.
689 enable = [self keyWindowIsNotModal] || ([NSApp modalWindow] == nil);
[email protected]fbc947b2009-06-19 13:28:24690 break;
[email protected]446e16a2010-05-03 21:23:58691 case IDC_SYNC_BOOKMARKS: {
692 Profile* defaultProfile = [self defaultProfile];
693 // The profile may be NULL during shutdown -- see
694 // http://code.google.com/p/chromium/issues/detail?id=43048 .
695 //
696 // TODO(akalin,viettrungluu): Figure out whether this method
697 // can be prevented from being called if defaultProfile is
698 // NULL.
699 if (!defaultProfile) {
700 LOG(WARNING)
701 << "NULL defaultProfile detected -- not doing anything";
702 break;
703 }
[email protected]d3b98c82010-07-14 07:45:59704 enable = defaultProfile->IsSyncAccessible() &&
705 [self keyWindowIsNotModal];
[email protected]446e16a2010-05-03 21:23:58706 sync_ui_util::UpdateSyncItem(item, enable, defaultProfile);
[email protected]f9bc9b92009-11-24 00:55:35707 break;
[email protected]446e16a2010-05-03 21:23:58708 }
[email protected]fbc947b2009-06-19 13:28:24709 default:
[email protected]f6c15fa2010-06-04 20:08:21710 enable = menuState_->IsCommandEnabled(tag) ?
711 [self keyWindowIsNotModal] : NO;
[email protected]fbc947b2009-06-19 13:28:24712 }
713 }
[email protected]3b6aa8b62009-09-15 21:36:11714 } else if (action == @selector(terminate:)) {
[email protected]88d74942009-01-21 22:04:44715 enable = YES;
[email protected]3111f08b2009-04-30 16:01:52716 } else if (action == @selector(showPreferences:)) {
717 enable = YES;
[email protected]bde3dda2009-05-20 22:13:07718 } else if (action == @selector(orderFrontStandardAboutPanel:)) {
719 enable = YES;
[email protected]93b59fc2010-12-21 20:00:47720 } else if (action == @selector(commandFromDock:)) {
[email protected]3d4bd23f2009-10-06 03:58:38721 enable = YES;
[email protected]a99fce0e2011-03-21 20:58:48722 } else if (action == @selector(toggleConfirmToQuit:)) {
723 [self updateConfirmToQuitPrefMenuItem:static_cast<NSMenuItem*>(item)];
724 enable = YES;
[email protected]88d74942009-01-21 22:04:44725 }
726 return enable;
727}
728
[email protected]947fc0d2010-01-14 22:33:05729// Called when the user picks a menu item when there are no key windows, or when
730// there is no foreground browser window. Calls through to the browser object to
731// execute the command. This assumes that the command is supported and doesn't
[email protected]d630d7d52010-02-21 00:55:11732// check, otherwise it should have been disabled in the UI in
733// |-validateUserInterfaceItem:|.
[email protected]88d74942009-01-21 22:04:44734- (void)commandDispatch:(id)sender {
[email protected]93e181762009-05-29 14:30:38735 Profile* defaultProfile = [self defaultProfile];
[email protected]f0a51fb52009-03-05 12:46:38736
[email protected]947fc0d2010-01-14 22:33:05737 // Handle the case where we're dispatching a command from a sender that's in a
738 // browser window. This means that the command came from a background window
739 // and is getting here because the foreground window is not a browser window.
740 if ([sender respondsToSelector:@selector(window)]) {
741 id delegate = [[sender window] windowController];
742 if ([delegate isKindOfClass:[BrowserWindowController class]]) {
743 [delegate commandDispatch:sender];
744 return;
745 }
746 }
747
[email protected]88d74942009-01-21 22:04:44748 NSInteger tag = [sender tag];
749 switch (tag) {
[email protected]fbc947b2009-06-19 13:28:24750 case IDC_NEW_TAB:
[email protected]d630d7d52010-02-21 00:55:11751 // Create a new tab in an existing browser window (which we activate) if
752 // possible.
753 if (Browser* browser = ActivateBrowser(defaultProfile)) {
754 browser->ExecuteCommand(IDC_NEW_TAB);
755 break;
756 }
757 // Else fall through to create new window.
[email protected]88d74942009-01-21 22:04:44758 case IDC_NEW_WINDOW:
[email protected]d630d7d52010-02-21 00:55:11759 CreateBrowser(defaultProfile);
760 break;
[email protected]91a5b3e2009-10-30 19:32:22761 case IDC_FOCUS_LOCATION:
[email protected]d630d7d52010-02-21 00:55:11762 ActivateOrCreateBrowser(defaultProfile)->
763 ExecuteCommand(IDC_FOCUS_LOCATION);
[email protected]88d74942009-01-21 22:04:44764 break;
[email protected]cddc5242009-12-07 18:54:33765 case IDC_FOCUS_SEARCH:
[email protected]d630d7d52010-02-21 00:55:11766 ActivateOrCreateBrowser(defaultProfile)->ExecuteCommand(IDC_FOCUS_SEARCH);
[email protected]cddc5242009-12-07 18:54:33767 break;
[email protected]863ff662009-01-26 20:18:18768 case IDC_NEW_INCOGNITO_WINDOW:
[email protected]e9e211272009-09-22 16:39:11769 Browser::OpenEmptyWindow(defaultProfile->GetOffTheRecordProfile());
[email protected]863ff662009-01-26 20:18:18770 break;
[email protected]fbc947b2009-06-19 13:28:24771 case IDC_RESTORE_TAB:
772 Browser::OpenWindowWithRestoredTabs(defaultProfile);
773 break;
[email protected]e19516d2009-04-28 17:15:19774 case IDC_OPEN_FILE:
[email protected]d630d7d52010-02-21 00:55:11775 CreateBrowser(defaultProfile)->ExecuteCommand(IDC_OPEN_FILE);
[email protected]e19516d2009-04-28 17:15:19776 break;
[email protected]1fdff702009-10-22 00:36:18777 case IDC_CLEAR_BROWSING_DATA: {
[email protected]93e181762009-05-29 14:30:38778 // There may not be a browser open, so use the default profile.
[email protected]445177652011-03-09 02:04:05779 if (Browser* browser = ActivateBrowser(defaultProfile)) {
780 browser->OpenClearBrowsingDataDialog();
[email protected]c8de64a2011-01-25 17:10:23781 } else {
[email protected]445177652011-03-09 02:04:05782 Browser::OpenClearBrowingDataDialogWindow(defaultProfile);
[email protected]c8de64a2011-01-25 17:10:23783 }
[email protected]93e181762009-05-29 14:30:38784 break;
[email protected]1fdff702009-10-22 00:36:18785 }
[email protected]4aee5652009-11-19 18:59:21786 case IDC_IMPORT_SETTINGS: {
[email protected]445177652011-03-09 02:04:05787 if (Browser* browser = ActivateBrowser(defaultProfile)) {
788 browser->OpenImportSettingsDialog();
[email protected]c8de64a2011-01-25 17:10:23789 } else {
[email protected]445177652011-03-09 02:04:05790 Browser::OpenImportSettingsDialogWindow(defaultProfile);
[email protected]c8de64a2011-01-25 17:10:23791 }
[email protected]4aee5652009-11-19 18:59:21792 break;
793 }
[email protected]af7c25e2010-01-23 05:46:45794 case IDC_SHOW_BOOKMARK_MANAGER:
[email protected]896220042010-03-23 18:14:28795 UserMetrics::RecordAction(UserMetricsAction("ShowBookmarkManager"),
796 defaultProfile);
[email protected]90a8bf252010-03-12 00:09:47797 if (Browser* browser = ActivateBrowser(defaultProfile)) {
[email protected]47e532b2010-04-23 17:46:40798 // Open a bookmark manager tab.
[email protected]90a8bf252010-03-12 00:09:47799 browser->OpenBookmarkManager();
800 } else {
[email protected]47e532b2010-04-23 17:46:40801 // No browser window, so create one for the bookmark manager tab.
802 Browser::OpenBookmarkManagerWindow(defaultProfile);
[email protected]90a8bf252010-03-12 00:09:47803 }
[email protected]af7c25e2010-01-23 05:46:45804 break;
[email protected]91a5b3e2009-10-30 19:32:22805 case IDC_SHOW_HISTORY:
[email protected]d630d7d52010-02-21 00:55:11806 if (Browser* browser = ActivateBrowser(defaultProfile))
807 browser->ShowHistoryTab();
808 else
809 Browser::OpenHistoryWindow(defaultProfile);
[email protected]91a5b3e2009-10-30 19:32:22810 break;
811 case IDC_SHOW_DOWNLOADS:
[email protected]d630d7d52010-02-21 00:55:11812 if (Browser* browser = ActivateBrowser(defaultProfile))
813 browser->ShowDownloadsTab();
814 else
815 Browser::OpenDownloadsWindow(defaultProfile);
[email protected]91a5b3e2009-10-30 19:32:22816 break;
[email protected]bb37e6ec2009-11-18 17:59:36817 case IDC_MANAGE_EXTENSIONS:
[email protected]d630d7d52010-02-21 00:55:11818 if (Browser* browser = ActivateBrowser(defaultProfile))
819 browser->ShowExtensionsTab();
820 else
821 Browser::OpenExtensionsWindow(defaultProfile);
[email protected]bb37e6ec2009-11-18 17:59:36822 break;
[email protected]1fdff702009-10-22 00:36:18823 case IDC_HELP_PAGE:
[email protected]d630d7d52010-02-21 00:55:11824 if (Browser* browser = ActivateBrowser(defaultProfile))
825 browser->OpenHelpTab();
826 else
827 Browser::OpenHelpWindow(defaultProfile);
[email protected]1fdff702009-10-22 00:36:18828 break;
[email protected]1c41a932010-11-22 23:41:23829 case IDC_FEEDBACK: {
[email protected]3896b182009-11-03 16:15:32830 Browser* browser = BrowserList::GetLastActive();
[email protected]d630d7d52010-02-21 00:55:11831 TabContents* currentTab =
832 browser ? browser->GetSelectedTabContents() : NULL;
[email protected]3896b182009-11-03 16:15:32833 BugReportWindowController* controller =
834 [[BugReportWindowController alloc]
[email protected]d630d7d52010-02-21 00:55:11835 initWithTabContents:currentTab
836 profile:[self defaultProfile]];
[email protected]3896b182009-11-03 16:15:32837 [controller runModalDialog];
838 break;
839 }
[email protected]f9bc9b92009-11-24 00:55:35840 case IDC_SYNC_BOOKMARKS:
[email protected]446e16a2010-05-03 21:23:58841 // The profile may be NULL during shutdown -- see
842 // http://code.google.com/p/chromium/issues/detail?id=43048 .
843 //
844 // TODO(akalin,viettrungluu): Figure out whether this method can
845 // be prevented from being called if defaultProfile is NULL.
846 if (!defaultProfile) {
847 LOG(WARNING) << "NULL defaultProfile detected -- not doing anything";
848 break;
849 }
[email protected]f9bc9b92009-11-24 00:55:35850 // TODO(akalin): Add a constant to denote starting sync from the
851 // main menu and use that instead of START_FROM_WRENCH.
[email protected]40f047972009-11-25 03:54:40852 sync_ui_util::OpenSyncMyBookmarksDialog(
[email protected]c8de64a2011-01-25 17:10:23853 defaultProfile, ActivateBrowser(defaultProfile),
854 ProfileSyncService::START_FROM_WRENCH);
[email protected]f9bc9b92009-11-24 00:55:35855 break;
[email protected]ceaaf1812010-01-20 22:13:09856 case IDC_TASK_MANAGER:
[email protected]896220042010-03-23 18:14:28857 UserMetrics::RecordAction(UserMetricsAction("TaskManager"),
858 defaultProfile);
[email protected]adb6a84d2011-02-07 16:58:40859 TaskManagerMac::Show(false);
[email protected]ceaaf1812010-01-20 22:13:09860 break;
[email protected]4a42d272010-06-18 01:29:42861 case IDC_OPTIONS:
862 [self showPreferences:sender];
863 break;
[email protected]93b59fc2010-12-21 20:00:47864 default:
865 // Background Applications use dynamic values that must be less than the
866 // smallest value among the predefined IDC_* labels.
867 if ([sender tag] < IDC_MinimumLabelValue)
868 [self executeApplication:sender];
869 break;
[email protected]d630d7d52010-02-21 00:55:11870 }
[email protected]88d74942009-01-21 22:04:44871}
872
[email protected]93b59fc2010-12-21 20:00:47873// Run a (background) application in a new tab.
874- (void)executeApplication:(id)sender {
875 NSInteger tag = [sender tag];
876 Profile* profile = [self defaultProfile];
877 DCHECK(profile);
878 BackgroundApplicationListModel applications(profile);
879 DCHECK(tag >= 0 &&
880 tag < static_cast<int>(applications.size()));
881 Browser* browser = BrowserList::GetLastActive();
882 if (!browser) {
883 Browser::OpenEmptyWindow(profile);
884 browser = BrowserList::GetLastActive();
885 }
886 const Extension* extension = applications.GetExtension(tag);
887 browser->OpenApplicationTab(profile, extension, NULL);
888}
889
[email protected]947fc0d2010-01-14 22:33:05890// Same as |-commandDispatch:|, but executes commands using a disposition
891// determined by the key flags. This will get called in the case where the
892// frontmost window is not a browser window, and the user has command-clicked
893// a button in a background browser window whose action is
894// |-commandDispatchUsingKeyModifiers:|
895- (void)commandDispatchUsingKeyModifiers:(id)sender {
896 DCHECK(sender);
897 if ([sender respondsToSelector:@selector(window)]) {
898 id delegate = [[sender window] windowController];
899 if ([delegate isKindOfClass:[BrowserWindowController class]]) {
900 [delegate commandDispatchUsingKeyModifiers:sender];
901 }
902 }
903}
904
[email protected]ff81e0a2009-04-13 14:58:54905// NSApplication delegate method called when someone clicks on the
906// dock icon and there are no open windows. To match standard mac
907// behavior, we should open a new window.
908- (BOOL)applicationShouldHandleReopen:(NSApplication*)theApplication
909 hasVisibleWindows:(BOOL)flag {
[email protected]70631402010-04-26 20:16:54910 // If the browser is currently trying to quit, don't do anything and return NO
911 // to prevent AppKit from doing anything.
912 // TODO(rohitrao): Remove this code when http://crbug.com/40861 is resolved.
913 if (browser_shutdown::IsTryingToQuit())
914 return NO;
915
[email protected]ff81e0a2009-04-13 14:58:54916 // Don't do anything if there are visible windows. This will cause
917 // AppKit to unminimize the most recently minimized window.
918 if (flag)
919 return YES;
920
[email protected]0d1210d62010-09-07 17:31:38921 // If launched as a hidden login item (due to installation of a persistent app
922 // or by the user, for example in System Preferenecs->Accounts->Login Items),
923 // allow session to be restored first time the user clicks on a Dock icon.
924 // Normally, it'd just open a new empty page.
925 {
926 static BOOL doneOnce = NO;
927 if (!doneOnce) {
928 doneOnce = YES;
[email protected]0378bf42011-01-01 18:20:14929 if (base::mac::WasLaunchedAsHiddenLoginItem()) {
[email protected]0d1210d62010-09-07 17:31:38930 SessionService* sessionService =
931 [self defaultProfile]->GetSessionService();
932 if (sessionService &&
933 sessionService->RestoreIfNecessary(std::vector<GURL>()))
934 return NO;
935 }
936 }
937 }
[email protected]ff81e0a2009-04-13 14:58:54938 // Otherwise open a new window.
[email protected]d630d7d52010-02-21 00:55:11939 {
[email protected]0fbd70332010-06-01 19:28:34940 AutoReset<bool> auto_reset_in_run(&g_is_opening_new_window, true);
[email protected]d630d7d52010-02-21 00:55:11941 Browser::OpenEmptyWindow([self defaultProfile]);
942 }
[email protected]ff81e0a2009-04-13 14:58:54943
944 // We've handled the reopen event, so return NO to tell AppKit not
945 // to do anything.
946 return NO;
947}
948
[email protected]88d74942009-01-21 22:04:44949- (void)initMenuState {
[email protected]3111f08b2009-04-30 16:01:52950 menuState_.reset(new CommandUpdater(NULL));
[email protected]fbc947b2009-06-19 13:28:24951 menuState_->UpdateCommandEnabled(IDC_NEW_TAB, true);
[email protected]88d74942009-01-21 22:04:44952 menuState_->UpdateCommandEnabled(IDC_NEW_WINDOW, true);
[email protected]863ff662009-01-26 20:18:18953 menuState_->UpdateCommandEnabled(IDC_NEW_INCOGNITO_WINDOW, true);
[email protected]e19516d2009-04-28 17:15:19954 menuState_->UpdateCommandEnabled(IDC_OPEN_FILE, true);
[email protected]93e181762009-05-29 14:30:38955 menuState_->UpdateCommandEnabled(IDC_CLEAR_BROWSING_DATA, true);
[email protected]fbc947b2009-06-19 13:28:24956 menuState_->UpdateCommandEnabled(IDC_RESTORE_TAB, false);
[email protected]91a5b3e2009-10-30 19:32:22957 menuState_->UpdateCommandEnabled(IDC_FOCUS_LOCATION, true);
[email protected]cddc5242009-12-07 18:54:33958 menuState_->UpdateCommandEnabled(IDC_FOCUS_SEARCH, true);
[email protected]af7c25e2010-01-23 05:46:45959 menuState_->UpdateCommandEnabled(IDC_SHOW_BOOKMARK_MANAGER, true);
[email protected]91a5b3e2009-10-30 19:32:22960 menuState_->UpdateCommandEnabled(IDC_SHOW_HISTORY, true);
961 menuState_->UpdateCommandEnabled(IDC_SHOW_DOWNLOADS, true);
[email protected]bb37e6ec2009-11-18 17:59:36962 menuState_->UpdateCommandEnabled(IDC_MANAGE_EXTENSIONS, true);
[email protected]1fdff702009-10-22 00:36:18963 menuState_->UpdateCommandEnabled(IDC_HELP_PAGE, true);
[email protected]4aee5652009-11-19 18:59:21964 menuState_->UpdateCommandEnabled(IDC_IMPORT_SETTINGS, true);
[email protected]1c41a932010-11-22 23:41:23965 menuState_->UpdateCommandEnabled(IDC_FEEDBACK, true);
[email protected]d3b98c82010-07-14 07:45:59966 menuState_->UpdateCommandEnabled(IDC_SYNC_BOOKMARKS, true);
[email protected]ceaaf1812010-01-20 22:13:09967 menuState_->UpdateCommandEnabled(IDC_TASK_MANAGER, true);
[email protected]88d74942009-01-21 22:04:44968}
[email protected]1bcdb532009-01-16 17:47:57969
[email protected]a99fce0e2011-03-21 20:58:48970// The Confirm to Quit preference is atypical in that the preference lives in
971// the app menu right above the Quit menu item. This method will refresh the
972// display of that item depending on the preference state.
973- (void)updateConfirmToQuitPrefMenuItem:(NSMenuItem*)item {
974 // Format the string so that the correct key equivalent is displayed.
975 NSString* acceleratorString = [ConfirmQuitPanelController keyCommandString];
976 NSString* title = l10n_util::GetNSStringF(IDS_CONFIRM_TO_QUIT_OPTION,
977 base::SysNSStringToUTF16(acceleratorString));
978 [item setTitle:title];
979
980 const PrefService* prefService = [self defaultProfile]->GetPrefs();
981 bool enabled = prefService->GetBoolean(prefs::kConfirmToQuitEnabled);
982 [item setState:enabled ? NSOnState : NSOffState];
983}
984
[email protected]eef99c22010-08-17 05:55:16985- (void)registerServicesMenuTypesTo:(NSApplication*)app {
[email protected]09ea24c2010-08-23 19:05:41986 // Note that RenderWidgetHostViewCocoa implements NSServicesRequests which
987 // handles requests from services.
988 NSArray* types = [NSArray arrayWithObjects:NSStringPboardType, nil];
989 [app registerServicesMenuSendTypes:types returnTypes:types];
[email protected]eef99c22010-08-17 05:55:16990}
991
[email protected]3f34599d2009-03-25 22:11:43992- (Profile*)defaultProfile {
[email protected]3f34599d2009-03-25 22:11:43993 if (g_browser_process->profile_manager())
[email protected]844a1002011-04-19 11:37:21994 return g_browser_process->profile_manager()->GetDefaultProfile();
[email protected]3f34599d2009-03-25 22:11:43995
[email protected]f6314002009-04-23 01:18:13996 return NULL;
[email protected]3f34599d2009-03-25 22:11:43997}
998
[email protected]57750f822009-04-21 21:43:09999// Various methods to open URLs that we get in a native fashion. We use
1000// BrowserInit here because on the other platforms, URLs to open come through
1001// the ProcessSingleton, and it calls BrowserInit. It's best to bottleneck the
1002// openings through that for uniform handling.
1003
[email protected]d630d7d52010-02-21 00:55:111004- (void)openUrls:(const std::vector<GURL>&)urls {
[email protected]caadfca2010-01-22 21:51:211005 // If the browser hasn't started yet, just queue up the URLs.
1006 if (!startupComplete_) {
[email protected]d630d7d52010-02-21 00:55:111007 startupUrls_.insert(startupUrls_.end(), urls.begin(), urls.end());
[email protected]136140c2009-05-19 13:58:251008 return;
1009 }
[email protected]57750f822009-04-21 21:43:091010
[email protected]0e863512009-05-28 19:45:071011 Browser* browser = BrowserList::GetLastActive();
1012 // if no browser window exists then create one with no tabs to be filled in
1013 if (!browser) {
1014 browser = Browser::Create([self defaultProfile]);
[email protected]ce560f82009-06-03 09:39:441015 browser->window()->Show();
[email protected]0e863512009-05-28 19:45:071016 }
1017
[email protected]947446b2010-10-21 03:36:311018 CommandLine dummy(CommandLine::NO_PROGRAM);
[email protected]f805fe82010-08-03 22:47:101019 BrowserInit::LaunchWithProfile launch(FilePath(), dummy);
[email protected]0e863512009-05-28 19:45:071020 launch.OpenURLsInBrowser(browser, false, urls);
[email protected]57750f822009-04-21 21:43:091021}
1022
[email protected]57750f822009-04-21 21:43:091023- (void)getUrl:(NSAppleEventDescriptor*)event
1024 withReply:(NSAppleEventDescriptor*)reply {
1025 NSString* urlStr = [[event paramDescriptorForKeyword:keyDirectObject]
1026 stringValue];
1027
1028 GURL gurl(base::SysNSStringToUTF8(urlStr));
1029 std::vector<GURL> gurlVector;
1030 gurlVector.push_back(gurl);
1031
[email protected]d630d7d52010-02-21 00:55:111032 [self openUrls:gurlVector];
[email protected]57750f822009-04-21 21:43:091033}
1034
[email protected]caadfca2010-01-22 21:51:211035- (void)application:(NSApplication*)sender
1036 openFiles:(NSArray*)filenames {
[email protected]57750f822009-04-21 21:43:091037 std::vector<GURL> gurlVector;
[email protected]caadfca2010-01-22 21:51:211038 for (NSString* file in filenames) {
1039 GURL gurl = net::FilePathToFileURL(FilePath(base::SysNSStringToUTF8(file)));
[email protected]57750f822009-04-21 21:43:091040 gurlVector.push_back(gurl);
1041 }
[email protected]caadfca2010-01-22 21:51:211042 if (!gurlVector.empty())
[email protected]d630d7d52010-02-21 00:55:111043 [self openUrls:gurlVector];
[email protected]caadfca2010-01-22 21:51:211044 else
1045 NOTREACHED() << "Nothing to open!";
[email protected]57750f822009-04-21 21:43:091046
[email protected]caadfca2010-01-22 21:51:211047 [sender replyToOpenOrPrint:NSApplicationDelegateReplySuccess];
[email protected]57750f822009-04-21 21:43:091048}
[email protected]3f34599d2009-03-25 22:11:431049
[email protected]3111f08b2009-04-30 16:01:521050// Show the preferences window, or bring it to the front if it's already
1051// visible.
1052- (IBAction)showPreferences:(id)sender {
[email protected]445177652011-03-09 02:04:051053 if (Browser* browser = ActivateBrowser([self defaultProfile])) {
1054 // Show options tab in the active browser window.
1055 browser->OpenOptionsDialog();
[email protected]4a42d272010-06-18 01:29:421056 } else {
[email protected]445177652011-03-09 02:04:051057 // No browser window, so create one for the options tab.
1058 Browser::OpenOptionsWindow([self defaultProfile]);
[email protected]4a42d272010-06-18 01:29:421059 }
[email protected]d989f07c2009-11-14 00:35:461060}
1061
[email protected]bde3dda2009-05-20 22:13:071062// Called when the about window is closed. We use this to release the
1063// window controller.
[email protected]d4e18652009-11-19 21:52:161064- (void)aboutWindowClosed:(NSNotification*)notification {
1065 NSWindow* window = [aboutController_ window];
[email protected]2ce9c89752011-02-25 18:24:341066 DCHECK_EQ([notification object], window);
[email protected]bde3dda2009-05-20 22:13:071067 [[NSNotificationCenter defaultCenter]
[email protected]49aeee52009-10-26 19:58:131068 removeObserver:self
[email protected]d4e18652009-11-19 21:52:161069 name:NSWindowWillCloseNotification
1070 object:window];
1071 // AboutWindowControllers are autoreleased in
1072 // -[AboutWindowController windowWillClose:].
1073 aboutController_ = nil;
[email protected]bde3dda2009-05-20 22:13:071074}
1075
1076- (IBAction)orderFrontStandardAboutPanel:(id)sender {
[email protected]bde3dda2009-05-20 22:13:071077 if (!aboutController_) {
[email protected]d4e18652009-11-19 21:52:161078 aboutController_ =
1079 [[AboutWindowController alloc] initWithProfile:[self defaultProfile]];
[email protected]49aeee52009-10-26 19:58:131080
[email protected]bde3dda2009-05-20 22:13:071081 // Watch for a notification of when it goes away so that we can destroy
1082 // the controller.
1083 [[NSNotificationCenter defaultCenter]
1084 addObserver:self
1085 selector:@selector(aboutWindowClosed:)
[email protected]d4e18652009-11-19 21:52:161086 name:NSWindowWillCloseNotification
1087 object:[aboutController_ window]];
[email protected]bde3dda2009-05-20 22:13:071088 }
[email protected]49aeee52009-10-26 19:58:131089
[email protected]bde3dda2009-05-20 22:13:071090 [aboutController_ showWindow:self];
[email protected]bde3dda2009-05-20 22:13:071091}
1092
[email protected]a99fce0e2011-03-21 20:58:481093- (IBAction)toggleConfirmToQuit:(id)sender {
1094 PrefService* prefService = [self defaultProfile]->GetPrefs();
1095 bool enabled = prefService->GetBoolean(prefs::kConfirmToQuitEnabled);
1096 prefService->SetBoolean(prefs::kConfirmToQuitEnabled, !enabled);
1097}
1098
[email protected]3d4bd23f2009-10-06 03:58:381099// Explicitly bring to the foreground when creating new windows from the dock.
[email protected]93b59fc2010-12-21 20:00:471100- (void)commandFromDock:(id)sender {
[email protected]3d4bd23f2009-10-06 03:58:381101 [NSApp activateIgnoringOtherApps:YES];
1102 [self commandDispatch:sender];
1103}
1104
[email protected]227fcee2010-06-11 19:19:371105- (NSMenu*)applicationDockMenu:(NSApplication*)sender {
[email protected]018a3962009-09-17 22:23:441106 NSMenu* dockMenu = [[[NSMenu alloc] initWithTitle: @""] autorelease];
[email protected]93b59fc2010-12-21 20:00:471107 Profile* profile = [self defaultProfile];
1108
[email protected]018a3962009-09-17 22:23:441109 NSString* titleStr = l10n_util::GetNSStringWithFixup(IDS_NEW_WINDOW_MAC);
[email protected]8c6b029d2011-01-21 22:39:511110 scoped_nsobject<NSMenuItem> item(
1111 [[NSMenuItem alloc] initWithTitle:titleStr
1112 action:@selector(commandFromDock:)
1113 keyEquivalent:@""]);
[email protected]288bfcd32009-09-14 18:14:461114 [item setTarget:self];
1115 [item setTag:IDC_NEW_WINDOW];
[email protected]018a3962009-09-17 22:23:441116 [dockMenu addItem:item];
[email protected]288bfcd32009-09-14 18:14:461117
1118 titleStr = l10n_util::GetNSStringWithFixup(IDS_NEW_INCOGNITO_WINDOW_MAC);
[email protected]018a3962009-09-17 22:23:441119 item.reset([[NSMenuItem alloc] initWithTitle:titleStr
[email protected]8c6b029d2011-01-21 22:39:511120 action:@selector(commandFromDock:)
[email protected]018a3962009-09-17 22:23:441121 keyEquivalent:@""]);
[email protected]288bfcd32009-09-14 18:14:461122 [item setTarget:self];
1123 [item setTag:IDC_NEW_INCOGNITO_WINDOW];
[email protected]018a3962009-09-17 22:23:441124 [dockMenu addItem:item];
[email protected]288bfcd32009-09-14 18:14:461125
[email protected]8c6b029d2011-01-21 22:39:511126 // TODO(rickcam): Mock out BackgroundApplicationListModel, then add unit
1127 // tests which use the mock in place of the profile-initialized model.
1128
1129 // Avoid breaking unit tests which have no profile.
1130 if (profile) {
1131 BackgroundApplicationListModel applications(profile);
1132 if (applications.size()) {
1133 int position = 0;
1134 NSString* menuStr =
1135 l10n_util::GetNSStringWithFixup(IDS_BACKGROUND_APPS_MAC);
1136 scoped_nsobject<NSMenu> appMenu([[NSMenu alloc] initWithTitle:menuStr]);
1137 for (ExtensionList::const_iterator cursor = applications.begin();
1138 cursor != applications.end();
1139 ++cursor, ++position) {
[email protected]2ce9c89752011-02-25 18:24:341140 DCHECK_EQ(applications.GetPosition(*cursor), position);
[email protected]8c6b029d2011-01-21 22:39:511141 NSString* itemStr =
1142 base::SysUTF16ToNSString(UTF8ToUTF16((*cursor)->name()));
1143 scoped_nsobject<NSMenuItem> appItem([[NSMenuItem alloc]
1144 initWithTitle:itemStr
1145 action:@selector(commandFromDock:)
1146 keyEquivalent:@""]);
1147 [appItem setTarget:self];
1148 [appItem setTag:position];
1149 [appMenu addItem:appItem];
1150 }
1151 scoped_nsobject<NSMenuItem> appMenuItem([[NSMenuItem alloc]
1152 initWithTitle:menuStr
1153 action:@selector(commandFromDock:)
1154 keyEquivalent:@""]);
1155 [appMenuItem setTarget:self];
1156 [appMenuItem setTag:position];
1157 [appMenuItem setSubmenu:appMenu];
1158 [dockMenu addItem:appMenuItem];
1159 }
1160 }
1161
[email protected]018a3962009-09-17 22:23:441162 return dockMenu;
[email protected]288bfcd32009-09-14 18:14:461163}
1164
[email protected]d630d7d52010-02-21 00:55:111165- (const std::vector<GURL>&)startupUrls {
1166 return startupUrls_;
[email protected]caadfca2010-01-22 21:51:211167}
1168
[email protected]d630d7d52010-02-21 00:55:111169- (void)clearStartupUrls {
1170 startupUrls_.clear();
[email protected]caadfca2010-01-22 21:51:211171}
1172
[email protected]d630d7d52010-02-21 00:55:111173@end // @implementation AppController
[email protected]60ad3e22009-09-18 21:07:191174
1175//---------------------------------------------------------------------------
1176
[email protected]0934de82011-03-09 18:42:161177namespace browser {
1178
1179void ShowInstantConfirmDialog(gfx::NativeWindow parent, Profile* profile) {
1180 if (Browser* browser = ActivateBrowser(profile)) {
[email protected]b99081b2011-03-10 17:43:471181 browser->OpenInstantConfirmDialog();
[email protected]0934de82011-03-09 18:42:161182 } else {
[email protected]b99081b2011-03-10 17:43:471183 Browser::OpenInstantConfirmDialogWindow(profile);
[email protected]0934de82011-03-09 18:42:161184 }
1185}
1186
1187} // namespace browser
1188
[email protected]216e9042009-10-29 17:05:231189namespace app_controller_mac {
1190
1191bool IsOpeningNewWindow() {
1192 return g_is_opening_new_window;
1193}
1194
1195} // namespace app_controller_mac