blob: 74667c72ca738fe6e762fd5dedf25b3fca73ae99 [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]57750f822009-04-21 21:43:097#include "base/command_line.h"
[email protected]2c47bc12009-04-10 20:14:008#include "base/message_loop.h"
[email protected]57750f822009-04-21 21:43:099#include "base/sys_string_conversions.h"
[email protected]2c47bc12009-04-10 20:14:0010#include "chrome/app/chrome_dll_resource.h"
11#include "chrome/browser/browser.h"
[email protected]57750f822009-04-21 21:43:0912#include "chrome/browser/browser_init.h"
[email protected]2c47bc12009-04-10 20:14:0013#include "chrome/browser/browser_list.h"
[email protected]41741a962009-02-18 21:51:3914#include "chrome/browser/browser_shutdown.h"
[email protected]bde3dda2009-05-20 22:13:0715#import "chrome/browser/cocoa/about_window_controller.h"
[email protected]3f34599d2009-03-25 22:11:4316#import "chrome/browser/cocoa/bookmark_menu_bridge.h"
[email protected]2bcec612009-05-14 17:50:5317#import "chrome/browser/cocoa/encoding_menu_controller_delegate_mac.h"
[email protected]7b225242009-05-20 17:59:3418#import "chrome/browser/cocoa/menu_localizer.h"
[email protected]42404382009-04-30 17:59:2419#import "chrome/browser/cocoa/preferences_window_controller.h"
[email protected]2c47bc12009-04-10 20:14:0020#include "chrome/browser/command_updater.h"
[email protected]cd63ef62009-05-06 19:41:3721#include "chrome/common/pref_names.h"
22#include "chrome/common/pref_service.h"
[email protected]2c47bc12009-04-10 20:14:0023#include "chrome/browser/profile_manager.h"
24#include "chrome/common/temp_scaffolding_stubs.h"
[email protected]88d74942009-01-21 22:04:4425
26@interface AppController(PRIVATE)
27- (void)initMenuState;
[email protected]136140c2009-05-19 13:58:2528- (void)openURLs:(const std::vector<GURL>&)urls;
29- (void)openPendingURLs;
[email protected]57750f822009-04-21 21:43:0930- (void)getUrl:(NSAppleEventDescriptor*)event
31 withReply:(NSAppleEventDescriptor*)reply;
[email protected]36fe18f2009-04-29 20:26:2032- (void)openFiles:(NSAppleEventDescriptor*)event
33 withReply:(NSAppleEventDescriptor*)reply;
[email protected]88d74942009-01-21 22:04:4434@end
[email protected]1bcdb532009-01-16 17:47:5735
36@implementation AppController
37
[email protected]cd63ef62009-05-06 19:41:3738// This method is called very early in application startup (ie, before
39// the profile is loaded or any preferences have been registered). Defer any
40// user-data initialization until -applicationDidFinishLaunching:.
[email protected]88d74942009-01-21 22:04:4441- (void)awakeFromNib {
[email protected]136140c2009-05-19 13:58:2542 pendingURLs_.reset(new std::vector<GURL>());
[email protected]88d74942009-01-21 22:04:4443
[email protected]136140c2009-05-19 13:58:2544 // We need to register the handlers early to catch events fired on launch.
[email protected]57750f822009-04-21 21:43:0945 NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager];
46 [em setEventHandler:self
47 andSelector:@selector(getUrl:withReply:)
48 forEventClass:kInternetEventClass
49 andEventID:kAEGetURL];
50 [em setEventHandler:self
51 andSelector:@selector(getUrl:withReply:)
52 forEventClass:'WWW!' // A particularly ancient AppleEvent that dates
53 andEventID:'OURL']; // back to the Spyglass days.
[email protected]36fe18f2009-04-29 20:26:2054 [em setEventHandler:self
55 andSelector:@selector(openFiles:withReply:)
56 forEventClass:kCoreEventClass
57 andEventID:kAEOpenDocuments];
[email protected]cd63ef62009-05-06 19:41:3758
[email protected]136140c2009-05-19 13:58:2559 // Set up the command updater for when there are no windows open
60 [self initMenuState];
61}
62
63// This is called after profiles have been loaded and preferences registered.
64// It is safe to access the default profile here.
65- (void)applicationDidFinishLaunching:(NSNotification*)notify {
66 // Hold an extra ref to the BrowserProcess singleton so it doesn't go away
67 // when all the browser windows get closed. We'll release it on quit which
68 // will be the signal to exit.
69 DCHECK(g_browser_process);
70 g_browser_process->AddRefModule();
71
[email protected]7b225242009-05-20 17:59:3472 // Create the localizer for the main menu. We can't do this in the nib
73 // because it's too early. Do it before we create any bookmark menus as well,
74 // just in case one has a title that matches any of our strings (unlikely,
75 // but technically possible).
76 scoped_nsobject<MenuLocalizer> localizer(
77 [[MenuLocalizer alloc] initWithBundle:nil]);
78 [localizer localizeObject:[NSApplication sharedApplication]
79 recursively:YES];
80
[email protected]cd63ef62009-05-06 19:41:3781 bookmarkMenuBridge_.reset(new BookmarkMenuBridge());
82
83 // Register any Mac-specific preferences.
84 PrefService* prefs = [self defaultProfile]->GetPrefs();
85 prefs->RegisterBooleanPref(prefs::kShowPageOptionsButtons, false);
[email protected]2bcec612009-05-14 17:50:5386
87 // Build up the encoding menu, the order of the items differs based on the
88 // current locale (see http://crbug.com/7647 for details).
89 // We need a valid g_browser_process to get the profile which is why we can't
90 // call this from awakeFromNib.
91 EncodingMenuControllerDelegate::BuildEncodingMenu([self defaultProfile]);
92
[email protected]136140c2009-05-19 13:58:2593 // Now that we're initialized we can open any URLs we've been holding onto.
94 [self openPendingURLs];
[email protected]7c321082009-02-09 15:35:4795}
96
[email protected]983f0fc82009-01-27 16:28:4497// We can't use the standard terminate: method because it will abruptly exit
[email protected]1bcdb532009-01-16 17:47:5798// the app and leave things on the stack in an unfinalized state. We need to
99// post a quit message to our run loop so the stack can gracefully unwind.
[email protected]f0a51fb52009-03-05 12:46:38100- (IBAction)quit:(id)sender {
[email protected]1bcdb532009-01-16 17:47:57101 // TODO(pinkerton):
102 // since we have to roll it ourselves, ask the delegate (ourselves, really)
103 // if we should terminate. For example, we might not want to if the user
104 // has ongoing downloads or multiple windows/tabs open. However, this would
[email protected]f0a51fb52009-03-05 12:46:38105 // require posting UI and may require spinning up another run loop to
[email protected]1bcdb532009-01-16 17:47:57106 // handle it. If it says to continue, post the quit message, otherwise
107 // go back to normal.
[email protected]8a53ee042009-01-21 16:41:33108
[email protected]57750f822009-04-21 21:43:09109 NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager];
110 [em removeEventHandlerForEventClass:kInternetEventClass
111 andEventID:kAEGetURL];
112 [em removeEventHandlerForEventClass:'WWW!'
113 andEventID:'OURL'];
[email protected]36fe18f2009-04-29 20:26:20114 [em removeEventHandlerForEventClass:kCoreEventClass
115 andEventID:kAEOpenDocuments];
[email protected]57750f822009-04-21 21:43:09116
[email protected]41741a962009-02-18 21:51:39117 // TODO(pinkerton): Not sure where this should live, including it here
118 // causes all sorts of asserts from the open renderers. On Windows, it
119 // lives in Browser::OnWindowClosing, but that's not appropriate on Mac
120 // since we don't shut down when we reach zero windows.
121 // browser_shutdown::OnShutdownStarting(browser_shutdown::WINDOW_CLOSE);
122
[email protected]8a53ee042009-01-21 16:41:33123 // Close all the windows.
[email protected]7c321082009-02-09 15:35:47124 BrowserList::CloseAllBrowsers(true);
[email protected]f0a51fb52009-03-05 12:46:38125
[email protected]7c321082009-02-09 15:35:47126 // Release the reference to the browser process. Once all the browsers get
127 // dealloc'd, it will stop the RunLoop and fall back into main().
128 g_browser_process->ReleaseModule();
[email protected]1bcdb532009-01-16 17:47:57129}
130
[email protected]88d74942009-01-21 22:04:44131// Called to validate menu items when there are no key windows. All the
132// items we care about have been set with the |commandDispatch:| action and
133// a target of FirstResponder in IB. If it's not one of those, let it
134// continue up the responder chain to be handled elsewhere. We pull out the
135// tag as the cross-platform constant to differentiate and dispatch the
136// various commands.
137- (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item {
138 SEL action = [item action];
139 BOOL enable = NO;
140 if (action == @selector(commandDispatch:)) {
141 NSInteger tag = [item tag];
142 if (menuState_->SupportsCommand(tag))
143 enable = menuState_->IsCommandEnabled(tag) ? YES : NO;
144 } else if (action == @selector(quit:)) {
145 enable = YES;
[email protected]3111f08b2009-04-30 16:01:52146 } else if (action == @selector(showPreferences:)) {
147 enable = YES;
[email protected]bde3dda2009-05-20 22:13:07148 } else if (action == @selector(orderFrontStandardAboutPanel:)) {
149 enable = YES;
[email protected]88d74942009-01-21 22:04:44150 }
151 return enable;
152}
153
154// Called when the user picks a menu item when there are no key windows. Calls
155// through to the browser object to execute the command. This assumes that the
156// command is supported and doesn't check, otherwise it would have been disabled
157// in the UI in validateUserInterfaceItem:.
158- (void)commandDispatch:(id)sender {
[email protected]3f34599d2009-03-25 22:11:43159 Profile* default_profile = [self defaultProfile];
[email protected]f0a51fb52009-03-05 12:46:38160
[email protected]88d74942009-01-21 22:04:44161 NSInteger tag = [sender tag];
162 switch (tag) {
163 case IDC_NEW_WINDOW:
[email protected]f7011fcb2009-01-28 21:54:32164 Browser::OpenEmptyWindow(default_profile);
[email protected]88d74942009-01-21 22:04:44165 break;
[email protected]863ff662009-01-26 20:18:18166 case IDC_NEW_INCOGNITO_WINDOW:
[email protected]f7011fcb2009-01-28 21:54:32167 Browser::OpenURLOffTheRecord(default_profile, GURL());
[email protected]863ff662009-01-26 20:18:18168 break;
[email protected]e19516d2009-04-28 17:15:19169 case IDC_OPEN_FILE:
170 Browser::OpenEmptyWindow(default_profile);
171 BrowserList::GetLastActive()->
172 ExecuteCommandWithDisposition(IDC_OPEN_FILE, CURRENT_TAB);
173 break;
[email protected]88d74942009-01-21 22:04:44174 };
175}
176
[email protected]ff81e0a2009-04-13 14:58:54177// NSApplication delegate method called when someone clicks on the
178// dock icon and there are no open windows. To match standard mac
179// behavior, we should open a new window.
180- (BOOL)applicationShouldHandleReopen:(NSApplication*)theApplication
181 hasVisibleWindows:(BOOL)flag {
182 // Don't do anything if there are visible windows. This will cause
183 // AppKit to unminimize the most recently minimized window.
184 if (flag)
185 return YES;
186
187 // Otherwise open a new window.
188 Browser::OpenEmptyWindow([self defaultProfile]);
189
190 // We've handled the reopen event, so return NO to tell AppKit not
191 // to do anything.
192 return NO;
193}
194
[email protected]88d74942009-01-21 22:04:44195- (void)initMenuState {
[email protected]3111f08b2009-04-30 16:01:52196 menuState_.reset(new CommandUpdater(NULL));
[email protected]88d74942009-01-21 22:04:44197 menuState_->UpdateCommandEnabled(IDC_NEW_WINDOW, true);
[email protected]863ff662009-01-26 20:18:18198 menuState_->UpdateCommandEnabled(IDC_NEW_INCOGNITO_WINDOW, true);
[email protected]e19516d2009-04-28 17:15:19199 menuState_->UpdateCommandEnabled(IDC_OPEN_FILE, true);
[email protected]88d74942009-01-21 22:04:44200 // TODO(pinkerton): ...more to come...
201}
[email protected]1bcdb532009-01-16 17:47:57202
[email protected]3f34599d2009-03-25 22:11:43203- (Profile*)defaultProfile {
[email protected]3f34599d2009-03-25 22:11:43204 // TODO(jrg): Find a better way to get the "default" profile.
205 if (g_browser_process->profile_manager())
[email protected]57750f822009-04-21 21:43:09206 return* g_browser_process->profile_manager()->begin();
[email protected]3f34599d2009-03-25 22:11:43207
[email protected]f6314002009-04-23 01:18:13208 return NULL;
[email protected]3f34599d2009-03-25 22:11:43209}
210
[email protected]57750f822009-04-21 21:43:09211// Various methods to open URLs that we get in a native fashion. We use
212// BrowserInit here because on the other platforms, URLs to open come through
213// the ProcessSingleton, and it calls BrowserInit. It's best to bottleneck the
214// openings through that for uniform handling.
215
[email protected]136140c2009-05-19 13:58:25216- (void)openURLs:(const std::vector<GURL>&)urls {
217 if (pendingURLs_.get()) {
218 // too early to open; save for later
219 pendingURLs_->insert(pendingURLs_->end(), urls.begin(), urls.end());
220 return;
221 }
[email protected]57750f822009-04-21 21:43:09222
[email protected]57750f822009-04-21 21:43:09223 CommandLine dummy((std::wstring()));
224 BrowserInit::LaunchWithProfile launch(std::wstring(), dummy);
225 launch.OpenURLsInBrowser(BrowserList::GetLastActive(), false, urls);
226}
227
[email protected]136140c2009-05-19 13:58:25228- (void)openPendingURLs {
229 // Since the existence of pendingURLs_ is a flag that it's too early to
230 // open URLs, we need to reset pendingURLs_.
231 std::vector<GURL> urls;
232 swap(urls, *pendingURLs_);
233 pendingURLs_.reset();
234
235 if (urls.size())
236 [self openURLs:urls];
237}
[email protected]57750f822009-04-21 21:43:09238
239- (void)getUrl:(NSAppleEventDescriptor*)event
240 withReply:(NSAppleEventDescriptor*)reply {
241 NSString* urlStr = [[event paramDescriptorForKeyword:keyDirectObject]
242 stringValue];
243
244 GURL gurl(base::SysNSStringToUTF8(urlStr));
245 std::vector<GURL> gurlVector;
246 gurlVector.push_back(gurl);
247
[email protected]136140c2009-05-19 13:58:25248 [self openURLs:gurlVector];
[email protected]57750f822009-04-21 21:43:09249}
250
[email protected]36fe18f2009-04-29 20:26:20251- (void)openFiles:(NSAppleEventDescriptor*)event
252 withReply:(NSAppleEventDescriptor*)reply {
253 // Ordinarily we'd use the NSApplication delegate method
254 // -application:openFiles:, but Cocoa tries to be smart and it sends files
255 // specified on the command line into that delegate method. That's too smart
256 // for us (our setup isn't done by the time Cocoa triggers the delegate method
257 // and we crash). Since all we want are files dropped on the app icon, and we
258 // have cross-platform code to handle the command-line files anyway, an Apple
259 // Event handler fits the bill just right.
260 NSAppleEventDescriptor* fileList =
261 [event paramDescriptorForKeyword:keyDirectObject];
262 if (!fileList)
263 return;
[email protected]57750f822009-04-21 21:43:09264 std::vector<GURL> gurlVector;
265
[email protected]36fe18f2009-04-29 20:26:20266 for (NSInteger i = 1; i <= [fileList numberOfItems]; ++i) {
267 NSAppleEventDescriptor* fileAliasDesc = [fileList descriptorAtIndex:i];
268 if (!fileAliasDesc)
269 continue;
270 NSAppleEventDescriptor* fileURLDesc =
271 [fileAliasDesc coerceToDescriptorType:typeFileURL];
272 if (!fileURLDesc)
273 continue;
274 NSData* fileURLData = [fileURLDesc data];
275 if (!fileURLData)
276 continue;
277 GURL gurl(std::string((char*)[fileURLData bytes], [fileURLData length]));
[email protected]57750f822009-04-21 21:43:09278 gurlVector.push_back(gurl);
279 }
280
[email protected]136140c2009-05-19 13:58:25281 [self openURLs:gurlVector];
[email protected]57750f822009-04-21 21:43:09282}
[email protected]3f34599d2009-03-25 22:11:43283
[email protected]767543d2009-04-30 19:23:58284// Called when the preferences window is closed. We use this to release the
285// window controller.
286- (void)prefsWindowClosed:(NSNotification*)notify {
[email protected]bde3dda2009-05-20 22:13:07287 [[NSNotificationCenter defaultCenter]
288 removeObserver:self
289 name:kUserDoneEditingPrefsNotification
290 object:prefsController_.get()];
[email protected]767543d2009-04-30 19:23:58291 prefsController_.reset(NULL);
292}
293
[email protected]3111f08b2009-04-30 16:01:52294// Show the preferences window, or bring it to the front if it's already
295// visible.
296- (IBAction)showPreferences:(id)sender {
[email protected]42404382009-04-30 17:59:24297 if (!prefsController_.get()) {
[email protected]42404382009-04-30 17:59:24298 prefsController_.reset([[PreferencesWindowController alloc]
[email protected]cd63ef62009-05-06 19:41:37299 initWithProfile:[self defaultProfile]]);
[email protected]767543d2009-04-30 19:23:58300 // Watch for a notification of when it goes away so that we can destroy
301 // the controller.
302 [[NSNotificationCenter defaultCenter]
303 addObserver:self
304 selector:@selector(prefsWindowClosed:)
305 name:kUserDoneEditingPrefsNotification
306 object:prefsController_.get()];
[email protected]42404382009-04-30 17:59:24307 }
308 [prefsController_ showPreferences:sender];
[email protected]3111f08b2009-04-30 16:01:52309}
310
[email protected]bde3dda2009-05-20 22:13:07311// Called when the about window is closed. We use this to release the
312// window controller.
313- (void)aboutWindowClosed:(NSNotification*)notify {
314 [[NSNotificationCenter defaultCenter]
315 removeObserver:self
316 name:kUserClosedAboutNotification
317 object:aboutController_.get()];
318 aboutController_.reset(NULL);
319}
320
321- (IBAction)orderFrontStandardAboutPanel:(id)sender {
322#if !defined(GOOGLE_CHROME_BUILD)
323 // If not branded behave like a generic Cocoa app.
324 [NSApp orderFrontStandardAboutPanel:sender];
325#else
326 // Otherwise bring up our special dialog (e.g. with an auto-update button).
327 if (!aboutController_) {
328 aboutController_.reset([[AboutWindowController alloc]
329 initWithWindowNibName:@"About"]);
330 if (!aboutController_) {
331 // If we get here something is wacky. I managed to do it when
332 // testing by explicitly forcing an auto-update to an older
333 // version then trying to open the about box again (missing
334 // nib). This shouldn't be possible in general but let's try
335 // hard to not do nothing.
336 [NSApp orderFrontStandardAboutPanel:sender];
337 return;
338 }
339 // Watch for a notification of when it goes away so that we can destroy
340 // the controller.
341 [[NSNotificationCenter defaultCenter]
342 addObserver:self
343 selector:@selector(aboutWindowClosed:)
344 name:kUserClosedAboutNotification
345 object:aboutController_.get()];
346 }
347 if (![[aboutController_ window] isVisible])
348 [[aboutController_ window] center];
349 [aboutController_ showWindow:self];
350#endif
351}
352
[email protected]1bcdb532009-01-16 17:47:57353@end