blob: ba1c8464670947d5433b14818b1934f1bcea3f64 [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]a07676b22011-06-17 16:36:5316#include "chrome/browser/background/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]a99fce0e2011-03-21 20:58:4822#include "chrome/browser/prefs/pref_service.h"
[email protected]e7dd6d82010-10-07 23:21:1223#include "chrome/browser/printing/print_job_manager.h"
[email protected]8ecad5e2010-12-02 21:18:3324#include "chrome/browser/profiles/profile_manager.h"
[email protected]d2912a22011-03-15 15:20:5025#include "chrome/browser/sessions/session_service.h"
[email protected]92371eb2011-04-28 11:50:1526#include "chrome/browser/sessions/session_service_factory.h"
[email protected]fbc947b2009-06-19 13:28:2427#include "chrome/browser/sessions/tab_restore_service.h"
[email protected]92371eb2011-04-28 11:50:1528#include "chrome/browser/sessions/tab_restore_service_factory.h"
[email protected]f9bc9b92009-11-24 00:55:3529#include "chrome/browser/sync/profile_sync_service.h"
[email protected]40f047972009-11-25 03:54:4030#include "chrome/browser/sync/sync_ui_util.h"
31#include "chrome/browser/sync/sync_ui_util_mac.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"
[email protected]7d791652010-12-01 16:34:4941#import "chrome/browser/ui/cocoa/confirm_quit_panel_controller.h"
42#import "chrome/browser/ui/cocoa/encoding_menu_controller_delegate_mac.h"
43#import "chrome/browser/ui/cocoa/history_menu_bridge.h"
[email protected]cfe1d612011-01-19 20:06:4744#import "chrome/browser/ui/cocoa/tabs/tab_strip_controller.h"
45#import "chrome/browser/ui/cocoa/tabs/tab_window_controller.h"
[email protected]7d791652010-12-01 16:34:4946#include "chrome/browser/ui/cocoa/task_manager_mac.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]7324d1d02011-03-01 05:02:1652#include "content/browser/browser_thread.h"
53#include "content/browser/tab_contents/tab_contents.h"
[email protected]afd1e522011-04-27 23:29:5954#include "content/browser/user_metrics.h"
[email protected]f20d7332011-03-08 21:11:5355#include "content/common/notification_service.h"
[email protected]9dcb59662009-09-23 01:15:5056#include "grit/chromium_strings.h"
[email protected]09729a552009-08-03 23:21:4157#include "grit/generated_resources.h"
[email protected]caadfca2010-01-22 21:51:2158#include "net/base/net_util.h"
[email protected]c051a1b2011-01-21 23:30:1759#include "ui/base/l10n/l10n_util.h"
60#include "ui/base/l10n/l10n_util_mac.h"
[email protected]ddd653f42011-03-02 15:14:3861#include "ui/base/models/accelerator_cocoa.h"
[email protected]88d74942009-01-21 22:04:4462
[email protected]f2c20fa2009-12-01 17:42:0263// 10.6 adds a public API for the Spotlight-backed search menu item in the Help
64// menu. Provide the declaration so it can be called below when building with
65// the 10.5 SDK.
66#if !defined(MAC_OS_X_VERSION_10_6) || \
67 MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6
68@interface NSApplication (SnowLeopardSDKDeclarations)
[email protected]caadfca2010-01-22 21:51:2169- (void)setHelpMenu:(NSMenu*)helpMenu;
[email protected]f2c20fa2009-12-01 17:42:0270@end
71#endif
72
[email protected]d630d7d52010-02-21 00:55:1173namespace {
74
75// True while AppController is calling Browser::OpenEmptyWindow(). We need a
76// global flag here, analogue to BrowserInit::InProcessStartup() because
77// otherwise the SessionService will try to restore sessions when we make a new
78// window while there are no other active windows.
79bool g_is_opening_new_window = false;
80
81// Activates a browser window having the given profile (the last one active) if
82// possible and returns a pointer to the activate |Browser| or NULL if this was
83// not possible. If the last active browser is minimized (in particular, if
84// there are only minimized windows), it will unminimize it.
85Browser* ActivateBrowser(Profile* profile) {
86 Browser* browser = BrowserList::GetLastActiveWithProfile(profile);
87 if (browser)
88 browser->window()->Activate();
89 return browser;
90}
91
92// Creates an empty browser window with the given profile and returns a pointer
93// to the new |Browser|.
94Browser* CreateBrowser(Profile* profile) {
95 {
[email protected]0fbd70332010-06-01 19:28:3496 AutoReset<bool> auto_reset_in_run(&g_is_opening_new_window, true);
[email protected]d630d7d52010-02-21 00:55:1197 Browser::OpenEmptyWindow(profile);
98 }
99
100 Browser* browser = BrowserList::GetLastActive();
101 CHECK(browser);
102 return browser;
103}
104
105// Activates a browser window having the given profile (the last one active) if
106// possible or creates an empty one if necessary. Returns a pointer to the
107// activated/new |Browser|.
108Browser* ActivateOrCreateBrowser(Profile* profile) {
109 if (Browser* browser = ActivateBrowser(profile))
110 return browser;
111 return CreateBrowser(profile);
112}
113
[email protected]151c4a62011-04-22 04:15:13114CFStringRef BaseBundleID_CFString() {
115 NSString* base_bundle_id =
116 [NSString stringWithUTF8String:base::mac::BaseBundleID()];
117 return base::mac::NSToCFCast(base_bundle_id);
118}
119
[email protected]12dc3d42010-02-22 23:37:12120// This task synchronizes preferences (under "org.chromium.Chromium" or
121// "com.google.Chrome"), in particular, writes them out to disk.
122class PrefsSyncTask : public Task {
123 public:
124 PrefsSyncTask() {}
125 virtual ~PrefsSyncTask() {}
126 virtual void Run() {
[email protected]151c4a62011-04-22 04:15:13127 if (!CFPreferencesAppSynchronize(BaseBundleID_CFString()))
[email protected]12dc3d42010-02-22 23:37:12128 LOG(WARNING) << "Error recording application bundle path.";
129 }
130};
131
132// Record the location of the application bundle (containing the main framework)
133// from which Chromium was loaded. This is used by app mode shims to find
134// Chromium.
135void RecordLastRunAppBundlePath() {
136 // Going up three levels from |chrome::GetVersionedDirectory()| gives the
137 // real, user-visible app bundle directory. (The alternatives give either the
138 // framework's path or the initial app's path, which may be an app mode shim
139 // or a unit test.)
140 FilePath appBundlePath =
141 chrome::GetVersionedDirectory().DirName().DirName().DirName();
142 CFPreferencesSetAppValue(app_mode::kLastRunAppBundlePathPrefsKey,
143 base::SysUTF8ToCFStringRef(appBundlePath.value()),
[email protected]151c4a62011-04-22 04:15:13144 BaseBundleID_CFString());
[email protected]12dc3d42010-02-22 23:37:12145
146 // Sync after a delay avoid I/O contention on startup; 1500 ms is plenty.
[email protected]d04e7662010-10-10 22:24:48147 BrowserThread::PostDelayedTask(BrowserThread::FILE, FROM_HERE,
148 new PrefsSyncTask(), 1500);
[email protected]12dc3d42010-02-22 23:37:12149}
150
[email protected]d630d7d52010-02-21 00:55:11151} // anonymous namespace
152
[email protected]a99fce0e2011-03-21 20:58:48153@interface AppController (Private)
[email protected]88d74942009-01-21 22:04:44154- (void)initMenuState;
[email protected]a99fce0e2011-03-21 20:58:48155- (void)updateConfirmToQuitPrefMenuItem:(NSMenuItem*)item;
[email protected]eef99c22010-08-17 05:55:16156- (void)registerServicesMenuTypesTo:(NSApplication*)app;
[email protected]d630d7d52010-02-21 00:55:11157- (void)openUrls:(const std::vector<GURL>&)urls;
[email protected]57750f822009-04-21 21:43:09158- (void)getUrl:(NSAppleEventDescriptor*)event
159 withReply:(NSAppleEventDescriptor*)reply;
[email protected]449dd2f2009-05-27 13:04:00160- (void)windowLayeringDidChange:(NSNotification*)inNotification;
[email protected]dd6ab1282010-07-20 01:07:32161- (void)checkForAnyKeyWindows;
[email protected]73f5b362009-08-10 23:58:21162- (BOOL)userWillWaitForInProgressDownloads:(int)downloadCount;
[email protected]09729a552009-08-03 23:21:41163- (BOOL)shouldQuitWithInProgressDownloads;
[email protected]93b59fc2010-12-21 20:00:47164- (void)executeApplication:(id)sender;
[email protected]88d74942009-01-21 22:04:44165@end
[email protected]1bcdb532009-01-16 17:47:57166
167@implementation AppController
168
[email protected]caadfca2010-01-22 21:51:21169@synthesize startupComplete = startupComplete_;
170
[email protected]cd63ef62009-05-06 19:41:37171// This method is called very early in application startup (ie, before
172// the profile is loaded or any preferences have been registered). Defer any
173// user-data initialization until -applicationDidFinishLaunching:.
[email protected]88d74942009-01-21 22:04:44174- (void)awakeFromNib {
[email protected]136140c2009-05-19 13:58:25175 // We need to register the handlers early to catch events fired on launch.
[email protected]57750f822009-04-21 21:43:09176 NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager];
177 [em setEventHandler:self
178 andSelector:@selector(getUrl:withReply:)
179 forEventClass:kInternetEventClass
180 andEventID:kAEGetURL];
181 [em setEventHandler:self
182 andSelector:@selector(getUrl:withReply:)
183 forEventClass:'WWW!' // A particularly ancient AppleEvent that dates
184 andEventID:'OURL']; // back to the Spyglass days.
[email protected]cd63ef62009-05-06 19:41:37185
[email protected]449dd2f2009-05-27 13:04:00186 // Register for various window layering changes. We use these to update
187 // various UI elements (command-key equivalents, etc) when the frontmost
188 // window changes.
189 NSNotificationCenter* notificationCenter =
190 [NSNotificationCenter defaultCenter];
[email protected]ce560f82009-06-03 09:39:44191 [notificationCenter
[email protected]449dd2f2009-05-27 13:04:00192 addObserver:self
193 selector:@selector(windowLayeringDidChange:)
194 name:NSWindowDidBecomeKeyNotification
195 object:nil];
196 [notificationCenter
197 addObserver:self
198 selector:@selector(windowLayeringDidChange:)
199 name:NSWindowDidResignKeyNotification
200 object:nil];
201 [notificationCenter
202 addObserver:self
203 selector:@selector(windowLayeringDidChange:)
204 name:NSWindowDidBecomeMainNotification
205 object:nil];
206 [notificationCenter
207 addObserver:self
208 selector:@selector(windowLayeringDidChange:)
209 name:NSWindowDidResignMainNotification
210 object:nil];
211
[email protected]136140c2009-05-19 13:58:25212 // Set up the command updater for when there are no windows open
213 [self initMenuState];
[email protected]f4acfae82009-09-11 00:33:38214
215 // Activate (bring to foreground) if asked to do so. On
216 // Windows this logic isn't necessary since
217 // BrowserWindow::Activate() calls ::SetForegroundWindow() which is
218 // adequate. On Mac, BrowserWindow::Activate() calls -[NSWindow
219 // makeKeyAndOrderFront:] which does not activate the application
220 // itself.
221 const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess();
222 if (parsed_command_line.HasSwitch(switches::kActivateOnLaunch)) {
223 [NSApp activateIgnoringOtherApps:YES];
224 }
[email protected]136140c2009-05-19 13:58:25225}
226
[email protected]7dc8c6b2010-04-09 17:02:50227// (NSApplicationDelegate protocol) This is the Apple-approved place to override
228// the default handlers.
229- (void)applicationWillFinishLaunching:(NSNotification*)notification {
[email protected]58877932010-05-25 05:43:11230 // Nothing here right now.
[email protected]7dc8c6b2010-04-09 17:02:50231}
232
233- (BOOL)tryToTerminateApplication:(NSApplication*)app {
[email protected]3076fad2010-04-28 18:11:45234 // Check for in-process downloads, and prompt the user if they really want
235 // to quit (and thus cancel downloads). Only check if we're not already
236 // shutting down, else the user might be prompted multiple times if the
237 // download isn't stopped before terminate is called again.
238 if (!browser_shutdown::IsTryingToQuit() &&
239 ![self shouldQuitWithInProgressDownloads])
240 return NO;
241
[email protected]7dc8c6b2010-04-09 17:02:50242 // TODO(viettrungluu): Remove Apple Event handlers here? (It's safe to leave
243 // them in, but I'm not sure about UX; we'd also want to disable other things
244 // though.) http://crbug.com/40861
245
[email protected]177aceb2010-11-03 16:17:41246 // Check if the user really wants to quit by employing the confirm-to-quit
247 // mechanism.
248 if (!browser_shutdown::IsTryingToQuit() &&
249 [self applicationShouldTerminate:app] != NSTerminateNow)
250 return NO;
251
[email protected]c984d9f2010-07-20 20:52:20252 size_t num_browsers = BrowserList::size();
[email protected]7dc8c6b2010-04-09 17:02:50253
[email protected]e7dd6d82010-10-07 23:21:12254 // Give any print jobs in progress time to finish.
255 if (!browser_shutdown::IsTryingToQuit())
256 g_browser_process->print_job_manager()->StopJobs(true);
257
[email protected]c984d9f2010-07-20 20:52:20258 // Initiate a shutdown (via BrowserList::CloseAllBrowsers()) if we aren't
259 // already shutting down.
260 if (!browser_shutdown::IsTryingToQuit())
[email protected]c6032e82010-09-13 20:06:05261 BrowserList::CloseAllBrowsers();
[email protected]7dc8c6b2010-04-09 17:02:50262
[email protected]c984d9f2010-07-20 20:52:20263 return num_browsers == 0 ? YES : NO;
[email protected]7dc8c6b2010-04-09 17:02:50264}
265
266- (void)stopTryingToTerminateApplication:(NSApplication*)app {
267 if (browser_shutdown::IsTryingToQuit()) {
268 // Reset the "trying to quit" state, so that closing all browser windows
269 // will no longer lead to termination.
270 browser_shutdown::SetTryingToQuit(false);
271
272 // TODO(viettrungluu): Were we to remove Apple Event handlers above, we
273 // would have to reinstall them here. http://crbug.com/40861
274 }
[email protected]a9e8afc2009-08-11 22:03:17275}
276
[email protected]177aceb2010-11-03 16:17:41277- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)app {
[email protected]a99fce0e2011-03-21 20:58:48278 // Check if the preference is turned on.
279 const PrefService* prefs = [self defaultProfile]->GetPrefs();
[email protected]1eaddf32011-04-05 15:09:23280 if (!prefs->GetBoolean(prefs::kConfirmToQuitEnabled)) {
281 confirm_quit::RecordHistogram(confirm_quit::kNoConfirm);
[email protected]177aceb2010-11-03 16:17:41282 return NSTerminateNow;
[email protected]1eaddf32011-04-05 15:09:23283 }
[email protected]177aceb2010-11-03 16:17:41284
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 // This logic is only for keyboard-initiated quits.
[email protected]5df54fb2011-03-07 18:15:26290 if (![ConfirmQuitPanelController eventTriggersFeature:[app currentEvent]])
[email protected]2020fd0b2010-12-10 01:16:50291 return NSTerminateNow;
[email protected]177aceb2010-11-03 16:17:41292
[email protected]5df54fb2011-03-07 18:15:26293 return [[ConfirmQuitPanelController sharedController]
294 runModalLoopForApplication:app];
[email protected]177aceb2010-11-03 16:17:41295}
296
[email protected]449dd2f2009-05-27 13:04:00297// Called when the app is shutting down. Clean-up as appropriate.
[email protected]7dc8c6b2010-04-09 17:02:50298- (void)applicationWillTerminate:(NSNotification*)aNotification {
[email protected]3b6aa8b62009-09-15 21:36:11299 NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager];
300 [em removeEventHandlerForEventClass:kInternetEventClass
301 andEventID:kAEGetURL];
302 [em removeEventHandlerForEventClass:'WWW!'
303 andEventID:'OURL'];
[email protected]3b6aa8b62009-09-15 21:36:11304
[email protected]7dc8c6b2010-04-09 17:02:50305 // There better be no browser windows left at this point.
306 CHECK_EQ(BrowserList::size(), 0u);
[email protected]3b6aa8b62009-09-15 21:36:11307
[email protected]c984d9f2010-07-20 20:52:20308 // Tell BrowserList not to keep the browser process alive. Once all the
309 // browsers get dealloc'd, it will stop the RunLoop and fall back into main().
310 BrowserList::EndKeepAlive();
[email protected]fbc947b2009-06-19 13:28:24311
[email protected]d4e18652009-11-19 21:52:16312 // Close these off if they have open windows.
[email protected]d4e18652009-11-19 21:52:16313 [aboutController_ close];
314
[email protected]449dd2f2009-05-27 13:04:00315 [[NSNotificationCenter defaultCenter] removeObserver:self];
316}
317
[email protected]3b6aa8b62009-09-15 21:36:11318- (void)didEndMainMessageLoop {
319 DCHECK(!BrowserList::HasBrowserWithProfile([self defaultProfile]));
320 if (!BrowserList::HasBrowserWithProfile([self defaultProfile])) {
321 // As we're shutting down, we need to nuke the TabRestoreService, which
322 // will start the shutdown of the NavigationControllers and allow for
323 // proper shutdown. If we don't do this, Chrome won't shut down cleanly,
324 // and may end up crashing when some thread tries to use the IO thread (or
325 // another thread) that is no longer valid.
[email protected]92371eb2011-04-28 11:50:15326 TabRestoreServiceFactory::ResetForProfile([self defaultProfile]);
[email protected]3b6aa8b62009-09-15 21:36:11327 }
328}
329
[email protected]449dd2f2009-05-27 13:04:00330// Helper routine to get the window controller if the key window is a tabbed
331// window, or nil if not. Examples of non-tabbed windows are "about" or
332// "preferences".
333- (TabWindowController*)keyWindowTabController {
334 NSWindowController* keyWindowController =
[email protected]5dbabd02009-09-17 20:44:13335 [[NSApp keyWindow] windowController];
[email protected]449dd2f2009-05-27 13:04:00336 if ([keyWindowController isKindOfClass:[TabWindowController class]])
337 return (TabWindowController*)keyWindowController;
338
339 return nil;
340}
341
[email protected]74c0c642009-09-22 21:25:50342// Helper routine to get the window controller if the main window is a tabbed
343// window, or nil if not. Examples of non-tabbed windows are "about" or
344// "preferences".
345- (TabWindowController*)mainWindowTabController {
346 NSWindowController* mainWindowController =
347 [[NSApp mainWindow] windowController];
348 if ([mainWindowController isKindOfClass:[TabWindowController class]])
349 return (TabWindowController*)mainWindowController;
350
351 return nil;
352}
353
[email protected]8cfbdbd2011-06-16 04:17:17354// If the window has a tab controller, make "close window" be cmd-shift-w,
355// otherwise leave it as the normal cmd-w. Capitalization of the key equivalent
356// affects whether the shift modifer is used.
357- (void)adjustCloseWindowMenuItemKeyEquivalent:(BOOL)hasTabs {
358 [closeWindowMenuItem_ setKeyEquivalent:(hasTabs ? @"W" : @"w")];
359 [closeWindowMenuItem_ setKeyEquivalentModifierMask:NSCommandKeyMask];
360}
361
362// If the window has a tab controller, make "close tab" take over cmd-w,
363// otherwise it shouldn't have any key-equivalent because it should be disabled.
364- (void)adjustCloseTabMenuItemKeyEquivalent:(BOOL)hasTabs {
365 if (hasTabs) {
366 [closeTabMenuItem_ setKeyEquivalent:@"w"];
367 [closeTabMenuItem_ setKeyEquivalentModifierMask:NSCommandKeyMask];
368 } else {
369 [closeTabMenuItem_ setKeyEquivalent:@""];
370 [closeTabMenuItem_ setKeyEquivalentModifierMask:0];
371 }
372}
373
374// Explicitly remove any command-key equivalents from the close tab/window
375// menus so that nothing can go haywire if we get a user action during pending
376// updates.
377- (void)clearCloseMenuItemKeyEquivalents {
378 [closeTabMenuItem_ setKeyEquivalent:@""];
379 [closeTabMenuItem_ setKeyEquivalentModifierMask:0];
380 [closeWindowMenuItem_ setKeyEquivalent:@""];
381 [closeWindowMenuItem_ setKeyEquivalentModifierMask:0];
382}
383
384// See if we have a window with tabs open, and adjust the key equivalents for
385// Close Tab/Close Window accordingly.
386- (void)fixCloseMenuItemKeyEquivalents:(NSWindow*)window {
387 fileMenuUpdatePending_ = NO;
388 TabWindowController* tabController = [self keyWindowTabController];
389 if (!tabController && ![NSApp keyWindow]) {
390 // There might be a small amount of time where there is no key window,
391 // so just use our main browser window if there is one.
392 tabController = [self mainWindowTabController];
393 }
394 BOOL hasTabs = !!tabController;
395
396 [self adjustCloseWindowMenuItemKeyEquivalent:hasTabs];
397 [self adjustCloseTabMenuItemKeyEquivalent:hasTabs];
398}
399
400// Fix up the "close tab/close window" command-key equivalents. We do this
401// after a delay to ensure that window layer state has been set by the time
402// we do the enabling. This should only be called on the main thread, code that
403// calls this (even as a side-effect) from other threads needs to be fixed.
404- (void)delayedFixCloseMenuItemKeyEquivalents:(NSNotification*)notify {
405 DCHECK([NSThread isMainThread]);
406 if (!fileMenuUpdatePending_) {
407 // The OS prefers keypresses to timers, so it's possible that a cmd-w
408 // can sneak in before this timer fires. In order to prevent that from
409 // having any bad consequences, just clear the keys combos altogether. They
410 // will be reset when the timer eventually fires.
411 if ([NSThread isMainThread]) {
412 fileMenuUpdatePending_ = YES;
413 [self clearCloseMenuItemKeyEquivalents];
414 [self performSelector:@selector(fixCloseMenuItemKeyEquivalents:)
415 withObject:[notify object]
416 afterDelay:0];
417 } else {
418 // This shouldn't be happening, but if it does, force it to the main
419 // thread to avoid dropping the update. Don't mess with
420 // |fileMenuUpdatePending_| as it's not expected to be threadsafe and
421 // there could be a race between the selector finishing and setting the
422 // flag.
423 [self
424 performSelectorOnMainThread:@selector(fixCloseMenuItemKeyEquivalents:)
425 withObject:[notify object]
426 waitUntilDone:NO];
427 }
428 }
429}
430
[email protected]449dd2f2009-05-27 13:04:00431// Called when we get a notification about the window layering changing to
432// update the UI based on the new main window.
433- (void)windowLayeringDidChange:(NSNotification*)notify {
[email protected]8cfbdbd2011-06-16 04:17:17434 [self delayedFixCloseMenuItemKeyEquivalents:notify];
435
[email protected]dd6ab1282010-07-20 01:07:32436 if ([notify name] == NSWindowDidResignKeyNotification) {
437 // If a window is closed, this notification is fired but |[NSApp keyWindow]|
438 // returns nil regardless of whether any suitable candidates for the key
439 // window remain. It seems that the new key window for the app is not set
440 // until after this notification is fired, so a check is performed after the
441 // run loop is allowed to spin.
442 [self performSelector:@selector(checkForAnyKeyWindows)
443 withObject:nil
444 afterDelay:0.0];
445 }
446}
447
448- (void)checkForAnyKeyWindows {
449 if ([NSApp keyWindow])
450 return;
451
452 NotificationService::current()->Notify(
453 NotificationType::NO_KEY_WINDOW,
454 NotificationService::AllSources(),
455 NotificationService::NoDetails());
[email protected]449dd2f2009-05-27 13:04:00456}
457
[email protected]414fde592009-05-21 16:14:43458// If the auto-update interval is not set, make it 5 hours.
459// This code is specific to Mac Chrome Dev Channel.
460// Placed here for 2 reasons:
461// 1) Same spot as other Pref stuff
462// 2) Try and be friendly by keeping this after app launch
463// TODO(jrg): remove once we go Beta.
464- (void)setUpdateCheckInterval {
465#if defined(GOOGLE_CHROME_BUILD)
466 CFStringRef app = (CFStringRef)@"com.google.Keystone.Agent";
467 CFStringRef checkInterval = (CFStringRef)@"checkInterval";
468 CFPropertyListRef plist = CFPreferencesCopyAppValue(checkInterval, app);
469 if (!plist) {
470 const float fiveHoursInSeconds = 5.0 * 60.0 * 60.0;
[email protected]7dc8c6b2010-04-09 17:02:50471 NSNumber* value = [NSNumber numberWithFloat:fiveHoursInSeconds];
[email protected]414fde592009-05-21 16:14:43472 CFPreferencesSetAppValue(checkInterval, value, app);
473 CFPreferencesAppSynchronize(app);
474 }
475#endif
476}
477
[email protected]136140c2009-05-19 13:58:25478// This is called after profiles have been loaded and preferences registered.
479// It is safe to access the default profile here.
480- (void)applicationDidFinishLaunching:(NSNotification*)notify {
[email protected]c984d9f2010-07-20 20:52:20481 // Notify BrowserList to keep the application running so it doesn't go away
482 // when all the browser windows get closed.
483 BrowserList::StartKeepAlive();
[email protected]136140c2009-05-19 13:58:25484
[email protected]e5575022009-08-22 00:31:22485 bookmarkMenuBridge_.reset(new BookmarkMenuBridge([self defaultProfile]));
[email protected]1c84c82c2009-07-27 15:37:25486 historyMenuBridge_.reset(new HistoryMenuBridge([self defaultProfile]));
[email protected]cd63ef62009-05-06 19:41:37487
[email protected]414fde592009-05-21 16:14:43488 [self setUpdateCheckInterval];
[email protected]2bcec612009-05-14 17:50:53489
490 // Build up the encoding menu, the order of the items differs based on the
491 // current locale (see http://crbug.com/7647 for details).
492 // We need a valid g_browser_process to get the profile which is why we can't
493 // call this from awakeFromNib.
[email protected]6f91e5ae2011-03-21 19:53:14494 NSMenu* viewMenu = [[[NSApp mainMenu] itemWithTag:IDC_VIEW_MENU] submenu];
495 NSMenuItem* encodingMenuItem = [viewMenu itemWithTag:IDC_ENCODING_MENU];
496 NSMenu* encodingMenu = [encodingMenuItem submenu];
[email protected]fc349322009-09-23 22:28:23497 EncodingMenuControllerDelegate::BuildEncodingMenu([self defaultProfile],
[email protected]6f91e5ae2011-03-21 19:53:14498 encodingMenu);
[email protected]2bcec612009-05-14 17:50:53499
[email protected]f2c20fa2009-12-01 17:42:02500 // Since Chrome is localized to more languages than the OS, tell Cocoa which
501 // menu is the Help so it can add the search item to it.
[email protected]caadfca2010-01-22 21:51:21502 if (helpMenu_ && [NSApp respondsToSelector:@selector(setHelpMenu:)])
[email protected]f2c20fa2009-12-01 17:42:02503 [NSApp setHelpMenu:helpMenu_];
[email protected]f2c20fa2009-12-01 17:42:02504
[email protected]12dc3d42010-02-22 23:37:12505 // Record the path to the (browser) app bundle; this is used by the app mode
506 // shim.
507 RecordLastRunAppBundlePath();
508
[email protected]eef99c22010-08-17 05:55:16509 // Makes "Services" menu items available.
510 [self registerServicesMenuTypesTo:[notify object]];
511
[email protected]caadfca2010-01-22 21:51:21512 startupComplete_ = YES;
513
514 // TODO(viettrungluu): This is very temporary, since this should be done "in"
515 // |BrowserMain()|, i.e., this list of startup URLs should be appended to the
516 // (probably-empty) list of URLs from the command line.
[email protected]d630d7d52010-02-21 00:55:11517 if (startupUrls_.size()) {
518 [self openUrls:startupUrls_];
519 [self clearStartupUrls];
[email protected]caadfca2010-01-22 21:51:21520 }
[email protected]aac169d2011-03-18 19:53:03521
522 const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess();
523 if (!parsed_command_line.HasSwitch(switches::kEnableExposeForTabs)) {
524 [tabposeMenuItem_ setHidden:YES];
525 }
[email protected]7c321082009-02-09 15:35:47526}
527
[email protected]a96ec6a2009-11-04 17:27:08528// This is called after profiles have been loaded and preferences registered.
529// It is safe to access the default profile here.
530- (void)applicationDidBecomeActive:(NSNotification*)notify {
531 NotificationService::current()->Notify(NotificationType::APP_ACTIVATED,
532 NotificationService::AllSources(),
533 NotificationService::NoDetails());
534}
535
[email protected]73f5b362009-08-10 23:58:21536// Helper function for populating and displaying the in progress downloads at
537// exit alert panel.
538- (BOOL)userWillWaitForInProgressDownloads:(int)downloadCount {
[email protected]e8b5f7882009-09-14 20:59:13539 NSString* warningText = nil;
540 NSString* explanationText = nil;
[email protected]73f5b362009-08-10 23:58:21541 NSString* waitTitle = nil;
542 NSString* exitTitle = nil;
543
[email protected]757127052010-04-27 14:42:26544 string16 product_name = l10n_util::GetStringUTF16(IDS_PRODUCT_NAME);
[email protected]9dcb59662009-09-23 01:15:50545
[email protected]73f5b362009-08-10 23:58:21546 // Set the dialog text based on whether or not there are multiple downloads.
547 if (downloadCount == 1) {
[email protected]e8b5f7882009-09-14 20:59:13548 // Dialog text: warning and explanation.
[email protected]757127052010-04-27 14:42:26549 warningText = l10n_util::GetNSStringF(
550 IDS_SINGLE_DOWNLOAD_REMOVE_CONFIRM_WARNING, product_name);
551 explanationText = l10n_util::GetNSStringF(
552 IDS_SINGLE_DOWNLOAD_REMOVE_CONFIRM_EXPLANATION, product_name);
[email protected]73f5b362009-08-10 23:58:21553
554 // Cancel download and exit button text.
[email protected]757127052010-04-27 14:42:26555 exitTitle = l10n_util::GetNSString(
556 IDS_SINGLE_DOWNLOAD_REMOVE_CONFIRM_OK_BUTTON_LABEL);
[email protected]73f5b362009-08-10 23:58:21557
558 // Wait for download button text.
[email protected]757127052010-04-27 14:42:26559 waitTitle = l10n_util::GetNSString(
560 IDS_SINGLE_DOWNLOAD_REMOVE_CONFIRM_CANCEL_BUTTON_LABEL);
[email protected]73f5b362009-08-10 23:58:21561 } else {
[email protected]e8b5f7882009-09-14 20:59:13562 // Dialog text: warning and explanation.
[email protected]757127052010-04-27 14:42:26563 warningText = l10n_util::GetNSStringF(
564 IDS_MULTIPLE_DOWNLOADS_REMOVE_CONFIRM_WARNING, product_name,
[email protected]528c56d2010-07-30 19:28:44565 base::IntToString16(downloadCount));
[email protected]757127052010-04-27 14:42:26566 explanationText = l10n_util::GetNSStringF(
567 IDS_MULTIPLE_DOWNLOADS_REMOVE_CONFIRM_EXPLANATION, product_name);
[email protected]73f5b362009-08-10 23:58:21568
569 // Cancel downloads and exit button text.
[email protected]757127052010-04-27 14:42:26570 exitTitle = l10n_util::GetNSString(
571 IDS_MULTIPLE_DOWNLOADS_REMOVE_CONFIRM_OK_BUTTON_LABEL);
[email protected]73f5b362009-08-10 23:58:21572
573 // Wait for downloads button text.
[email protected]757127052010-04-27 14:42:26574 waitTitle = l10n_util::GetNSString(
575 IDS_MULTIPLE_DOWNLOADS_REMOVE_CONFIRM_CANCEL_BUTTON_LABEL);
[email protected]73f5b362009-08-10 23:58:21576 }
577
578 // 'waitButton' is the default choice.
[email protected]e8b5f7882009-09-14 20:59:13579 int choice = NSRunAlertPanel(warningText, explanationText,
580 waitTitle, exitTitle, nil);
[email protected]73f5b362009-08-10 23:58:21581 return choice == NSAlertDefaultReturn ? YES : NO;
582}
583
584// Check all profiles for in progress downloads, and if we find any, prompt the
[email protected]09729a552009-08-03 23:21:41585// user to see if we should continue to exit (and thus cancel the downloads), or
586// if we should wait.
587- (BOOL)shouldQuitWithInProgressDownloads {
[email protected]73f5b362009-08-10 23:58:21588 ProfileManager* profile_manager = g_browser_process->profile_manager();
589 if (!profile_manager)
590 return YES;
[email protected]09729a552009-08-03 23:21:41591
[email protected]844a1002011-04-19 11:37:21592 std::vector<Profile*> profiles(profile_manager->GetLoadedProfiles());
593 for (size_t i = 0; i < profiles.size(); ++i) {
594 DownloadManager* download_manager = profiles[i]->GetDownloadManager();
[email protected]73f5b362009-08-10 23:58:21595 if (download_manager && download_manager->in_progress_count() > 0) {
596 int downloadCount = download_manager->in_progress_count();
597 if ([self userWillWaitForInProgressDownloads:downloadCount]) {
598 // Create a new browser window (if necessary) and navigate to the
599 // downloads page if the user chooses to wait.
[email protected]844a1002011-04-19 11:37:21600 Browser* browser = BrowserList::FindBrowserWithProfile(profiles[i]);
[email protected]73f5b362009-08-10 23:58:21601 if (!browser) {
[email protected]844a1002011-04-19 11:37:21602 browser = Browser::Create(profiles[i]);
[email protected]73f5b362009-08-10 23:58:21603 browser->window()->Show();
604 }
605 DCHECK(browser);
606 browser->ShowDownloadsTab();
607 return NO;
608 }
[email protected]09729a552009-08-03 23:21:41609
[email protected]73f5b362009-08-10 23:58:21610 // User wants to exit.
611 return YES;
[email protected]09729a552009-08-03 23:21:41612 }
[email protected]09729a552009-08-03 23:21:41613 }
614
[email protected]73f5b362009-08-10 23:58:21615 // No profiles or active downloads found, okay to exit.
[email protected]09729a552009-08-03 23:21:41616 return YES;
617}
618
[email protected]fbc947b2009-06-19 13:28:24619// Called to determine if we should enable the "restore tab" menu item.
620// Checks with the TabRestoreService to see if there's anything there to
621// restore and returns YES if so.
622- (BOOL)canRestoreTab {
[email protected]92371eb2011-04-28 11:50:15623 TabRestoreService* service =
624 TabRestoreServiceFactory::GetForProfile([self defaultProfile]);
[email protected]fbc947b2009-06-19 13:28:24625 return service && !service->entries().empty();
626}
627
[email protected]f6c15fa2010-06-04 20:08:21628// Returns true if there is not a modal window (either window- or application-
629// modal) blocking the active browser. Note that tab modal dialogs (HTTP auth
630// sheets) will not count as blocking the browser. But things like open/save
631// dialogs that are window modal will block the browser.
632- (BOOL)keyWindowIsNotModal {
[email protected]6a281332009-10-13 17:41:06633 Browser* browser = BrowserList::GetLastActive();
[email protected]dc76e8362010-06-11 16:29:52634 return [NSApp modalWindow] == nil && (!browser ||
[email protected]6a281332009-10-13 17:41:06635 ![[browser->window()->GetNativeHandle() attachedSheet]
[email protected]f6c15fa2010-06-04 20:08:21636 isKindOfClass:[NSWindow class]]);
[email protected]6a281332009-10-13 17:41:06637}
638
[email protected]88d74942009-01-21 22:04:44639// Called to validate menu items when there are no key windows. All the
640// items we care about have been set with the |commandDispatch:| action and
641// a target of FirstResponder in IB. If it's not one of those, let it
642// continue up the responder chain to be handled elsewhere. We pull out the
643// tag as the cross-platform constant to differentiate and dispatch the
644// various commands.
645- (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item {
646 SEL action = [item action];
647 BOOL enable = NO;
648 if (action == @selector(commandDispatch:)) {
649 NSInteger tag = [item tag];
[email protected]fbc947b2009-06-19 13:28:24650 if (menuState_->SupportsCommand(tag)) {
651 switch (tag) {
[email protected]419eb2a2009-11-11 16:26:26652 // The File Menu commands are not automatically disabled by Cocoa when a
653 // dialog sheet obscures the browser window, so we disable several of
654 // them here. We don't need to include IDC_CLOSE_WINDOW, because
655 // app_controller is only activated when there are no key windows (see
656 // function comment).
[email protected]fbc947b2009-06-19 13:28:24657 case IDC_RESTORE_TAB:
[email protected]f6c15fa2010-06-04 20:08:21658 enable = [self keyWindowIsNotModal] && [self canRestoreTab];
[email protected]6a281332009-10-13 17:41:06659 break;
[email protected]f6c15fa2010-06-04 20:08:21660 // Browser-level items that open in new tabs should not open if there's
661 // a window- or app-modal dialog.
[email protected]6a281332009-10-13 17:41:06662 case IDC_OPEN_FILE:
[email protected]6a281332009-10-13 17:41:06663 case IDC_NEW_TAB:
[email protected]f6c15fa2010-06-04 20:08:21664 case IDC_SHOW_HISTORY:
665 case IDC_SHOW_BOOKMARK_MANAGER:
666 enable = [self keyWindowIsNotModal];
667 break;
668 // Browser-level items that open in new windows.
669 case IDC_NEW_WINDOW:
670 case IDC_TASK_MANAGER:
671 // Allow the user to open a new window if there's a window-modal
672 // dialog.
673 enable = [self keyWindowIsNotModal] || ([NSApp modalWindow] == nil);
[email protected]fbc947b2009-06-19 13:28:24674 break;
[email protected]446e16a2010-05-03 21:23:58675 case IDC_SYNC_BOOKMARKS: {
676 Profile* defaultProfile = [self defaultProfile];
677 // The profile may be NULL during shutdown -- see
678 // http://code.google.com/p/chromium/issues/detail?id=43048 .
679 //
680 // TODO(akalin,viettrungluu): Figure out whether this method
681 // can be prevented from being called if defaultProfile is
682 // NULL.
683 if (!defaultProfile) {
684 LOG(WARNING)
685 << "NULL defaultProfile detected -- not doing anything";
686 break;
687 }
[email protected]d3b98c82010-07-14 07:45:59688 enable = defaultProfile->IsSyncAccessible() &&
689 [self keyWindowIsNotModal];
[email protected]446e16a2010-05-03 21:23:58690 sync_ui_util::UpdateSyncItem(item, enable, defaultProfile);
[email protected]f9bc9b92009-11-24 00:55:35691 break;
[email protected]446e16a2010-05-03 21:23:58692 }
[email protected]fbc947b2009-06-19 13:28:24693 default:
[email protected]f6c15fa2010-06-04 20:08:21694 enable = menuState_->IsCommandEnabled(tag) ?
695 [self keyWindowIsNotModal] : NO;
[email protected]fbc947b2009-06-19 13:28:24696 }
697 }
[email protected]3b6aa8b62009-09-15 21:36:11698 } else if (action == @selector(terminate:)) {
[email protected]88d74942009-01-21 22:04:44699 enable = YES;
[email protected]3111f08b2009-04-30 16:01:52700 } else if (action == @selector(showPreferences:)) {
701 enable = YES;
[email protected]bde3dda2009-05-20 22:13:07702 } else if (action == @selector(orderFrontStandardAboutPanel:)) {
703 enable = YES;
[email protected]93b59fc2010-12-21 20:00:47704 } else if (action == @selector(commandFromDock:)) {
[email protected]3d4bd23f2009-10-06 03:58:38705 enable = YES;
[email protected]a99fce0e2011-03-21 20:58:48706 } else if (action == @selector(toggleConfirmToQuit:)) {
707 [self updateConfirmToQuitPrefMenuItem:static_cast<NSMenuItem*>(item)];
708 enable = YES;
[email protected]88d74942009-01-21 22:04:44709 }
710 return enable;
711}
712
[email protected]947fc0d2010-01-14 22:33:05713// Called when the user picks a menu item when there are no key windows, or when
714// there is no foreground browser window. Calls through to the browser object to
715// execute the command. This assumes that the command is supported and doesn't
[email protected]d630d7d52010-02-21 00:55:11716// check, otherwise it should have been disabled in the UI in
717// |-validateUserInterfaceItem:|.
[email protected]88d74942009-01-21 22:04:44718- (void)commandDispatch:(id)sender {
[email protected]93e181762009-05-29 14:30:38719 Profile* defaultProfile = [self defaultProfile];
[email protected]f0a51fb52009-03-05 12:46:38720
[email protected]947fc0d2010-01-14 22:33:05721 // Handle the case where we're dispatching a command from a sender that's in a
722 // browser window. This means that the command came from a background window
723 // and is getting here because the foreground window is not a browser window.
724 if ([sender respondsToSelector:@selector(window)]) {
725 id delegate = [[sender window] windowController];
726 if ([delegate isKindOfClass:[BrowserWindowController class]]) {
727 [delegate commandDispatch:sender];
728 return;
729 }
730 }
731
[email protected]88d74942009-01-21 22:04:44732 NSInteger tag = [sender tag];
733 switch (tag) {
[email protected]fbc947b2009-06-19 13:28:24734 case IDC_NEW_TAB:
[email protected]d630d7d52010-02-21 00:55:11735 // Create a new tab in an existing browser window (which we activate) if
736 // possible.
737 if (Browser* browser = ActivateBrowser(defaultProfile)) {
738 browser->ExecuteCommand(IDC_NEW_TAB);
739 break;
740 }
741 // Else fall through to create new window.
[email protected]88d74942009-01-21 22:04:44742 case IDC_NEW_WINDOW:
[email protected]d630d7d52010-02-21 00:55:11743 CreateBrowser(defaultProfile);
744 break;
[email protected]91a5b3e2009-10-30 19:32:22745 case IDC_FOCUS_LOCATION:
[email protected]d630d7d52010-02-21 00:55:11746 ActivateOrCreateBrowser(defaultProfile)->
747 ExecuteCommand(IDC_FOCUS_LOCATION);
[email protected]88d74942009-01-21 22:04:44748 break;
[email protected]cddc5242009-12-07 18:54:33749 case IDC_FOCUS_SEARCH:
[email protected]d630d7d52010-02-21 00:55:11750 ActivateOrCreateBrowser(defaultProfile)->ExecuteCommand(IDC_FOCUS_SEARCH);
[email protected]cddc5242009-12-07 18:54:33751 break;
[email protected]863ff662009-01-26 20:18:18752 case IDC_NEW_INCOGNITO_WINDOW:
[email protected]e9e211272009-09-22 16:39:11753 Browser::OpenEmptyWindow(defaultProfile->GetOffTheRecordProfile());
[email protected]863ff662009-01-26 20:18:18754 break;
[email protected]fbc947b2009-06-19 13:28:24755 case IDC_RESTORE_TAB:
756 Browser::OpenWindowWithRestoredTabs(defaultProfile);
757 break;
[email protected]e19516d2009-04-28 17:15:19758 case IDC_OPEN_FILE:
[email protected]d630d7d52010-02-21 00:55:11759 CreateBrowser(defaultProfile)->ExecuteCommand(IDC_OPEN_FILE);
[email protected]e19516d2009-04-28 17:15:19760 break;
[email protected]1fdff702009-10-22 00:36:18761 case IDC_CLEAR_BROWSING_DATA: {
[email protected]93e181762009-05-29 14:30:38762 // There may not be a browser open, so use the default profile.
[email protected]445177652011-03-09 02:04:05763 if (Browser* browser = ActivateBrowser(defaultProfile)) {
764 browser->OpenClearBrowsingDataDialog();
[email protected]c8de64a2011-01-25 17:10:23765 } else {
[email protected]445177652011-03-09 02:04:05766 Browser::OpenClearBrowingDataDialogWindow(defaultProfile);
[email protected]c8de64a2011-01-25 17:10:23767 }
[email protected]93e181762009-05-29 14:30:38768 break;
[email protected]1fdff702009-10-22 00:36:18769 }
[email protected]4aee5652009-11-19 18:59:21770 case IDC_IMPORT_SETTINGS: {
[email protected]445177652011-03-09 02:04:05771 if (Browser* browser = ActivateBrowser(defaultProfile)) {
772 browser->OpenImportSettingsDialog();
[email protected]c8de64a2011-01-25 17:10:23773 } else {
[email protected]445177652011-03-09 02:04:05774 Browser::OpenImportSettingsDialogWindow(defaultProfile);
[email protected]c8de64a2011-01-25 17:10:23775 }
[email protected]4aee5652009-11-19 18:59:21776 break;
777 }
[email protected]af7c25e2010-01-23 05:46:45778 case IDC_SHOW_BOOKMARK_MANAGER:
[email protected]afd1e522011-04-27 23:29:59779 UserMetrics::RecordAction(UserMetricsAction("ShowBookmarkManager"));
[email protected]90a8bf252010-03-12 00:09:47780 if (Browser* browser = ActivateBrowser(defaultProfile)) {
[email protected]47e532b2010-04-23 17:46:40781 // Open a bookmark manager tab.
[email protected]90a8bf252010-03-12 00:09:47782 browser->OpenBookmarkManager();
783 } else {
[email protected]47e532b2010-04-23 17:46:40784 // No browser window, so create one for the bookmark manager tab.
785 Browser::OpenBookmarkManagerWindow(defaultProfile);
[email protected]90a8bf252010-03-12 00:09:47786 }
[email protected]af7c25e2010-01-23 05:46:45787 break;
[email protected]91a5b3e2009-10-30 19:32:22788 case IDC_SHOW_HISTORY:
[email protected]d630d7d52010-02-21 00:55:11789 if (Browser* browser = ActivateBrowser(defaultProfile))
790 browser->ShowHistoryTab();
791 else
792 Browser::OpenHistoryWindow(defaultProfile);
[email protected]91a5b3e2009-10-30 19:32:22793 break;
794 case IDC_SHOW_DOWNLOADS:
[email protected]d630d7d52010-02-21 00:55:11795 if (Browser* browser = ActivateBrowser(defaultProfile))
796 browser->ShowDownloadsTab();
797 else
798 Browser::OpenDownloadsWindow(defaultProfile);
[email protected]91a5b3e2009-10-30 19:32:22799 break;
[email protected]bb37e6ec2009-11-18 17:59:36800 case IDC_MANAGE_EXTENSIONS:
[email protected]d630d7d52010-02-21 00:55:11801 if (Browser* browser = ActivateBrowser(defaultProfile))
802 browser->ShowExtensionsTab();
803 else
804 Browser::OpenExtensionsWindow(defaultProfile);
[email protected]bb37e6ec2009-11-18 17:59:36805 break;
[email protected]1fdff702009-10-22 00:36:18806 case IDC_HELP_PAGE:
[email protected]d630d7d52010-02-21 00:55:11807 if (Browser* browser = ActivateBrowser(defaultProfile))
808 browser->OpenHelpTab();
809 else
810 Browser::OpenHelpWindow(defaultProfile);
[email protected]1fdff702009-10-22 00:36:18811 break;
[email protected]1c41a932010-11-22 23:41:23812 case IDC_FEEDBACK: {
[email protected]3896b182009-11-03 16:15:32813 Browser* browser = BrowserList::GetLastActive();
[email protected]d630d7d52010-02-21 00:55:11814 TabContents* currentTab =
815 browser ? browser->GetSelectedTabContents() : NULL;
[email protected]3896b182009-11-03 16:15:32816 BugReportWindowController* controller =
817 [[BugReportWindowController alloc]
[email protected]d630d7d52010-02-21 00:55:11818 initWithTabContents:currentTab
819 profile:[self defaultProfile]];
[email protected]3896b182009-11-03 16:15:32820 [controller runModalDialog];
821 break;
822 }
[email protected]f9bc9b92009-11-24 00:55:35823 case IDC_SYNC_BOOKMARKS:
[email protected]446e16a2010-05-03 21:23:58824 // The profile may be NULL during shutdown -- see
825 // http://code.google.com/p/chromium/issues/detail?id=43048 .
826 //
827 // TODO(akalin,viettrungluu): Figure out whether this method can
828 // be prevented from being called if defaultProfile is NULL.
829 if (!defaultProfile) {
830 LOG(WARNING) << "NULL defaultProfile detected -- not doing anything";
831 break;
832 }
[email protected]f9bc9b92009-11-24 00:55:35833 // TODO(akalin): Add a constant to denote starting sync from the
834 // main menu and use that instead of START_FROM_WRENCH.
[email protected]40f047972009-11-25 03:54:40835 sync_ui_util::OpenSyncMyBookmarksDialog(
[email protected]c8de64a2011-01-25 17:10:23836 defaultProfile, ActivateBrowser(defaultProfile),
837 ProfileSyncService::START_FROM_WRENCH);
[email protected]f9bc9b92009-11-24 00:55:35838 break;
[email protected]ceaaf1812010-01-20 22:13:09839 case IDC_TASK_MANAGER:
[email protected]afd1e522011-04-27 23:29:59840 UserMetrics::RecordAction(UserMetricsAction("TaskManager"));
[email protected]adb6a84d2011-02-07 16:58:40841 TaskManagerMac::Show(false);
[email protected]ceaaf1812010-01-20 22:13:09842 break;
[email protected]4a42d272010-06-18 01:29:42843 case IDC_OPTIONS:
844 [self showPreferences:sender];
845 break;
[email protected]93b59fc2010-12-21 20:00:47846 default:
847 // Background Applications use dynamic values that must be less than the
848 // smallest value among the predefined IDC_* labels.
849 if ([sender tag] < IDC_MinimumLabelValue)
850 [self executeApplication:sender];
851 break;
[email protected]d630d7d52010-02-21 00:55:11852 }
[email protected]88d74942009-01-21 22:04:44853}
854
[email protected]93b59fc2010-12-21 20:00:47855// Run a (background) application in a new tab.
856- (void)executeApplication:(id)sender {
857 NSInteger tag = [sender tag];
858 Profile* profile = [self defaultProfile];
859 DCHECK(profile);
860 BackgroundApplicationListModel applications(profile);
861 DCHECK(tag >= 0 &&
862 tag < static_cast<int>(applications.size()));
863 Browser* browser = BrowserList::GetLastActive();
864 if (!browser) {
865 Browser::OpenEmptyWindow(profile);
866 browser = BrowserList::GetLastActive();
867 }
868 const Extension* extension = applications.GetExtension(tag);
[email protected]7e41c2b2011-05-06 10:31:16869 browser->OpenApplicationTab(profile, extension, NEW_FOREGROUND_TAB);
[email protected]93b59fc2010-12-21 20:00:47870}
871
[email protected]947fc0d2010-01-14 22:33:05872// Same as |-commandDispatch:|, but executes commands using a disposition
873// determined by the key flags. This will get called in the case where the
874// frontmost window is not a browser window, and the user has command-clicked
875// a button in a background browser window whose action is
876// |-commandDispatchUsingKeyModifiers:|
877- (void)commandDispatchUsingKeyModifiers:(id)sender {
878 DCHECK(sender);
879 if ([sender respondsToSelector:@selector(window)]) {
880 id delegate = [[sender window] windowController];
881 if ([delegate isKindOfClass:[BrowserWindowController class]]) {
882 [delegate commandDispatchUsingKeyModifiers:sender];
883 }
884 }
885}
886
[email protected]ff81e0a2009-04-13 14:58:54887// NSApplication delegate method called when someone clicks on the
888// dock icon and there are no open windows. To match standard mac
889// behavior, we should open a new window.
890- (BOOL)applicationShouldHandleReopen:(NSApplication*)theApplication
891 hasVisibleWindows:(BOOL)flag {
[email protected]70631402010-04-26 20:16:54892 // If the browser is currently trying to quit, don't do anything and return NO
893 // to prevent AppKit from doing anything.
894 // TODO(rohitrao): Remove this code when http://crbug.com/40861 is resolved.
895 if (browser_shutdown::IsTryingToQuit())
896 return NO;
897
[email protected]ff81e0a2009-04-13 14:58:54898 // Don't do anything if there are visible windows. This will cause
899 // AppKit to unminimize the most recently minimized window.
900 if (flag)
901 return YES;
902
[email protected]0d1210d62010-09-07 17:31:38903 // If launched as a hidden login item (due to installation of a persistent app
904 // or by the user, for example in System Preferenecs->Accounts->Login Items),
905 // allow session to be restored first time the user clicks on a Dock icon.
906 // Normally, it'd just open a new empty page.
907 {
908 static BOOL doneOnce = NO;
909 if (!doneOnce) {
910 doneOnce = YES;
[email protected]0378bf42011-01-01 18:20:14911 if (base::mac::WasLaunchedAsHiddenLoginItem()) {
[email protected]0d1210d62010-09-07 17:31:38912 SessionService* sessionService =
[email protected]92371eb2011-04-28 11:50:15913 SessionServiceFactory::GetForProfile([self defaultProfile]);
[email protected]0d1210d62010-09-07 17:31:38914 if (sessionService &&
915 sessionService->RestoreIfNecessary(std::vector<GURL>()))
916 return NO;
917 }
918 }
919 }
[email protected]ff81e0a2009-04-13 14:58:54920 // Otherwise open a new window.
[email protected]d630d7d52010-02-21 00:55:11921 {
[email protected]0fbd70332010-06-01 19:28:34922 AutoReset<bool> auto_reset_in_run(&g_is_opening_new_window, true);
[email protected]d630d7d52010-02-21 00:55:11923 Browser::OpenEmptyWindow([self defaultProfile]);
924 }
[email protected]ff81e0a2009-04-13 14:58:54925
926 // We've handled the reopen event, so return NO to tell AppKit not
927 // to do anything.
928 return NO;
929}
930
[email protected]88d74942009-01-21 22:04:44931- (void)initMenuState {
[email protected]3111f08b2009-04-30 16:01:52932 menuState_.reset(new CommandUpdater(NULL));
[email protected]fbc947b2009-06-19 13:28:24933 menuState_->UpdateCommandEnabled(IDC_NEW_TAB, true);
[email protected]88d74942009-01-21 22:04:44934 menuState_->UpdateCommandEnabled(IDC_NEW_WINDOW, true);
[email protected]863ff662009-01-26 20:18:18935 menuState_->UpdateCommandEnabled(IDC_NEW_INCOGNITO_WINDOW, true);
[email protected]e19516d2009-04-28 17:15:19936 menuState_->UpdateCommandEnabled(IDC_OPEN_FILE, true);
[email protected]93e181762009-05-29 14:30:38937 menuState_->UpdateCommandEnabled(IDC_CLEAR_BROWSING_DATA, true);
[email protected]fbc947b2009-06-19 13:28:24938 menuState_->UpdateCommandEnabled(IDC_RESTORE_TAB, false);
[email protected]91a5b3e2009-10-30 19:32:22939 menuState_->UpdateCommandEnabled(IDC_FOCUS_LOCATION, true);
[email protected]cddc5242009-12-07 18:54:33940 menuState_->UpdateCommandEnabled(IDC_FOCUS_SEARCH, true);
[email protected]af7c25e2010-01-23 05:46:45941 menuState_->UpdateCommandEnabled(IDC_SHOW_BOOKMARK_MANAGER, true);
[email protected]91a5b3e2009-10-30 19:32:22942 menuState_->UpdateCommandEnabled(IDC_SHOW_HISTORY, true);
943 menuState_->UpdateCommandEnabled(IDC_SHOW_DOWNLOADS, true);
[email protected]bb37e6ec2009-11-18 17:59:36944 menuState_->UpdateCommandEnabled(IDC_MANAGE_EXTENSIONS, true);
[email protected]1fdff702009-10-22 00:36:18945 menuState_->UpdateCommandEnabled(IDC_HELP_PAGE, true);
[email protected]4aee5652009-11-19 18:59:21946 menuState_->UpdateCommandEnabled(IDC_IMPORT_SETTINGS, true);
[email protected]1c41a932010-11-22 23:41:23947 menuState_->UpdateCommandEnabled(IDC_FEEDBACK, true);
[email protected]d3b98c82010-07-14 07:45:59948 menuState_->UpdateCommandEnabled(IDC_SYNC_BOOKMARKS, true);
[email protected]ceaaf1812010-01-20 22:13:09949 menuState_->UpdateCommandEnabled(IDC_TASK_MANAGER, true);
[email protected]88d74942009-01-21 22:04:44950}
[email protected]1bcdb532009-01-16 17:47:57951
[email protected]a99fce0e2011-03-21 20:58:48952// The Confirm to Quit preference is atypical in that the preference lives in
953// the app menu right above the Quit menu item. This method will refresh the
954// display of that item depending on the preference state.
955- (void)updateConfirmToQuitPrefMenuItem:(NSMenuItem*)item {
956 // Format the string so that the correct key equivalent is displayed.
957 NSString* acceleratorString = [ConfirmQuitPanelController keyCommandString];
958 NSString* title = l10n_util::GetNSStringF(IDS_CONFIRM_TO_QUIT_OPTION,
959 base::SysNSStringToUTF16(acceleratorString));
960 [item setTitle:title];
961
962 const PrefService* prefService = [self defaultProfile]->GetPrefs();
963 bool enabled = prefService->GetBoolean(prefs::kConfirmToQuitEnabled);
964 [item setState:enabled ? NSOnState : NSOffState];
965}
966
[email protected]eef99c22010-08-17 05:55:16967- (void)registerServicesMenuTypesTo:(NSApplication*)app {
[email protected]09ea24c2010-08-23 19:05:41968 // Note that RenderWidgetHostViewCocoa implements NSServicesRequests which
969 // handles requests from services.
970 NSArray* types = [NSArray arrayWithObjects:NSStringPboardType, nil];
971 [app registerServicesMenuSendTypes:types returnTypes:types];
[email protected]eef99c22010-08-17 05:55:16972}
973
[email protected]3f34599d2009-03-25 22:11:43974- (Profile*)defaultProfile {
[email protected]3f34599d2009-03-25 22:11:43975 if (g_browser_process->profile_manager())
[email protected]844a1002011-04-19 11:37:21976 return g_browser_process->profile_manager()->GetDefaultProfile();
[email protected]3f34599d2009-03-25 22:11:43977
[email protected]f6314002009-04-23 01:18:13978 return NULL;
[email protected]3f34599d2009-03-25 22:11:43979}
980
[email protected]57750f822009-04-21 21:43:09981// Various methods to open URLs that we get in a native fashion. We use
982// BrowserInit here because on the other platforms, URLs to open come through
983// the ProcessSingleton, and it calls BrowserInit. It's best to bottleneck the
984// openings through that for uniform handling.
985
[email protected]d630d7d52010-02-21 00:55:11986- (void)openUrls:(const std::vector<GURL>&)urls {
[email protected]caadfca2010-01-22 21:51:21987 // If the browser hasn't started yet, just queue up the URLs.
988 if (!startupComplete_) {
[email protected]d630d7d52010-02-21 00:55:11989 startupUrls_.insert(startupUrls_.end(), urls.begin(), urls.end());
[email protected]136140c2009-05-19 13:58:25990 return;
991 }
[email protected]57750f822009-04-21 21:43:09992
[email protected]0e863512009-05-28 19:45:07993 Browser* browser = BrowserList::GetLastActive();
994 // if no browser window exists then create one with no tabs to be filled in
995 if (!browser) {
996 browser = Browser::Create([self defaultProfile]);
[email protected]ce560f82009-06-03 09:39:44997 browser->window()->Show();
[email protected]0e863512009-05-28 19:45:07998 }
999
[email protected]947446b2010-10-21 03:36:311000 CommandLine dummy(CommandLine::NO_PROGRAM);
[email protected]f805fe82010-08-03 22:47:101001 BrowserInit::LaunchWithProfile launch(FilePath(), dummy);
[email protected]0e863512009-05-28 19:45:071002 launch.OpenURLsInBrowser(browser, false, urls);
[email protected]57750f822009-04-21 21:43:091003}
1004
[email protected]57750f822009-04-21 21:43:091005- (void)getUrl:(NSAppleEventDescriptor*)event
1006 withReply:(NSAppleEventDescriptor*)reply {
1007 NSString* urlStr = [[event paramDescriptorForKeyword:keyDirectObject]
1008 stringValue];
1009
1010 GURL gurl(base::SysNSStringToUTF8(urlStr));
1011 std::vector<GURL> gurlVector;
1012 gurlVector.push_back(gurl);
1013
[email protected]d630d7d52010-02-21 00:55:111014 [self openUrls:gurlVector];
[email protected]57750f822009-04-21 21:43:091015}
1016
[email protected]caadfca2010-01-22 21:51:211017- (void)application:(NSApplication*)sender
1018 openFiles:(NSArray*)filenames {
[email protected]57750f822009-04-21 21:43:091019 std::vector<GURL> gurlVector;
[email protected]caadfca2010-01-22 21:51:211020 for (NSString* file in filenames) {
1021 GURL gurl = net::FilePathToFileURL(FilePath(base::SysNSStringToUTF8(file)));
[email protected]57750f822009-04-21 21:43:091022 gurlVector.push_back(gurl);
1023 }
[email protected]caadfca2010-01-22 21:51:211024 if (!gurlVector.empty())
[email protected]d630d7d52010-02-21 00:55:111025 [self openUrls:gurlVector];
[email protected]caadfca2010-01-22 21:51:211026 else
1027 NOTREACHED() << "Nothing to open!";
[email protected]57750f822009-04-21 21:43:091028
[email protected]caadfca2010-01-22 21:51:211029 [sender replyToOpenOrPrint:NSApplicationDelegateReplySuccess];
[email protected]57750f822009-04-21 21:43:091030}
[email protected]3f34599d2009-03-25 22:11:431031
[email protected]3111f08b2009-04-30 16:01:521032// Show the preferences window, or bring it to the front if it's already
1033// visible.
1034- (IBAction)showPreferences:(id)sender {
[email protected]445177652011-03-09 02:04:051035 if (Browser* browser = ActivateBrowser([self defaultProfile])) {
1036 // Show options tab in the active browser window.
1037 browser->OpenOptionsDialog();
[email protected]4a42d272010-06-18 01:29:421038 } else {
[email protected]445177652011-03-09 02:04:051039 // No browser window, so create one for the options tab.
1040 Browser::OpenOptionsWindow([self defaultProfile]);
[email protected]4a42d272010-06-18 01:29:421041 }
[email protected]d989f07c2009-11-14 00:35:461042}
1043
[email protected]bde3dda2009-05-20 22:13:071044// Called when the about window is closed. We use this to release the
1045// window controller.
[email protected]d4e18652009-11-19 21:52:161046- (void)aboutWindowClosed:(NSNotification*)notification {
1047 NSWindow* window = [aboutController_ window];
[email protected]2ce9c89752011-02-25 18:24:341048 DCHECK_EQ([notification object], window);
[email protected]bde3dda2009-05-20 22:13:071049 [[NSNotificationCenter defaultCenter]
[email protected]49aeee52009-10-26 19:58:131050 removeObserver:self
[email protected]d4e18652009-11-19 21:52:161051 name:NSWindowWillCloseNotification
1052 object:window];
1053 // AboutWindowControllers are autoreleased in
1054 // -[AboutWindowController windowWillClose:].
1055 aboutController_ = nil;
[email protected]bde3dda2009-05-20 22:13:071056}
1057
1058- (IBAction)orderFrontStandardAboutPanel:(id)sender {
[email protected]bde3dda2009-05-20 22:13:071059 if (!aboutController_) {
[email protected]d4e18652009-11-19 21:52:161060 aboutController_ =
1061 [[AboutWindowController alloc] initWithProfile:[self defaultProfile]];
[email protected]49aeee52009-10-26 19:58:131062
[email protected]bde3dda2009-05-20 22:13:071063 // Watch for a notification of when it goes away so that we can destroy
1064 // the controller.
1065 [[NSNotificationCenter defaultCenter]
1066 addObserver:self
1067 selector:@selector(aboutWindowClosed:)
[email protected]d4e18652009-11-19 21:52:161068 name:NSWindowWillCloseNotification
1069 object:[aboutController_ window]];
[email protected]bde3dda2009-05-20 22:13:071070 }
[email protected]49aeee52009-10-26 19:58:131071
[email protected]bde3dda2009-05-20 22:13:071072 [aboutController_ showWindow:self];
[email protected]bde3dda2009-05-20 22:13:071073}
1074
[email protected]a99fce0e2011-03-21 20:58:481075- (IBAction)toggleConfirmToQuit:(id)sender {
1076 PrefService* prefService = [self defaultProfile]->GetPrefs();
1077 bool enabled = prefService->GetBoolean(prefs::kConfirmToQuitEnabled);
1078 prefService->SetBoolean(prefs::kConfirmToQuitEnabled, !enabled);
1079}
1080
[email protected]3d4bd23f2009-10-06 03:58:381081// Explicitly bring to the foreground when creating new windows from the dock.
[email protected]93b59fc2010-12-21 20:00:471082- (void)commandFromDock:(id)sender {
[email protected]3d4bd23f2009-10-06 03:58:381083 [NSApp activateIgnoringOtherApps:YES];
1084 [self commandDispatch:sender];
1085}
1086
[email protected]227fcee2010-06-11 19:19:371087- (NSMenu*)applicationDockMenu:(NSApplication*)sender {
[email protected]018a3962009-09-17 22:23:441088 NSMenu* dockMenu = [[[NSMenu alloc] initWithTitle: @""] autorelease];
[email protected]93b59fc2010-12-21 20:00:471089 Profile* profile = [self defaultProfile];
1090
[email protected]018a3962009-09-17 22:23:441091 NSString* titleStr = l10n_util::GetNSStringWithFixup(IDS_NEW_WINDOW_MAC);
[email protected]8c6b029d2011-01-21 22:39:511092 scoped_nsobject<NSMenuItem> item(
1093 [[NSMenuItem alloc] initWithTitle:titleStr
1094 action:@selector(commandFromDock:)
1095 keyEquivalent:@""]);
[email protected]288bfcd32009-09-14 18:14:461096 [item setTarget:self];
1097 [item setTag:IDC_NEW_WINDOW];
[email protected]018a3962009-09-17 22:23:441098 [dockMenu addItem:item];
[email protected]288bfcd32009-09-14 18:14:461099
1100 titleStr = l10n_util::GetNSStringWithFixup(IDS_NEW_INCOGNITO_WINDOW_MAC);
[email protected]018a3962009-09-17 22:23:441101 item.reset([[NSMenuItem alloc] initWithTitle:titleStr
[email protected]8c6b029d2011-01-21 22:39:511102 action:@selector(commandFromDock:)
[email protected]018a3962009-09-17 22:23:441103 keyEquivalent:@""]);
[email protected]288bfcd32009-09-14 18:14:461104 [item setTarget:self];
1105 [item setTag:IDC_NEW_INCOGNITO_WINDOW];
[email protected]018a3962009-09-17 22:23:441106 [dockMenu addItem:item];
[email protected]288bfcd32009-09-14 18:14:461107
[email protected]8c6b029d2011-01-21 22:39:511108 // TODO(rickcam): Mock out BackgroundApplicationListModel, then add unit
1109 // tests which use the mock in place of the profile-initialized model.
1110
1111 // Avoid breaking unit tests which have no profile.
1112 if (profile) {
1113 BackgroundApplicationListModel applications(profile);
1114 if (applications.size()) {
1115 int position = 0;
1116 NSString* menuStr =
1117 l10n_util::GetNSStringWithFixup(IDS_BACKGROUND_APPS_MAC);
1118 scoped_nsobject<NSMenu> appMenu([[NSMenu alloc] initWithTitle:menuStr]);
1119 for (ExtensionList::const_iterator cursor = applications.begin();
1120 cursor != applications.end();
1121 ++cursor, ++position) {
[email protected]2ce9c89752011-02-25 18:24:341122 DCHECK_EQ(applications.GetPosition(*cursor), position);
[email protected]8c6b029d2011-01-21 22:39:511123 NSString* itemStr =
1124 base::SysUTF16ToNSString(UTF8ToUTF16((*cursor)->name()));
1125 scoped_nsobject<NSMenuItem> appItem([[NSMenuItem alloc]
1126 initWithTitle:itemStr
1127 action:@selector(commandFromDock:)
1128 keyEquivalent:@""]);
1129 [appItem setTarget:self];
1130 [appItem setTag:position];
1131 [appMenu addItem:appItem];
1132 }
1133 scoped_nsobject<NSMenuItem> appMenuItem([[NSMenuItem alloc]
1134 initWithTitle:menuStr
1135 action:@selector(commandFromDock:)
1136 keyEquivalent:@""]);
1137 [appMenuItem setTarget:self];
1138 [appMenuItem setTag:position];
1139 [appMenuItem setSubmenu:appMenu];
1140 [dockMenu addItem:appMenuItem];
1141 }
1142 }
1143
[email protected]018a3962009-09-17 22:23:441144 return dockMenu;
[email protected]288bfcd32009-09-14 18:14:461145}
1146
[email protected]d630d7d52010-02-21 00:55:111147- (const std::vector<GURL>&)startupUrls {
1148 return startupUrls_;
[email protected]caadfca2010-01-22 21:51:211149}
1150
[email protected]d630d7d52010-02-21 00:55:111151- (void)clearStartupUrls {
1152 startupUrls_.clear();
[email protected]caadfca2010-01-22 21:51:211153}
1154
[email protected]7e4c69d02011-05-11 22:16:471155- (BookmarkMenuBridge*)bookmarkMenuBridge {
1156 return bookmarkMenuBridge_.get();
1157}
1158
[email protected]d630d7d52010-02-21 00:55:111159@end // @implementation AppController
[email protected]60ad3e22009-09-18 21:07:191160
1161//---------------------------------------------------------------------------
1162
[email protected]0934de82011-03-09 18:42:161163namespace browser {
1164
1165void ShowInstantConfirmDialog(gfx::NativeWindow parent, Profile* profile) {
1166 if (Browser* browser = ActivateBrowser(profile)) {
[email protected]b99081b2011-03-10 17:43:471167 browser->OpenInstantConfirmDialog();
[email protected]0934de82011-03-09 18:42:161168 } else {
[email protected]b99081b2011-03-10 17:43:471169 Browser::OpenInstantConfirmDialogWindow(profile);
[email protected]0934de82011-03-09 18:42:161170 }
1171}
1172
1173} // namespace browser
1174
[email protected]216e9042009-10-29 17:05:231175namespace app_controller_mac {
1176
1177bool IsOpeningNewWindow() {
1178 return g_is_opening_new_window;
1179}
1180
1181} // namespace app_controller_mac