blob: ccfb0ebaabaa4fde2864dc682c4ca8d8934fce63 [file] [log] [blame]
[email protected]1bcdb532009-01-16 17:47:571// Copyright (c) 2009 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]2c47bc12009-04-10 20:14:005#import "chrome/browser/app_controller_mac.h"
[email protected]1bcdb532009-01-16 17:47:576
[email protected]288bfcd32009-09-14 18:14:467#include "app/l10n_util_mac.h"
[email protected]57750f822009-04-21 21:43:098#include "base/command_line.h"
[email protected]09729a552009-08-03 23:21:419#include "base/mac_util.h"
[email protected]2c47bc12009-04-10 20:14:0010#include "base/message_loop.h"
[email protected]57750f822009-04-21 21:43:0911#include "base/sys_string_conversions.h"
[email protected]2c47bc12009-04-10 20:14:0012#include "chrome/app/chrome_dll_resource.h"
13#include "chrome/browser/browser.h"
[email protected]57750f822009-04-21 21:43:0914#include "chrome/browser/browser_init.h"
[email protected]2c47bc12009-04-10 20:14:0015#include "chrome/browser/browser_list.h"
[email protected]5c238752009-06-13 10:29:0716#include "chrome/browser/browser_process.h"
[email protected]41741a962009-02-18 21:51:3917#include "chrome/browser/browser_shutdown.h"
[email protected]ce560f82009-06-03 09:39:4418#include "chrome/browser/browser_window.h"
[email protected]3b6aa8b62009-09-15 21:36:1119#import "chrome/browser/chrome_application_mac.h"
[email protected]bde3dda2009-05-20 22:13:0720#import "chrome/browser/cocoa/about_window_controller.h"
[email protected]3f34599d2009-03-25 22:11:4321#import "chrome/browser/cocoa/bookmark_menu_bridge.h"
[email protected]a9e8afc2009-08-11 22:03:1722#import "chrome/browser/cocoa/browser_window_cocoa.h"
23#import "chrome/browser/cocoa/browser_window_controller.h"
[email protected]1c84c82c2009-07-27 15:37:2524#import "chrome/browser/cocoa/history_menu_bridge.h"
[email protected]93e181762009-05-29 14:30:3825#import "chrome/browser/cocoa/clear_browsing_data_controller.h"
[email protected]2bcec612009-05-14 17:50:5326#import "chrome/browser/cocoa/encoding_menu_controller_delegate_mac.h"
[email protected]42404382009-04-30 17:59:2427#import "chrome/browser/cocoa/preferences_window_controller.h"
[email protected]449dd2f2009-05-27 13:04:0028#import "chrome/browser/cocoa/tab_strip_controller.h"
29#import "chrome/browser/cocoa/tab_window_controller.h"
[email protected]2c47bc12009-04-10 20:14:0030#include "chrome/browser/command_updater.h"
[email protected]09729a552009-08-03 23:21:4131#include "chrome/browser/download/download_manager.h"
[email protected]a9e8afc2009-08-11 22:03:1732#include "chrome/browser/tab_contents/tab_contents.h"
[email protected]fbc947b2009-06-19 13:28:2433#include "chrome/browser/sessions/tab_restore_service.h"
[email protected]f4acfae82009-09-11 00:33:3834#include "chrome/common/chrome_switches.h"
[email protected]cd63ef62009-05-06 19:41:3735#include "chrome/common/pref_names.h"
36#include "chrome/common/pref_service.h"
[email protected]2c47bc12009-04-10 20:14:0037#include "chrome/browser/profile_manager.h"
38#include "chrome/common/temp_scaffolding_stubs.h"
[email protected]09729a552009-08-03 23:21:4139#include "grit/generated_resources.h"
[email protected]88d74942009-01-21 22:04:4440
41@interface AppController(PRIVATE)
42- (void)initMenuState;
[email protected]136140c2009-05-19 13:58:2543- (void)openURLs:(const std::vector<GURL>&)urls;
44- (void)openPendingURLs;
[email protected]57750f822009-04-21 21:43:0945- (void)getUrl:(NSAppleEventDescriptor*)event
46 withReply:(NSAppleEventDescriptor*)reply;
[email protected]36fe18f2009-04-29 20:26:2047- (void)openFiles:(NSAppleEventDescriptor*)event
48 withReply:(NSAppleEventDescriptor*)reply;
[email protected]449dd2f2009-05-27 13:04:0049- (void)windowLayeringDidChange:(NSNotification*)inNotification;
[email protected]73f5b362009-08-10 23:58:2150- (BOOL)userWillWaitForInProgressDownloads:(int)downloadCount;
[email protected]09729a552009-08-03 23:21:4151- (BOOL)shouldQuitWithInProgressDownloads;
[email protected]288bfcd32009-09-14 18:14:4652- (NSMenu*)applicationDockMenu:(NSApplication*)sender;
[email protected]88d74942009-01-21 22:04:4453@end
[email protected]1bcdb532009-01-16 17:47:5754
55@implementation AppController
56
[email protected]cd63ef62009-05-06 19:41:3757// This method is called very early in application startup (ie, before
58// the profile is loaded or any preferences have been registered). Defer any
59// user-data initialization until -applicationDidFinishLaunching:.
[email protected]88d74942009-01-21 22:04:4460- (void)awakeFromNib {
[email protected]136140c2009-05-19 13:58:2561 pendingURLs_.reset(new std::vector<GURL>());
[email protected]88d74942009-01-21 22:04:4462
[email protected]136140c2009-05-19 13:58:2563 // We need to register the handlers early to catch events fired on launch.
[email protected]57750f822009-04-21 21:43:0964 NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager];
65 [em setEventHandler:self
66 andSelector:@selector(getUrl:withReply:)
67 forEventClass:kInternetEventClass
68 andEventID:kAEGetURL];
69 [em setEventHandler:self
70 andSelector:@selector(getUrl:withReply:)
71 forEventClass:'WWW!' // A particularly ancient AppleEvent that dates
72 andEventID:'OURL']; // back to the Spyglass days.
[email protected]36fe18f2009-04-29 20:26:2073 [em setEventHandler:self
74 andSelector:@selector(openFiles:withReply:)
75 forEventClass:kCoreEventClass
76 andEventID:kAEOpenDocuments];
[email protected]cd63ef62009-05-06 19:41:3777
[email protected]449dd2f2009-05-27 13:04:0078 // Register for various window layering changes. We use these to update
79 // various UI elements (command-key equivalents, etc) when the frontmost
80 // window changes.
81 NSNotificationCenter* notificationCenter =
82 [NSNotificationCenter defaultCenter];
[email protected]ce560f82009-06-03 09:39:4483 [notificationCenter
[email protected]449dd2f2009-05-27 13:04:0084 addObserver:self
85 selector:@selector(windowLayeringDidChange:)
86 name:NSWindowDidBecomeKeyNotification
87 object:nil];
88 [notificationCenter
89 addObserver:self
90 selector:@selector(windowLayeringDidChange:)
91 name:NSWindowDidResignKeyNotification
92 object:nil];
93 [notificationCenter
94 addObserver:self
95 selector:@selector(windowLayeringDidChange:)
96 name:NSWindowDidBecomeMainNotification
97 object:nil];
98 [notificationCenter
99 addObserver:self
100 selector:@selector(windowLayeringDidChange:)
101 name:NSWindowDidResignMainNotification
102 object:nil];
103
104 // Register for a notification that the number of tabs changes in windows
105 // so we can adjust the close tab/window command keys.
106 [notificationCenter
107 addObserver:self
108 selector:@selector(tabsChanged:)
109 name:kTabStripNumberOfTabsChanged
110 object:nil];
111
[email protected]136140c2009-05-19 13:58:25112 // Set up the command updater for when there are no windows open
113 [self initMenuState];
[email protected]f4acfae82009-09-11 00:33:38114
115 // Activate (bring to foreground) if asked to do so. On
116 // Windows this logic isn't necessary since
117 // BrowserWindow::Activate() calls ::SetForegroundWindow() which is
118 // adequate. On Mac, BrowserWindow::Activate() calls -[NSWindow
119 // makeKeyAndOrderFront:] which does not activate the application
120 // itself.
121 const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess();
122 if (parsed_command_line.HasSwitch(switches::kActivateOnLaunch)) {
123 [NSApp activateIgnoringOtherApps:YES];
124 }
[email protected]136140c2009-05-19 13:58:25125}
126
[email protected]a9e8afc2009-08-11 22:03:17127// Checks if there are any tabs with sheets open, and if so, raises one of
128// the tabs with a sheet and returns NO.
129- (BOOL)shouldQuitWithOpenPerTabSheets {
130 BrowserList::const_iterator it = BrowserList::begin();
131 for (; it != BrowserList::end(); ++it) {
132 Browser* browser = *it;
133 BrowserWindowCocoa* window =
134 static_cast<BrowserWindowCocoa*>(browser->window());
135
136 // Could do this more nicely with a method e.g. on BWC. If I decide for
137 // keeping it this way, at least add a DCHECK().
138 BrowserWindowController* controller =
139 (BrowserWindowController*)[window->GetNativeHandle() windowController];
140
141 if (![controller shouldCloseWithOpenPerTabSheets])
142 return NO;
143 }
144
145 return YES;
146}
147
[email protected]a9e8afc2009-08-11 22:03:17148- (NSApplicationTerminateReply)applicationShouldTerminate:
149 (NSApplication *)sender {
150 // Do not quit if any per-tab sheets are open, as required by
151 // GTMWindowSheetController.
152 if (![self shouldQuitWithOpenPerTabSheets])
153 return NSTerminateCancel;
154
155 // Check for in-progress downloads, and prompt the user if they really want to
156 // quit (and thus cancel the downloads).
157 if (![self shouldQuitWithInProgressDownloads])
158 return NSTerminateCancel;
159
160 return NSTerminateNow;
161}
162
[email protected]449dd2f2009-05-27 13:04:00163// Called when the app is shutting down. Clean-up as appropriate.
164- (void)applicationWillTerminate:(NSNotification *)aNotification {
[email protected]3b6aa8b62009-09-15 21:36:11165 NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager];
166 [em removeEventHandlerForEventClass:kInternetEventClass
167 andEventID:kAEGetURL];
168 [em removeEventHandlerForEventClass:'WWW!'
169 andEventID:'OURL'];
170 [em removeEventHandlerForEventClass:kCoreEventClass
171 andEventID:kAEOpenDocuments];
172
173 // Close all the windows.
174 BrowserList::CloseAllBrowsers(true);
175
176 // On Windows, this is done in Browser::OnWindowClosing, but that's not
177 // appropriate on Mac since we don't shut down when we reach zero windows.
178 browser_shutdown::OnShutdownStarting(browser_shutdown::BROWSER_EXIT);
179
180 // Release the reference to the browser process. Once all the browsers get
181 // dealloc'd, it will stop the RunLoop and fall back into main().
182 g_browser_process->ReleaseModule();
[email protected]fbc947b2009-06-19 13:28:24183
[email protected]449dd2f2009-05-27 13:04:00184 [[NSNotificationCenter defaultCenter] removeObserver:self];
185}
186
[email protected]3b6aa8b62009-09-15 21:36:11187- (void)didEndMainMessageLoop {
188 DCHECK(!BrowserList::HasBrowserWithProfile([self defaultProfile]));
189 if (!BrowserList::HasBrowserWithProfile([self defaultProfile])) {
190 // As we're shutting down, we need to nuke the TabRestoreService, which
191 // will start the shutdown of the NavigationControllers and allow for
192 // proper shutdown. If we don't do this, Chrome won't shut down cleanly,
193 // and may end up crashing when some thread tries to use the IO thread (or
194 // another thread) that is no longer valid.
195 [self defaultProfile]->ResetTabRestoreService();
196 }
197}
198
[email protected]449dd2f2009-05-27 13:04:00199// Helper routine to get the window controller if the key window is a tabbed
200// window, or nil if not. Examples of non-tabbed windows are "about" or
201// "preferences".
202- (TabWindowController*)keyWindowTabController {
203 NSWindowController* keyWindowController =
204 [[[NSApplication sharedApplication] keyWindow] windowController];
205 if ([keyWindowController isKindOfClass:[TabWindowController class]])
206 return (TabWindowController*)keyWindowController;
207
208 return nil;
209}
210
211// If the window has tabs, make "close window" be cmd-shift-w, otherwise leave
212// it as the normal cmd-w. Capitalization of the key equivalent affects whether
213// the shift modifer is used.
214- (void)adjustCloseWindowMenuItemKeyEquivalent:(BOOL)inHaveTabs {
215 [closeWindowMenuItem_ setKeyEquivalent:(inHaveTabs ? @"W" : @"w")];
216}
217
218// If the window has tabs, make "close tab" take over cmd-w, otherwise it
219// shouldn't have any key-equivalent because it should be disabled.
220- (void)adjustCloseTabMenuItemKeyEquivalent:(BOOL)hasTabs {
221 if (hasTabs) {
222 [closeTabMenuItem_ setKeyEquivalent:@"w"];
223 [closeTabMenuItem_ setKeyEquivalentModifierMask:NSCommandKeyMask];
224 } else {
225 [closeTabMenuItem_ setKeyEquivalent:@""];
226 [closeTabMenuItem_ setKeyEquivalentModifierMask:0];
227 }
228}
229
230// See if we have a window with tabs open, and adjust the key equivalents for
231// Close Tab/Close Window accordingly
232- (void)fixCloseMenuItemKeyEquivalents {
233 TabWindowController* tabController = [self keyWindowTabController];
234 BOOL windowWithMultipleTabs =
235 (tabController && [tabController numberOfTabs] > 1);
236 [self adjustCloseWindowMenuItemKeyEquivalent:windowWithMultipleTabs];
237 [self adjustCloseTabMenuItemKeyEquivalent:windowWithMultipleTabs];
238 fileMenuUpdatePending_ = NO;
239}
240
241// Fix up the "close tab/close window" command-key equivalents. We do this
242// after a delay to ensure that window layer state has been set by the time
243// we do the enabling.
244- (void)delayedFixCloseMenuItemKeyEquivalents {
245 if (!fileMenuUpdatePending_) {
246 [self performSelector:@selector(fixCloseMenuItemKeyEquivalents)
247 withObject:nil
248 afterDelay:0];
249 fileMenuUpdatePending_ = YES;
250 }
251}
252
253// Called when we get a notification about the window layering changing to
254// update the UI based on the new main window.
255- (void)windowLayeringDidChange:(NSNotification*)notify {
256 [self delayedFixCloseMenuItemKeyEquivalents];
[email protected]ce560f82009-06-03 09:39:44257
[email protected]449dd2f2009-05-27 13:04:00258 // TODO(pinkerton): If we have other things here, such as inspector panels
259 // that follow the contents of the selected webpage, we would update those
260 // here.
261}
262
263// Called when the number of tabs changes in one of the browser windows. The
264// object is the tab strip controller, but we don't currently care.
265- (void)tabsChanged:(NSNotification*)notify {
266 [self delayedFixCloseMenuItemKeyEquivalents];
267}
268
[email protected]414fde592009-05-21 16:14:43269// If the auto-update interval is not set, make it 5 hours.
270// This code is specific to Mac Chrome Dev Channel.
271// Placed here for 2 reasons:
272// 1) Same spot as other Pref stuff
273// 2) Try and be friendly by keeping this after app launch
274// TODO(jrg): remove once we go Beta.
275- (void)setUpdateCheckInterval {
276#if defined(GOOGLE_CHROME_BUILD)
277 CFStringRef app = (CFStringRef)@"com.google.Keystone.Agent";
278 CFStringRef checkInterval = (CFStringRef)@"checkInterval";
279 CFPropertyListRef plist = CFPreferencesCopyAppValue(checkInterval, app);
280 if (!plist) {
281 const float fiveHoursInSeconds = 5.0 * 60.0 * 60.0;
282 NSNumber *value = [NSNumber numberWithFloat:fiveHoursInSeconds];
283 CFPreferencesSetAppValue(checkInterval, value, app);
284 CFPreferencesAppSynchronize(app);
285 }
286#endif
287}
288
[email protected]136140c2009-05-19 13:58:25289// This is called after profiles have been loaded and preferences registered.
290// It is safe to access the default profile here.
291- (void)applicationDidFinishLaunching:(NSNotification*)notify {
292 // Hold an extra ref to the BrowserProcess singleton so it doesn't go away
293 // when all the browser windows get closed. We'll release it on quit which
294 // will be the signal to exit.
295 DCHECK(g_browser_process);
296 g_browser_process->AddRefModule();
297
[email protected]e5575022009-08-22 00:31:22298 bookmarkMenuBridge_.reset(new BookmarkMenuBridge([self defaultProfile]));
[email protected]1c84c82c2009-07-27 15:37:25299 historyMenuBridge_.reset(new HistoryMenuBridge([self defaultProfile]));
[email protected]cd63ef62009-05-06 19:41:37300
[email protected]414fde592009-05-21 16:14:43301 [self setUpdateCheckInterval];
[email protected]2bcec612009-05-14 17:50:53302
303 // Build up the encoding menu, the order of the items differs based on the
304 // current locale (see http://crbug.com/7647 for details).
305 // We need a valid g_browser_process to get the profile which is why we can't
306 // call this from awakeFromNib.
307 EncodingMenuControllerDelegate::BuildEncodingMenu([self defaultProfile]);
308
[email protected]136140c2009-05-19 13:58:25309 // Now that we're initialized we can open any URLs we've been holding onto.
310 [self openPendingURLs];
[email protected]7c321082009-02-09 15:35:47311}
312
[email protected]73f5b362009-08-10 23:58:21313// Helper function for populating and displaying the in progress downloads at
314// exit alert panel.
315- (BOOL)userWillWaitForInProgressDownloads:(int)downloadCount {
[email protected]e8b5f7882009-09-14 20:59:13316 NSString* warningText = nil;
317 NSString* explanationText = nil;
[email protected]73f5b362009-08-10 23:58:21318 NSString* waitTitle = nil;
319 NSString* exitTitle = nil;
320
321 // Set the dialog text based on whether or not there are multiple downloads.
322 if (downloadCount == 1) {
[email protected]e8b5f7882009-09-14 20:59:13323 // Dialog text: warning and explanation.
324 warningText =
325 base::SysWideToNSString(l10n_util::GetString(
326 IDS_SINGLE_DOWNLOAD_REMOVE_CONFIRM_WARNING));
327 explanationText =
328 base::SysWideToNSString(l10n_util::GetString(
329 IDS_SINGLE_DOWNLOAD_REMOVE_CONFIRM_EXPLANATION));
[email protected]73f5b362009-08-10 23:58:21330
331 // Cancel download and exit button text.
332 exitTitle =
[email protected]e8b5f7882009-09-14 20:59:13333 base::SysWideToNSString(l10n_util::GetString(
334 IDS_SINGLE_DOWNLOAD_REMOVE_CONFIRM_OK_BUTTON_LABEL));
[email protected]73f5b362009-08-10 23:58:21335
336 // Wait for download button text.
337 waitTitle =
[email protected]e8b5f7882009-09-14 20:59:13338 base::SysWideToNSString(l10n_util::GetString(
339 IDS_SINGLE_DOWNLOAD_REMOVE_CONFIRM_CANCEL_BUTTON_LABEL));
[email protected]73f5b362009-08-10 23:58:21340 } else {
[email protected]e8b5f7882009-09-14 20:59:13341 // Dialog text: warning and explanation.
342 warningText =
343 base::SysWideToNSString(l10n_util::GetStringF(
344 IDS_MULTIPLE_DOWNLOADS_REMOVE_CONFIRM_WARNING, downloadCount));
345 explanationText =
346 base::SysWideToNSString(l10n_util::GetString(
347 IDS_MULTIPLE_DOWNLOADS_REMOVE_CONFIRM_EXPLANATION));
[email protected]73f5b362009-08-10 23:58:21348
349 // Cancel downloads and exit button text.
350 exitTitle =
[email protected]e8b5f7882009-09-14 20:59:13351 base::SysWideToNSString(l10n_util::GetString(
352 IDS_MULTIPLE_DOWNLOADS_REMOVE_CONFIRM_OK_BUTTON_LABEL));
[email protected]73f5b362009-08-10 23:58:21353
354 // Wait for downloads button text.
355 waitTitle =
[email protected]e8b5f7882009-09-14 20:59:13356 base::SysWideToNSString(l10n_util::GetString(
357 IDS_MULTIPLE_DOWNLOADS_REMOVE_CONFIRM_CANCEL_BUTTON_LABEL));
[email protected]73f5b362009-08-10 23:58:21358 }
359
360 // 'waitButton' is the default choice.
[email protected]e8b5f7882009-09-14 20:59:13361 int choice = NSRunAlertPanel(warningText, explanationText,
362 waitTitle, exitTitle, nil);
[email protected]73f5b362009-08-10 23:58:21363 return choice == NSAlertDefaultReturn ? YES : NO;
364}
365
366// Check all profiles for in progress downloads, and if we find any, prompt the
[email protected]09729a552009-08-03 23:21:41367// user to see if we should continue to exit (and thus cancel the downloads), or
368// if we should wait.
369- (BOOL)shouldQuitWithInProgressDownloads {
[email protected]73f5b362009-08-10 23:58:21370 ProfileManager* profile_manager = g_browser_process->profile_manager();
371 if (!profile_manager)
372 return YES;
[email protected]09729a552009-08-03 23:21:41373
[email protected]73f5b362009-08-10 23:58:21374 ProfileManager::const_iterator it = profile_manager->begin();
375 for (; it != profile_manager->end(); ++it) {
376 Profile* profile = *it;
[email protected]09729a552009-08-03 23:21:41377 DownloadManager* download_manager = profile->GetDownloadManager();
[email protected]73f5b362009-08-10 23:58:21378 if (download_manager && download_manager->in_progress_count() > 0) {
379 int downloadCount = download_manager->in_progress_count();
380 if ([self userWillWaitForInProgressDownloads:downloadCount]) {
381 // Create a new browser window (if necessary) and navigate to the
382 // downloads page if the user chooses to wait.
383 Browser* browser = BrowserList::FindBrowserWithProfile(profile);
384 if (!browser) {
385 browser = Browser::Create(profile);
386 browser->window()->Show();
387 }
388 DCHECK(browser);
389 browser->ShowDownloadsTab();
390 return NO;
391 }
[email protected]09729a552009-08-03 23:21:41392
[email protected]73f5b362009-08-10 23:58:21393 // User wants to exit.
394 return YES;
[email protected]09729a552009-08-03 23:21:41395 }
[email protected]09729a552009-08-03 23:21:41396 }
397
[email protected]73f5b362009-08-10 23:58:21398 // No profiles or active downloads found, okay to exit.
[email protected]09729a552009-08-03 23:21:41399 return YES;
400}
401
[email protected]fbc947b2009-06-19 13:28:24402// Called to determine if we should enable the "restore tab" menu item.
403// Checks with the TabRestoreService to see if there's anything there to
404// restore and returns YES if so.
405- (BOOL)canRestoreTab {
406 TabRestoreService* service = [self defaultProfile]->GetTabRestoreService();
407 return service && !service->entries().empty();
408}
409
[email protected]88d74942009-01-21 22:04:44410// Called to validate menu items when there are no key windows. All the
411// items we care about have been set with the |commandDispatch:| action and
412// a target of FirstResponder in IB. If it's not one of those, let it
413// continue up the responder chain to be handled elsewhere. We pull out the
414// tag as the cross-platform constant to differentiate and dispatch the
415// various commands.
416- (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item {
417 SEL action = [item action];
418 BOOL enable = NO;
419 if (action == @selector(commandDispatch:)) {
420 NSInteger tag = [item tag];
[email protected]fbc947b2009-06-19 13:28:24421 if (menuState_->SupportsCommand(tag)) {
422 switch (tag) {
423 case IDC_RESTORE_TAB:
424 enable = [self canRestoreTab];
425 break;
426 default:
427 enable = menuState_->IsCommandEnabled(tag) ? YES : NO;
428 }
429 }
[email protected]3b6aa8b62009-09-15 21:36:11430 } else if (action == @selector(terminate:)) {
[email protected]88d74942009-01-21 22:04:44431 enable = YES;
[email protected]3111f08b2009-04-30 16:01:52432 } else if (action == @selector(showPreferences:)) {
433 enable = YES;
[email protected]bde3dda2009-05-20 22:13:07434 } else if (action == @selector(orderFrontStandardAboutPanel:)) {
435 enable = YES;
[email protected]88d74942009-01-21 22:04:44436 }
437 return enable;
438}
439
440// Called when the user picks a menu item when there are no key windows. Calls
441// through to the browser object to execute the command. This assumes that the
442// command is supported and doesn't check, otherwise it would have been disabled
443// in the UI in validateUserInterfaceItem:.
444- (void)commandDispatch:(id)sender {
[email protected]93e181762009-05-29 14:30:38445 Profile* defaultProfile = [self defaultProfile];
[email protected]f0a51fb52009-03-05 12:46:38446
[email protected]88d74942009-01-21 22:04:44447 NSInteger tag = [sender tag];
448 switch (tag) {
[email protected]fbc947b2009-06-19 13:28:24449 case IDC_NEW_TAB:
[email protected]88d74942009-01-21 22:04:44450 case IDC_NEW_WINDOW:
[email protected]93e181762009-05-29 14:30:38451 Browser::OpenEmptyWindow(defaultProfile);
[email protected]88d74942009-01-21 22:04:44452 break;
[email protected]863ff662009-01-26 20:18:18453 case IDC_NEW_INCOGNITO_WINDOW:
[email protected]93e181762009-05-29 14:30:38454 Browser::OpenURLOffTheRecord(defaultProfile, GURL());
[email protected]863ff662009-01-26 20:18:18455 break;
[email protected]fbc947b2009-06-19 13:28:24456 case IDC_RESTORE_TAB:
457 Browser::OpenWindowWithRestoredTabs(defaultProfile);
458 break;
[email protected]e19516d2009-04-28 17:15:19459 case IDC_OPEN_FILE:
[email protected]93e181762009-05-29 14:30:38460 Browser::OpenEmptyWindow(defaultProfile);
[email protected]e19516d2009-04-28 17:15:19461 BrowserList::GetLastActive()->
462 ExecuteCommandWithDisposition(IDC_OPEN_FILE, CURRENT_TAB);
463 break;
[email protected]93e181762009-05-29 14:30:38464 case IDC_CLEAR_BROWSING_DATA:
465 // There may not be a browser open, so use the default profile.
466 scoped_nsobject<ClearBrowsingDataController> controller(
467 [[ClearBrowsingDataController alloc]
468 initWithProfile:defaultProfile]);
469 [controller runModalDialog];
470 break;
[email protected]88d74942009-01-21 22:04:44471 };
472}
473
[email protected]ff81e0a2009-04-13 14:58:54474// NSApplication delegate method called when someone clicks on the
475// dock icon and there are no open windows. To match standard mac
476// behavior, we should open a new window.
477- (BOOL)applicationShouldHandleReopen:(NSApplication*)theApplication
478 hasVisibleWindows:(BOOL)flag {
479 // Don't do anything if there are visible windows. This will cause
480 // AppKit to unminimize the most recently minimized window.
481 if (flag)
482 return YES;
483
484 // Otherwise open a new window.
485 Browser::OpenEmptyWindow([self defaultProfile]);
486
487 // We've handled the reopen event, so return NO to tell AppKit not
488 // to do anything.
489 return NO;
490}
491
[email protected]88d74942009-01-21 22:04:44492- (void)initMenuState {
[email protected]3111f08b2009-04-30 16:01:52493 menuState_.reset(new CommandUpdater(NULL));
[email protected]fbc947b2009-06-19 13:28:24494 menuState_->UpdateCommandEnabled(IDC_NEW_TAB, true);
[email protected]88d74942009-01-21 22:04:44495 menuState_->UpdateCommandEnabled(IDC_NEW_WINDOW, true);
[email protected]863ff662009-01-26 20:18:18496 menuState_->UpdateCommandEnabled(IDC_NEW_INCOGNITO_WINDOW, true);
[email protected]e19516d2009-04-28 17:15:19497 menuState_->UpdateCommandEnabled(IDC_OPEN_FILE, true);
[email protected]93e181762009-05-29 14:30:38498 menuState_->UpdateCommandEnabled(IDC_CLEAR_BROWSING_DATA, true);
[email protected]fbc947b2009-06-19 13:28:24499 menuState_->UpdateCommandEnabled(IDC_RESTORE_TAB, false);
[email protected]88d74942009-01-21 22:04:44500 // TODO(pinkerton): ...more to come...
501}
[email protected]1bcdb532009-01-16 17:47:57502
[email protected]3f34599d2009-03-25 22:11:43503- (Profile*)defaultProfile {
[email protected]3f34599d2009-03-25 22:11:43504 // TODO(jrg): Find a better way to get the "default" profile.
505 if (g_browser_process->profile_manager())
[email protected]57750f822009-04-21 21:43:09506 return* g_browser_process->profile_manager()->begin();
[email protected]3f34599d2009-03-25 22:11:43507
[email protected]f6314002009-04-23 01:18:13508 return NULL;
[email protected]3f34599d2009-03-25 22:11:43509}
510
[email protected]57750f822009-04-21 21:43:09511// Various methods to open URLs that we get in a native fashion. We use
512// BrowserInit here because on the other platforms, URLs to open come through
513// the ProcessSingleton, and it calls BrowserInit. It's best to bottleneck the
514// openings through that for uniform handling.
515
[email protected]136140c2009-05-19 13:58:25516- (void)openURLs:(const std::vector<GURL>&)urls {
517 if (pendingURLs_.get()) {
518 // too early to open; save for later
519 pendingURLs_->insert(pendingURLs_->end(), urls.begin(), urls.end());
520 return;
521 }
[email protected]57750f822009-04-21 21:43:09522
[email protected]0e863512009-05-28 19:45:07523 Browser* browser = BrowserList::GetLastActive();
524 // if no browser window exists then create one with no tabs to be filled in
525 if (!browser) {
526 browser = Browser::Create([self defaultProfile]);
[email protected]ce560f82009-06-03 09:39:44527 browser->window()->Show();
[email protected]0e863512009-05-28 19:45:07528 }
529
[email protected]57750f822009-04-21 21:43:09530 CommandLine dummy((std::wstring()));
531 BrowserInit::LaunchWithProfile launch(std::wstring(), dummy);
[email protected]0e863512009-05-28 19:45:07532 launch.OpenURLsInBrowser(browser, false, urls);
[email protected]57750f822009-04-21 21:43:09533}
534
[email protected]136140c2009-05-19 13:58:25535- (void)openPendingURLs {
536 // Since the existence of pendingURLs_ is a flag that it's too early to
537 // open URLs, we need to reset pendingURLs_.
538 std::vector<GURL> urls;
539 swap(urls, *pendingURLs_);
540 pendingURLs_.reset();
541
542 if (urls.size())
543 [self openURLs:urls];
544}
[email protected]57750f822009-04-21 21:43:09545
546- (void)getUrl:(NSAppleEventDescriptor*)event
547 withReply:(NSAppleEventDescriptor*)reply {
548 NSString* urlStr = [[event paramDescriptorForKeyword:keyDirectObject]
549 stringValue];
550
551 GURL gurl(base::SysNSStringToUTF8(urlStr));
552 std::vector<GURL> gurlVector;
553 gurlVector.push_back(gurl);
554
[email protected]136140c2009-05-19 13:58:25555 [self openURLs:gurlVector];
[email protected]57750f822009-04-21 21:43:09556}
557
[email protected]36fe18f2009-04-29 20:26:20558- (void)openFiles:(NSAppleEventDescriptor*)event
559 withReply:(NSAppleEventDescriptor*)reply {
560 // Ordinarily we'd use the NSApplication delegate method
561 // -application:openFiles:, but Cocoa tries to be smart and it sends files
562 // specified on the command line into that delegate method. That's too smart
563 // for us (our setup isn't done by the time Cocoa triggers the delegate method
564 // and we crash). Since all we want are files dropped on the app icon, and we
565 // have cross-platform code to handle the command-line files anyway, an Apple
566 // Event handler fits the bill just right.
567 NSAppleEventDescriptor* fileList =
568 [event paramDescriptorForKeyword:keyDirectObject];
569 if (!fileList)
570 return;
[email protected]57750f822009-04-21 21:43:09571 std::vector<GURL> gurlVector;
572
[email protected]36fe18f2009-04-29 20:26:20573 for (NSInteger i = 1; i <= [fileList numberOfItems]; ++i) {
574 NSAppleEventDescriptor* fileAliasDesc = [fileList descriptorAtIndex:i];
575 if (!fileAliasDesc)
576 continue;
577 NSAppleEventDescriptor* fileURLDesc =
578 [fileAliasDesc coerceToDescriptorType:typeFileURL];
579 if (!fileURLDesc)
580 continue;
581 NSData* fileURLData = [fileURLDesc data];
582 if (!fileURLData)
583 continue;
584 GURL gurl(std::string((char*)[fileURLData bytes], [fileURLData length]));
[email protected]57750f822009-04-21 21:43:09585 gurlVector.push_back(gurl);
586 }
587
[email protected]136140c2009-05-19 13:58:25588 [self openURLs:gurlVector];
[email protected]57750f822009-04-21 21:43:09589}
[email protected]3f34599d2009-03-25 22:11:43590
[email protected]767543d2009-04-30 19:23:58591// Called when the preferences window is closed. We use this to release the
592// window controller.
593- (void)prefsWindowClosed:(NSNotification*)notify {
[email protected]bde3dda2009-05-20 22:13:07594 [[NSNotificationCenter defaultCenter]
595 removeObserver:self
596 name:kUserDoneEditingPrefsNotification
597 object:prefsController_.get()];
[email protected]767543d2009-04-30 19:23:58598 prefsController_.reset(NULL);
599}
600
[email protected]3111f08b2009-04-30 16:01:52601// Show the preferences window, or bring it to the front if it's already
602// visible.
603- (IBAction)showPreferences:(id)sender {
[email protected]42404382009-04-30 17:59:24604 if (!prefsController_.get()) {
[email protected]42404382009-04-30 17:59:24605 prefsController_.reset([[PreferencesWindowController alloc]
[email protected]cd63ef62009-05-06 19:41:37606 initWithProfile:[self defaultProfile]]);
[email protected]767543d2009-04-30 19:23:58607 // Watch for a notification of when it goes away so that we can destroy
608 // the controller.
609 [[NSNotificationCenter defaultCenter]
610 addObserver:self
611 selector:@selector(prefsWindowClosed:)
612 name:kUserDoneEditingPrefsNotification
613 object:prefsController_.get()];
[email protected]42404382009-04-30 17:59:24614 }
615 [prefsController_ showPreferences:sender];
[email protected]3111f08b2009-04-30 16:01:52616}
617
[email protected]bde3dda2009-05-20 22:13:07618// Called when the about window is closed. We use this to release the
619// window controller.
620- (void)aboutWindowClosed:(NSNotification*)notify {
621 [[NSNotificationCenter defaultCenter]
622 removeObserver:self
623 name:kUserClosedAboutNotification
624 object:aboutController_.get()];
625 aboutController_.reset(NULL);
626}
627
628- (IBAction)orderFrontStandardAboutPanel:(id)sender {
[email protected]bde3dda2009-05-20 22:13:07629 // Otherwise bring up our special dialog (e.g. with an auto-update button).
630 if (!aboutController_) {
631 aboutController_.reset([[AboutWindowController alloc]
632 initWithWindowNibName:@"About"]);
633 if (!aboutController_) {
634 // If we get here something is wacky. I managed to do it when
635 // testing by explicitly forcing an auto-update to an older
636 // version then trying to open the about box again (missing
637 // nib). This shouldn't be possible in general but let's try
638 // hard to not do nothing.
639 [NSApp orderFrontStandardAboutPanel:sender];
640 return;
641 }
642 // Watch for a notification of when it goes away so that we can destroy
643 // the controller.
644 [[NSNotificationCenter defaultCenter]
645 addObserver:self
646 selector:@selector(aboutWindowClosed:)
647 name:kUserClosedAboutNotification
648 object:aboutController_.get()];
649 }
650 if (![[aboutController_ window] isVisible])
651 [[aboutController_ window] center];
652 [aboutController_ showWindow:self];
[email protected]bde3dda2009-05-20 22:13:07653}
654
[email protected]288bfcd32009-09-14 18:14:46655- (NSMenu*)applicationDockMenu:(id)sender {
656 NSMenu* result = [[[NSMenu alloc] initWithTitle: @""] autorelease];
657 NSString* titleStr;
658 id item;
659
660 titleStr = l10n_util::GetNSStringWithFixup(IDS_NEW_WINDOW_MAC);
661 item = [[[NSMenuItem alloc] initWithTitle:titleStr
662 action:@selector(commandDispatch:)
663 keyEquivalent:@""] autorelease];
664 [item setTarget:self];
665 [item setTag:IDC_NEW_WINDOW];
666 [result addItem:item];
667
668 titleStr = l10n_util::GetNSStringWithFixup(IDS_NEW_INCOGNITO_WINDOW_MAC);
669 item = [[[NSMenuItem alloc] initWithTitle:titleStr
670 action:@selector(commandDispatch:)
671 keyEquivalent:@""] autorelease];
672 [item setTarget:self];
673 [item setTag:IDC_NEW_INCOGNITO_WINDOW];
674 [result addItem:item];
675
676 return result;
677}
678
[email protected]1bcdb532009-01-16 17:47:57679@end