blob: 0cdee36dd707e6955a0e16c5f79b3d1daa51c9f5 [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]09729a552009-08-03 23:21:417#include "app/l10n_util.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]bde3dda2009-05-20 22:13:0719#import "chrome/browser/cocoa/about_window_controller.h"
[email protected]3f34599d2009-03-25 22:11:4320#import "chrome/browser/cocoa/bookmark_menu_bridge.h"
[email protected]1c84c82c2009-07-27 15:37:2521#import "chrome/browser/cocoa/history_menu_bridge.h"
[email protected]93e181762009-05-29 14:30:3822#import "chrome/browser/cocoa/clear_browsing_data_controller.h"
[email protected]2bcec612009-05-14 17:50:5323#import "chrome/browser/cocoa/encoding_menu_controller_delegate_mac.h"
[email protected]42404382009-04-30 17:59:2424#import "chrome/browser/cocoa/preferences_window_controller.h"
[email protected]449dd2f2009-05-27 13:04:0025#import "chrome/browser/cocoa/tab_strip_controller.h"
26#import "chrome/browser/cocoa/tab_window_controller.h"
[email protected]2c47bc12009-04-10 20:14:0027#include "chrome/browser/command_updater.h"
[email protected]09729a552009-08-03 23:21:4128#include "chrome/browser/download/download_manager.h"
[email protected]fbc947b2009-06-19 13:28:2429#include "chrome/browser/sessions/tab_restore_service.h"
[email protected]cd63ef62009-05-06 19:41:3730#include "chrome/common/pref_names.h"
31#include "chrome/common/pref_service.h"
[email protected]2c47bc12009-04-10 20:14:0032#include "chrome/browser/profile_manager.h"
33#include "chrome/common/temp_scaffolding_stubs.h"
[email protected]09729a552009-08-03 23:21:4134#include "grit/generated_resources.h"
[email protected]b0a2f0c2009-07-22 14:04:2735#import "xib_localizers/main_menu_localizer.h"
[email protected]88d74942009-01-21 22:04:4436
37@interface AppController(PRIVATE)
38- (void)initMenuState;
[email protected]136140c2009-05-19 13:58:2539- (void)openURLs:(const std::vector<GURL>&)urls;
40- (void)openPendingURLs;
[email protected]57750f822009-04-21 21:43:0941- (void)getUrl:(NSAppleEventDescriptor*)event
42 withReply:(NSAppleEventDescriptor*)reply;
[email protected]36fe18f2009-04-29 20:26:2043- (void)openFiles:(NSAppleEventDescriptor*)event
44 withReply:(NSAppleEventDescriptor*)reply;
[email protected]449dd2f2009-05-27 13:04:0045- (void)windowLayeringDidChange:(NSNotification*)inNotification;
[email protected]73f5b362009-08-10 23:58:2146- (BOOL)userWillWaitForInProgressDownloads:(int)downloadCount;
[email protected]09729a552009-08-03 23:21:4147- (BOOL)shouldQuitWithInProgressDownloads;
[email protected]88d74942009-01-21 22:04:4448@end
[email protected]1bcdb532009-01-16 17:47:5749
50@implementation AppController
51
[email protected]cd63ef62009-05-06 19:41:3752// This method is called very early in application startup (ie, before
53// the profile is loaded or any preferences have been registered). Defer any
54// user-data initialization until -applicationDidFinishLaunching:.
[email protected]88d74942009-01-21 22:04:4455- (void)awakeFromNib {
[email protected]136140c2009-05-19 13:58:2556 pendingURLs_.reset(new std::vector<GURL>());
[email protected]88d74942009-01-21 22:04:4457
[email protected]136140c2009-05-19 13:58:2558 // We need to register the handlers early to catch events fired on launch.
[email protected]57750f822009-04-21 21:43:0959 NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager];
60 [em setEventHandler:self
61 andSelector:@selector(getUrl:withReply:)
62 forEventClass:kInternetEventClass
63 andEventID:kAEGetURL];
64 [em setEventHandler:self
65 andSelector:@selector(getUrl:withReply:)
66 forEventClass:'WWW!' // A particularly ancient AppleEvent that dates
67 andEventID:'OURL']; // back to the Spyglass days.
[email protected]36fe18f2009-04-29 20:26:2068 [em setEventHandler:self
69 andSelector:@selector(openFiles:withReply:)
70 forEventClass:kCoreEventClass
71 andEventID:kAEOpenDocuments];
[email protected]cd63ef62009-05-06 19:41:3772
[email protected]449dd2f2009-05-27 13:04:0073 // Register for various window layering changes. We use these to update
74 // various UI elements (command-key equivalents, etc) when the frontmost
75 // window changes.
76 NSNotificationCenter* notificationCenter =
77 [NSNotificationCenter defaultCenter];
[email protected]ce560f82009-06-03 09:39:4478 [notificationCenter
[email protected]449dd2f2009-05-27 13:04:0079 addObserver:self
80 selector:@selector(windowLayeringDidChange:)
81 name:NSWindowDidBecomeKeyNotification
82 object:nil];
83 [notificationCenter
84 addObserver:self
85 selector:@selector(windowLayeringDidChange:)
86 name:NSWindowDidResignKeyNotification
87 object:nil];
88 [notificationCenter
89 addObserver:self
90 selector:@selector(windowLayeringDidChange:)
91 name:NSWindowDidBecomeMainNotification
92 object:nil];
93 [notificationCenter
94 addObserver:self
95 selector:@selector(windowLayeringDidChange:)
96 name:NSWindowDidResignMainNotification
97 object:nil];
98
99 // Register for a notification that the number of tabs changes in windows
100 // so we can adjust the close tab/window command keys.
101 [notificationCenter
102 addObserver:self
103 selector:@selector(tabsChanged:)
104 name:kTabStripNumberOfTabsChanged
105 object:nil];
106
[email protected]136140c2009-05-19 13:58:25107 // Set up the command updater for when there are no windows open
108 [self initMenuState];
109}
110
[email protected]449dd2f2009-05-27 13:04:00111// Called when the app is shutting down. Clean-up as appropriate.
112- (void)applicationWillTerminate:(NSNotification *)aNotification {
[email protected]fbc947b2009-06-19 13:28:24113 DCHECK(!BrowserList::HasBrowserWithProfile([self defaultProfile]));
114 if (!BrowserList::HasBrowserWithProfile([self defaultProfile])) {
115 // As we're shutting down, we need to nuke the TabRestoreService, which will
116 // start the shutdown of the NavigationControllers and allow for proper
117 // shutdown. If we don't do this chrome won't shutdown cleanly, and may end
118 // up crashing when some thread tries to use the IO thread (or another
119 // thread) that is no longer valid.
120 [self defaultProfile]->ResetTabRestoreService();
121 }
122
[email protected]449dd2f2009-05-27 13:04:00123 [[NSNotificationCenter defaultCenter] removeObserver:self];
124}
125
126// Helper routine to get the window controller if the key window is a tabbed
127// window, or nil if not. Examples of non-tabbed windows are "about" or
128// "preferences".
129- (TabWindowController*)keyWindowTabController {
130 NSWindowController* keyWindowController =
131 [[[NSApplication sharedApplication] keyWindow] windowController];
132 if ([keyWindowController isKindOfClass:[TabWindowController class]])
133 return (TabWindowController*)keyWindowController;
134
135 return nil;
136}
137
138// If the window has tabs, make "close window" be cmd-shift-w, otherwise leave
139// it as the normal cmd-w. Capitalization of the key equivalent affects whether
140// the shift modifer is used.
141- (void)adjustCloseWindowMenuItemKeyEquivalent:(BOOL)inHaveTabs {
142 [closeWindowMenuItem_ setKeyEquivalent:(inHaveTabs ? @"W" : @"w")];
143}
144
145// If the window has tabs, make "close tab" take over cmd-w, otherwise it
146// shouldn't have any key-equivalent because it should be disabled.
147- (void)adjustCloseTabMenuItemKeyEquivalent:(BOOL)hasTabs {
148 if (hasTabs) {
149 [closeTabMenuItem_ setKeyEquivalent:@"w"];
150 [closeTabMenuItem_ setKeyEquivalentModifierMask:NSCommandKeyMask];
151 } else {
152 [closeTabMenuItem_ setKeyEquivalent:@""];
153 [closeTabMenuItem_ setKeyEquivalentModifierMask:0];
154 }
155}
156
157// See if we have a window with tabs open, and adjust the key equivalents for
158// Close Tab/Close Window accordingly
159- (void)fixCloseMenuItemKeyEquivalents {
160 TabWindowController* tabController = [self keyWindowTabController];
161 BOOL windowWithMultipleTabs =
162 (tabController && [tabController numberOfTabs] > 1);
163 [self adjustCloseWindowMenuItemKeyEquivalent:windowWithMultipleTabs];
164 [self adjustCloseTabMenuItemKeyEquivalent:windowWithMultipleTabs];
165 fileMenuUpdatePending_ = NO;
166}
167
168// Fix up the "close tab/close window" command-key equivalents. We do this
169// after a delay to ensure that window layer state has been set by the time
170// we do the enabling.
171- (void)delayedFixCloseMenuItemKeyEquivalents {
172 if (!fileMenuUpdatePending_) {
173 [self performSelector:@selector(fixCloseMenuItemKeyEquivalents)
174 withObject:nil
175 afterDelay:0];
176 fileMenuUpdatePending_ = YES;
177 }
178}
179
180// Called when we get a notification about the window layering changing to
181// update the UI based on the new main window.
182- (void)windowLayeringDidChange:(NSNotification*)notify {
183 [self delayedFixCloseMenuItemKeyEquivalents];
[email protected]ce560f82009-06-03 09:39:44184
[email protected]449dd2f2009-05-27 13:04:00185 // TODO(pinkerton): If we have other things here, such as inspector panels
186 // that follow the contents of the selected webpage, we would update those
187 // here.
188}
189
190// Called when the number of tabs changes in one of the browser windows. The
191// object is the tab strip controller, but we don't currently care.
192- (void)tabsChanged:(NSNotification*)notify {
193 [self delayedFixCloseMenuItemKeyEquivalents];
194}
195
[email protected]414fde592009-05-21 16:14:43196// If the auto-update interval is not set, make it 5 hours.
197// This code is specific to Mac Chrome Dev Channel.
198// Placed here for 2 reasons:
199// 1) Same spot as other Pref stuff
200// 2) Try and be friendly by keeping this after app launch
201// TODO(jrg): remove once we go Beta.
202- (void)setUpdateCheckInterval {
203#if defined(GOOGLE_CHROME_BUILD)
204 CFStringRef app = (CFStringRef)@"com.google.Keystone.Agent";
205 CFStringRef checkInterval = (CFStringRef)@"checkInterval";
206 CFPropertyListRef plist = CFPreferencesCopyAppValue(checkInterval, app);
207 if (!plist) {
208 const float fiveHoursInSeconds = 5.0 * 60.0 * 60.0;
209 NSNumber *value = [NSNumber numberWithFloat:fiveHoursInSeconds];
210 CFPreferencesSetAppValue(checkInterval, value, app);
211 CFPreferencesAppSynchronize(app);
212 }
213#endif
214}
215
[email protected]136140c2009-05-19 13:58:25216// This is called after profiles have been loaded and preferences registered.
217// It is safe to access the default profile here.
218- (void)applicationDidFinishLaunching:(NSNotification*)notify {
219 // Hold an extra ref to the BrowserProcess singleton so it doesn't go away
220 // when all the browser windows get closed. We'll release it on quit which
221 // will be the signal to exit.
222 DCHECK(g_browser_process);
223 g_browser_process->AddRefModule();
224
[email protected]b0a2f0c2009-07-22 14:04:27225 // TODO: move this into the MainMenu.xib once we clean up the startup order
226 // dependencies so that works. http://crbug.com/17380
[email protected]7b225242009-05-20 17:59:34227 // Create the localizer for the main menu. We can't do this in the nib
228 // because it's too early. Do it before we create any bookmark menus as well,
229 // just in case one has a title that matches any of our strings (unlikely,
230 // but technically possible).
[email protected]b0a2f0c2009-07-22 14:04:27231 scoped_nsobject<MainMenuLocalizer> localizer(
232 [[MainMenuLocalizer alloc] initWithBundle:nil]);
[email protected]7b225242009-05-20 17:59:34233 [localizer localizeObject:[NSApplication sharedApplication]
234 recursively:YES];
235
[email protected]cd63ef62009-05-06 19:41:37236 bookmarkMenuBridge_.reset(new BookmarkMenuBridge());
[email protected]1c84c82c2009-07-27 15:37:25237 historyMenuBridge_.reset(new HistoryMenuBridge([self defaultProfile]));
[email protected]cd63ef62009-05-06 19:41:37238
[email protected]414fde592009-05-21 16:14:43239 [self setUpdateCheckInterval];
[email protected]2bcec612009-05-14 17:50:53240
241 // Build up the encoding menu, the order of the items differs based on the
242 // current locale (see http://crbug.com/7647 for details).
243 // We need a valid g_browser_process to get the profile which is why we can't
244 // call this from awakeFromNib.
245 EncodingMenuControllerDelegate::BuildEncodingMenu([self defaultProfile]);
246
[email protected]136140c2009-05-19 13:58:25247 // Now that we're initialized we can open any URLs we've been holding onto.
248 [self openPendingURLs];
[email protected]7c321082009-02-09 15:35:47249}
250
[email protected]73f5b362009-08-10 23:58:21251// Helper function for populating and displaying the in progress downloads at
252// exit alert panel.
253- (BOOL)userWillWaitForInProgressDownloads:(int)downloadCount {
254 NSString* descriptionText = nil;
255 NSString* waitTitle = nil;
256 NSString* exitTitle = nil;
257
258 // Set the dialog text based on whether or not there are multiple downloads.
259 if (downloadCount == 1) {
260 // Dialog text.
261 descriptionText =
262 base::SysWideToNSString(
263 l10n_util::GetString(IDS_SINGLE_DOWNLOAD_REMOVE_CONFIRM_TITLE));
264
265 // Cancel download and exit button text.
266 exitTitle =
267 base::SysWideToNSString(
268 l10n_util::GetString(
269 IDS_SINGLE_DOWNLOAD_REMOVE_CONFIRM_OK_BUTTON_LABEL));
270
271 // Wait for download button text.
272 waitTitle =
273 base::SysWideToNSString(
274 l10n_util::GetString(
275 IDS_SINGLE_DOWNLOAD_REMOVE_CONFIRM_CANCEL_BUTTON_LABEL));
276 } else {
277 // Dialog text.
278 descriptionText =
279 base::SysWideToNSString(
280 l10n_util::GetStringF(IDS_MULTIPLE_DOWNLOADS_REMOVE_CONFIRM_TITLE,
281 downloadCount));
282
283 // Cancel downloads and exit button text.
284 exitTitle =
285 base::SysWideToNSString(
286 l10n_util::GetString(
287 IDS_MULTIPLE_DOWNLOADS_REMOVE_CONFIRM_OK_BUTTON_LABEL));
288
289 // Wait for downloads button text.
290 waitTitle =
291 base::SysWideToNSString(
292 l10n_util::GetString(
293 IDS_MULTIPLE_DOWNLOADS_REMOVE_CONFIRM_CANCEL_BUTTON_LABEL));
294 }
295
296 // 'waitButton' is the default choice.
297 int choice = NSRunAlertPanel(nil, descriptionText, waitTitle, exitTitle, nil);
298 return choice == NSAlertDefaultReturn ? YES : NO;
299}
300
301// Check all profiles for in progress downloads, and if we find any, prompt the
[email protected]09729a552009-08-03 23:21:41302// user to see if we should continue to exit (and thus cancel the downloads), or
303// if we should wait.
304- (BOOL)shouldQuitWithInProgressDownloads {
[email protected]73f5b362009-08-10 23:58:21305 ProfileManager* profile_manager = g_browser_process->profile_manager();
306 if (!profile_manager)
307 return YES;
[email protected]09729a552009-08-03 23:21:41308
[email protected]73f5b362009-08-10 23:58:21309 ProfileManager::const_iterator it = profile_manager->begin();
310 for (; it != profile_manager->end(); ++it) {
311 Profile* profile = *it;
[email protected]09729a552009-08-03 23:21:41312 DownloadManager* download_manager = profile->GetDownloadManager();
[email protected]73f5b362009-08-10 23:58:21313 if (download_manager && download_manager->in_progress_count() > 0) {
314 int downloadCount = download_manager->in_progress_count();
315 if ([self userWillWaitForInProgressDownloads:downloadCount]) {
316 // Create a new browser window (if necessary) and navigate to the
317 // downloads page if the user chooses to wait.
318 Browser* browser = BrowserList::FindBrowserWithProfile(profile);
319 if (!browser) {
320 browser = Browser::Create(profile);
321 browser->window()->Show();
322 }
323 DCHECK(browser);
324 browser->ShowDownloadsTab();
325 return NO;
326 }
[email protected]09729a552009-08-03 23:21:41327
[email protected]73f5b362009-08-10 23:58:21328 // User wants to exit.
329 return YES;
[email protected]09729a552009-08-03 23:21:41330 }
[email protected]09729a552009-08-03 23:21:41331 }
332
[email protected]73f5b362009-08-10 23:58:21333 // No profiles or active downloads found, okay to exit.
[email protected]09729a552009-08-03 23:21:41334 return YES;
335}
336
[email protected]983f0fc82009-01-27 16:28:44337// We can't use the standard terminate: method because it will abruptly exit
[email protected]1bcdb532009-01-16 17:47:57338// the app and leave things on the stack in an unfinalized state. We need to
339// post a quit message to our run loop so the stack can gracefully unwind.
[email protected]f0a51fb52009-03-05 12:46:38340- (IBAction)quit:(id)sender {
[email protected]1bcdb532009-01-16 17:47:57341 // TODO(pinkerton):
342 // since we have to roll it ourselves, ask the delegate (ourselves, really)
343 // if we should terminate. For example, we might not want to if the user
344 // has ongoing downloads or multiple windows/tabs open. However, this would
[email protected]f0a51fb52009-03-05 12:46:38345 // require posting UI and may require spinning up another run loop to
[email protected]1bcdb532009-01-16 17:47:57346 // handle it. If it says to continue, post the quit message, otherwise
347 // go back to normal.
[email protected]8a53ee042009-01-21 16:41:33348
[email protected]09729a552009-08-03 23:21:41349 // Check for in-progress downloads, and prompt the user if they really want to
350 // quit (and thus cancel the downloads).
351 if (![self shouldQuitWithInProgressDownloads])
352 return;
353
[email protected]57750f822009-04-21 21:43:09354 NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager];
355 [em removeEventHandlerForEventClass:kInternetEventClass
356 andEventID:kAEGetURL];
357 [em removeEventHandlerForEventClass:'WWW!'
358 andEventID:'OURL'];
[email protected]36fe18f2009-04-29 20:26:20359 [em removeEventHandlerForEventClass:kCoreEventClass
360 andEventID:kAEOpenDocuments];
[email protected]57750f822009-04-21 21:43:09361
[email protected]41741a962009-02-18 21:51:39362 // TODO(pinkerton): Not sure where this should live, including it here
363 // causes all sorts of asserts from the open renderers. On Windows, it
364 // lives in Browser::OnWindowClosing, but that's not appropriate on Mac
365 // since we don't shut down when we reach zero windows.
366 // browser_shutdown::OnShutdownStarting(browser_shutdown::WINDOW_CLOSE);
367
[email protected]8a53ee042009-01-21 16:41:33368 // Close all the windows.
[email protected]7c321082009-02-09 15:35:47369 BrowserList::CloseAllBrowsers(true);
[email protected]f0a51fb52009-03-05 12:46:38370
[email protected]7c321082009-02-09 15:35:47371 // Release the reference to the browser process. Once all the browsers get
372 // dealloc'd, it will stop the RunLoop and fall back into main().
373 g_browser_process->ReleaseModule();
[email protected]1bcdb532009-01-16 17:47:57374}
375
[email protected]fbc947b2009-06-19 13:28:24376// Called to determine if we should enable the "restore tab" menu item.
377// Checks with the TabRestoreService to see if there's anything there to
378// restore and returns YES if so.
379- (BOOL)canRestoreTab {
380 TabRestoreService* service = [self defaultProfile]->GetTabRestoreService();
381 return service && !service->entries().empty();
382}
383
[email protected]88d74942009-01-21 22:04:44384// Called to validate menu items when there are no key windows. All the
385// items we care about have been set with the |commandDispatch:| action and
386// a target of FirstResponder in IB. If it's not one of those, let it
387// continue up the responder chain to be handled elsewhere. We pull out the
388// tag as the cross-platform constant to differentiate and dispatch the
389// various commands.
390- (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item {
391 SEL action = [item action];
392 BOOL enable = NO;
393 if (action == @selector(commandDispatch:)) {
394 NSInteger tag = [item tag];
[email protected]fbc947b2009-06-19 13:28:24395 if (menuState_->SupportsCommand(tag)) {
396 switch (tag) {
397 case IDC_RESTORE_TAB:
398 enable = [self canRestoreTab];
399 break;
400 default:
401 enable = menuState_->IsCommandEnabled(tag) ? YES : NO;
402 }
403 }
[email protected]88d74942009-01-21 22:04:44404 } else if (action == @selector(quit:)) {
405 enable = YES;
[email protected]3111f08b2009-04-30 16:01:52406 } else if (action == @selector(showPreferences:)) {
407 enable = YES;
[email protected]bde3dda2009-05-20 22:13:07408 } else if (action == @selector(orderFrontStandardAboutPanel:)) {
409 enable = YES;
[email protected]88d74942009-01-21 22:04:44410 }
411 return enable;
412}
413
414// Called when the user picks a menu item when there are no key windows. Calls
415// through to the browser object to execute the command. This assumes that the
416// command is supported and doesn't check, otherwise it would have been disabled
417// in the UI in validateUserInterfaceItem:.
418- (void)commandDispatch:(id)sender {
[email protected]93e181762009-05-29 14:30:38419 Profile* defaultProfile = [self defaultProfile];
[email protected]f0a51fb52009-03-05 12:46:38420
[email protected]88d74942009-01-21 22:04:44421 NSInteger tag = [sender tag];
422 switch (tag) {
[email protected]fbc947b2009-06-19 13:28:24423 case IDC_NEW_TAB:
[email protected]88d74942009-01-21 22:04:44424 case IDC_NEW_WINDOW:
[email protected]93e181762009-05-29 14:30:38425 Browser::OpenEmptyWindow(defaultProfile);
[email protected]88d74942009-01-21 22:04:44426 break;
[email protected]863ff662009-01-26 20:18:18427 case IDC_NEW_INCOGNITO_WINDOW:
[email protected]93e181762009-05-29 14:30:38428 Browser::OpenURLOffTheRecord(defaultProfile, GURL());
[email protected]863ff662009-01-26 20:18:18429 break;
[email protected]fbc947b2009-06-19 13:28:24430 case IDC_RESTORE_TAB:
431 Browser::OpenWindowWithRestoredTabs(defaultProfile);
432 break;
[email protected]e19516d2009-04-28 17:15:19433 case IDC_OPEN_FILE:
[email protected]93e181762009-05-29 14:30:38434 Browser::OpenEmptyWindow(defaultProfile);
[email protected]e19516d2009-04-28 17:15:19435 BrowserList::GetLastActive()->
436 ExecuteCommandWithDisposition(IDC_OPEN_FILE, CURRENT_TAB);
437 break;
[email protected]93e181762009-05-29 14:30:38438 case IDC_CLEAR_BROWSING_DATA:
439 // There may not be a browser open, so use the default profile.
440 scoped_nsobject<ClearBrowsingDataController> controller(
441 [[ClearBrowsingDataController alloc]
442 initWithProfile:defaultProfile]);
443 [controller runModalDialog];
444 break;
[email protected]88d74942009-01-21 22:04:44445 };
446}
447
[email protected]ff81e0a2009-04-13 14:58:54448// NSApplication delegate method called when someone clicks on the
449// dock icon and there are no open windows. To match standard mac
450// behavior, we should open a new window.
451- (BOOL)applicationShouldHandleReopen:(NSApplication*)theApplication
452 hasVisibleWindows:(BOOL)flag {
453 // Don't do anything if there are visible windows. This will cause
454 // AppKit to unminimize the most recently minimized window.
455 if (flag)
456 return YES;
457
458 // Otherwise open a new window.
459 Browser::OpenEmptyWindow([self defaultProfile]);
460
461 // We've handled the reopen event, so return NO to tell AppKit not
462 // to do anything.
463 return NO;
464}
465
[email protected]88d74942009-01-21 22:04:44466- (void)initMenuState {
[email protected]3111f08b2009-04-30 16:01:52467 menuState_.reset(new CommandUpdater(NULL));
[email protected]fbc947b2009-06-19 13:28:24468 menuState_->UpdateCommandEnabled(IDC_NEW_TAB, true);
[email protected]88d74942009-01-21 22:04:44469 menuState_->UpdateCommandEnabled(IDC_NEW_WINDOW, true);
[email protected]863ff662009-01-26 20:18:18470 menuState_->UpdateCommandEnabled(IDC_NEW_INCOGNITO_WINDOW, true);
[email protected]e19516d2009-04-28 17:15:19471 menuState_->UpdateCommandEnabled(IDC_OPEN_FILE, true);
[email protected]93e181762009-05-29 14:30:38472 menuState_->UpdateCommandEnabled(IDC_CLEAR_BROWSING_DATA, true);
[email protected]fbc947b2009-06-19 13:28:24473 menuState_->UpdateCommandEnabled(IDC_RESTORE_TAB, false);
[email protected]88d74942009-01-21 22:04:44474 // TODO(pinkerton): ...more to come...
475}
[email protected]1bcdb532009-01-16 17:47:57476
[email protected]3f34599d2009-03-25 22:11:43477- (Profile*)defaultProfile {
[email protected]3f34599d2009-03-25 22:11:43478 // TODO(jrg): Find a better way to get the "default" profile.
479 if (g_browser_process->profile_manager())
[email protected]57750f822009-04-21 21:43:09480 return* g_browser_process->profile_manager()->begin();
[email protected]3f34599d2009-03-25 22:11:43481
[email protected]f6314002009-04-23 01:18:13482 return NULL;
[email protected]3f34599d2009-03-25 22:11:43483}
484
[email protected]57750f822009-04-21 21:43:09485// Various methods to open URLs that we get in a native fashion. We use
486// BrowserInit here because on the other platforms, URLs to open come through
487// the ProcessSingleton, and it calls BrowserInit. It's best to bottleneck the
488// openings through that for uniform handling.
489
[email protected]136140c2009-05-19 13:58:25490- (void)openURLs:(const std::vector<GURL>&)urls {
491 if (pendingURLs_.get()) {
492 // too early to open; save for later
493 pendingURLs_->insert(pendingURLs_->end(), urls.begin(), urls.end());
494 return;
495 }
[email protected]57750f822009-04-21 21:43:09496
[email protected]0e863512009-05-28 19:45:07497 Browser* browser = BrowserList::GetLastActive();
498 // if no browser window exists then create one with no tabs to be filled in
499 if (!browser) {
500 browser = Browser::Create([self defaultProfile]);
[email protected]ce560f82009-06-03 09:39:44501 browser->window()->Show();
[email protected]0e863512009-05-28 19:45:07502 }
503
[email protected]57750f822009-04-21 21:43:09504 CommandLine dummy((std::wstring()));
505 BrowserInit::LaunchWithProfile launch(std::wstring(), dummy);
[email protected]0e863512009-05-28 19:45:07506 launch.OpenURLsInBrowser(browser, false, urls);
[email protected]57750f822009-04-21 21:43:09507}
508
[email protected]136140c2009-05-19 13:58:25509- (void)openPendingURLs {
510 // Since the existence of pendingURLs_ is a flag that it's too early to
511 // open URLs, we need to reset pendingURLs_.
512 std::vector<GURL> urls;
513 swap(urls, *pendingURLs_);
514 pendingURLs_.reset();
515
516 if (urls.size())
517 [self openURLs:urls];
518}
[email protected]57750f822009-04-21 21:43:09519
520- (void)getUrl:(NSAppleEventDescriptor*)event
521 withReply:(NSAppleEventDescriptor*)reply {
522 NSString* urlStr = [[event paramDescriptorForKeyword:keyDirectObject]
523 stringValue];
524
525 GURL gurl(base::SysNSStringToUTF8(urlStr));
526 std::vector<GURL> gurlVector;
527 gurlVector.push_back(gurl);
528
[email protected]136140c2009-05-19 13:58:25529 [self openURLs:gurlVector];
[email protected]57750f822009-04-21 21:43:09530}
531
[email protected]36fe18f2009-04-29 20:26:20532- (void)openFiles:(NSAppleEventDescriptor*)event
533 withReply:(NSAppleEventDescriptor*)reply {
534 // Ordinarily we'd use the NSApplication delegate method
535 // -application:openFiles:, but Cocoa tries to be smart and it sends files
536 // specified on the command line into that delegate method. That's too smart
537 // for us (our setup isn't done by the time Cocoa triggers the delegate method
538 // and we crash). Since all we want are files dropped on the app icon, and we
539 // have cross-platform code to handle the command-line files anyway, an Apple
540 // Event handler fits the bill just right.
541 NSAppleEventDescriptor* fileList =
542 [event paramDescriptorForKeyword:keyDirectObject];
543 if (!fileList)
544 return;
[email protected]57750f822009-04-21 21:43:09545 std::vector<GURL> gurlVector;
546
[email protected]36fe18f2009-04-29 20:26:20547 for (NSInteger i = 1; i <= [fileList numberOfItems]; ++i) {
548 NSAppleEventDescriptor* fileAliasDesc = [fileList descriptorAtIndex:i];
549 if (!fileAliasDesc)
550 continue;
551 NSAppleEventDescriptor* fileURLDesc =
552 [fileAliasDesc coerceToDescriptorType:typeFileURL];
553 if (!fileURLDesc)
554 continue;
555 NSData* fileURLData = [fileURLDesc data];
556 if (!fileURLData)
557 continue;
558 GURL gurl(std::string((char*)[fileURLData bytes], [fileURLData length]));
[email protected]57750f822009-04-21 21:43:09559 gurlVector.push_back(gurl);
560 }
561
[email protected]136140c2009-05-19 13:58:25562 [self openURLs:gurlVector];
[email protected]57750f822009-04-21 21:43:09563}
[email protected]3f34599d2009-03-25 22:11:43564
[email protected]767543d2009-04-30 19:23:58565// Called when the preferences window is closed. We use this to release the
566// window controller.
567- (void)prefsWindowClosed:(NSNotification*)notify {
[email protected]bde3dda2009-05-20 22:13:07568 [[NSNotificationCenter defaultCenter]
569 removeObserver:self
570 name:kUserDoneEditingPrefsNotification
571 object:prefsController_.get()];
[email protected]767543d2009-04-30 19:23:58572 prefsController_.reset(NULL);
573}
574
[email protected]3111f08b2009-04-30 16:01:52575// Show the preferences window, or bring it to the front if it's already
576// visible.
577- (IBAction)showPreferences:(id)sender {
[email protected]42404382009-04-30 17:59:24578 if (!prefsController_.get()) {
[email protected]42404382009-04-30 17:59:24579 prefsController_.reset([[PreferencesWindowController alloc]
[email protected]cd63ef62009-05-06 19:41:37580 initWithProfile:[self defaultProfile]]);
[email protected]767543d2009-04-30 19:23:58581 // Watch for a notification of when it goes away so that we can destroy
582 // the controller.
583 [[NSNotificationCenter defaultCenter]
584 addObserver:self
585 selector:@selector(prefsWindowClosed:)
586 name:kUserDoneEditingPrefsNotification
587 object:prefsController_.get()];
[email protected]42404382009-04-30 17:59:24588 }
589 [prefsController_ showPreferences:sender];
[email protected]3111f08b2009-04-30 16:01:52590}
591
[email protected]bde3dda2009-05-20 22:13:07592// Called when the about window is closed. We use this to release the
593// window controller.
594- (void)aboutWindowClosed:(NSNotification*)notify {
595 [[NSNotificationCenter defaultCenter]
596 removeObserver:self
597 name:kUserClosedAboutNotification
598 object:aboutController_.get()];
599 aboutController_.reset(NULL);
600}
601
602- (IBAction)orderFrontStandardAboutPanel:(id)sender {
603#if !defined(GOOGLE_CHROME_BUILD)
604 // If not branded behave like a generic Cocoa app.
605 [NSApp orderFrontStandardAboutPanel:sender];
606#else
607 // Otherwise bring up our special dialog (e.g. with an auto-update button).
608 if (!aboutController_) {
609 aboutController_.reset([[AboutWindowController alloc]
610 initWithWindowNibName:@"About"]);
611 if (!aboutController_) {
612 // If we get here something is wacky. I managed to do it when
613 // testing by explicitly forcing an auto-update to an older
614 // version then trying to open the about box again (missing
615 // nib). This shouldn't be possible in general but let's try
616 // hard to not do nothing.
617 [NSApp orderFrontStandardAboutPanel:sender];
618 return;
619 }
620 // Watch for a notification of when it goes away so that we can destroy
621 // the controller.
622 [[NSNotificationCenter defaultCenter]
623 addObserver:self
624 selector:@selector(aboutWindowClosed:)
625 name:kUserClosedAboutNotification
626 object:aboutController_.get()];
627 }
628 if (![[aboutController_ window] isVisible])
629 [[aboutController_ window] center];
630 [aboutController_ showWindow:self];
631#endif
632}
633
[email protected]1bcdb532009-01-16 17:47:57634@end