OLD | NEW |
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #import "chrome/browser/app_controller_mac.h" | 5 #import "chrome/browser/app_controller_mac.h" |
6 | 6 |
7 #include "app/l10n_util_mac.h" | 7 #include "app/l10n_util_mac.h" |
8 #include "base/command_line.h" | 8 #include "base/command_line.h" |
9 #include "base/mac_util.h" | 9 #include "base/mac_util.h" |
10 #include "base/message_loop.h" | 10 #include "base/message_loop.h" |
11 #include "base/sys_string_conversions.h" | 11 #include "base/sys_string_conversions.h" |
12 #include "chrome/app/chrome_dll_resource.h" | 12 #include "chrome/app/chrome_dll_resource.h" |
13 #include "chrome/browser/browser.h" | 13 #include "chrome/browser/browser.h" |
14 #include "chrome/browser/browser_init.h" | 14 #include "chrome/browser/browser_init.h" |
15 #include "chrome/browser/browser_list.h" | 15 #include "chrome/browser/browser_list.h" |
16 #include "chrome/browser/browser_process.h" | 16 #include "chrome/browser/browser_process.h" |
17 #include "chrome/browser/browser_shutdown.h" | 17 #include "chrome/browser/browser_shutdown.h" |
18 #include "chrome/browser/browser_window.h" | 18 #include "chrome/browser/browser_window.h" |
| 19 #import "chrome/browser/chrome_application_mac.h" |
19 #import "chrome/browser/cocoa/about_window_controller.h" | 20 #import "chrome/browser/cocoa/about_window_controller.h" |
20 #import "chrome/browser/cocoa/bookmark_menu_bridge.h" | 21 #import "chrome/browser/cocoa/bookmark_menu_bridge.h" |
21 #import "chrome/browser/cocoa/browser_window_cocoa.h" | 22 #import "chrome/browser/cocoa/browser_window_cocoa.h" |
22 #import "chrome/browser/cocoa/browser_window_controller.h" | 23 #import "chrome/browser/cocoa/browser_window_controller.h" |
23 #import "chrome/browser/cocoa/history_menu_bridge.h" | 24 #import "chrome/browser/cocoa/history_menu_bridge.h" |
24 #import "chrome/browser/cocoa/clear_browsing_data_controller.h" | 25 #import "chrome/browser/cocoa/clear_browsing_data_controller.h" |
25 #import "chrome/browser/cocoa/encoding_menu_controller_delegate_mac.h" | 26 #import "chrome/browser/cocoa/encoding_menu_controller_delegate_mac.h" |
26 #import "chrome/browser/cocoa/preferences_window_controller.h" | 27 #import "chrome/browser/cocoa/preferences_window_controller.h" |
27 #import "chrome/browser/cocoa/tab_strip_controller.h" | 28 #import "chrome/browser/cocoa/tab_strip_controller.h" |
28 #import "chrome/browser/cocoa/tab_window_controller.h" | 29 #import "chrome/browser/cocoa/tab_window_controller.h" |
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
137 BrowserWindowController* controller = | 138 BrowserWindowController* controller = |
138 (BrowserWindowController*)[window->GetNativeHandle() windowController]; | 139 (BrowserWindowController*)[window->GetNativeHandle() windowController]; |
139 | 140 |
140 if (![controller shouldCloseWithOpenPerTabSheets]) | 141 if (![controller shouldCloseWithOpenPerTabSheets]) |
141 return NO; | 142 return NO; |
142 } | 143 } |
143 | 144 |
144 return YES; | 145 return YES; |
145 } | 146 } |
146 | 147 |
147 // We do not use the normal application teardown process -- this function is | |
148 // not called by the system but by us in |quit:|. |NSTerminateLater| is not a | |
149 // return value that is supported by |quit:|. | |
150 - (NSApplicationTerminateReply)applicationShouldTerminate: | 148 - (NSApplicationTerminateReply)applicationShouldTerminate: |
151 (NSApplication *)sender { | 149 (NSApplication *)sender { |
152 // Do not quit if any per-tab sheets are open, as required by | 150 // Do not quit if any per-tab sheets are open, as required by |
153 // GTMWindowSheetController. | 151 // GTMWindowSheetController. |
154 if (![self shouldQuitWithOpenPerTabSheets]) | 152 if (![self shouldQuitWithOpenPerTabSheets]) |
155 return NSTerminateCancel; | 153 return NSTerminateCancel; |
156 | 154 |
157 // Check for in-progress downloads, and prompt the user if they really want to | 155 // Check for in-progress downloads, and prompt the user if they really want to |
158 // quit (and thus cancel the downloads). | 156 // quit (and thus cancel the downloads). |
159 if (![self shouldQuitWithInProgressDownloads]) | 157 if (![self shouldQuitWithInProgressDownloads]) |
160 return NSTerminateCancel; | 158 return NSTerminateCancel; |
161 | 159 |
162 return NSTerminateNow; | 160 return NSTerminateNow; |
163 } | 161 } |
164 | 162 |
165 // Called when the app is shutting down. Clean-up as appropriate. | 163 // Called when the app is shutting down. Clean-up as appropriate. |
166 - (void)applicationWillTerminate:(NSNotification *)aNotification { | 164 - (void)applicationWillTerminate:(NSNotification *)aNotification { |
167 DCHECK(!BrowserList::HasBrowserWithProfile([self defaultProfile])); | 165 NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager]; |
168 if (!BrowserList::HasBrowserWithProfile([self defaultProfile])) { | 166 [em removeEventHandlerForEventClass:kInternetEventClass |
169 // As we're shutting down, we need to nuke the TabRestoreService, which will | 167 andEventID:kAEGetURL]; |
170 // start the shutdown of the NavigationControllers and allow for proper | 168 [em removeEventHandlerForEventClass:'WWW!' |
171 // shutdown. If we don't do this chrome won't shutdown cleanly, and may end | 169 andEventID:'OURL']; |
172 // up crashing when some thread tries to use the IO thread (or another | 170 [em removeEventHandlerForEventClass:kCoreEventClass |
173 // thread) that is no longer valid. | 171 andEventID:kAEOpenDocuments]; |
174 [self defaultProfile]->ResetTabRestoreService(); | 172 |
175 } | 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(); |
176 | 183 |
177 [[NSNotificationCenter defaultCenter] removeObserver:self]; | 184 [[NSNotificationCenter defaultCenter] removeObserver:self]; |
178 } | 185 } |
179 | 186 |
| 187 - (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 |
180 // Helper routine to get the window controller if the key window is a tabbed | 199 // Helper routine to get the window controller if the key window is a tabbed |
181 // window, or nil if not. Examples of non-tabbed windows are "about" or | 200 // window, or nil if not. Examples of non-tabbed windows are "about" or |
182 // "preferences". | 201 // "preferences". |
183 - (TabWindowController*)keyWindowTabController { | 202 - (TabWindowController*)keyWindowTabController { |
184 NSWindowController* keyWindowController = | 203 NSWindowController* keyWindowController = |
185 [[[NSApplication sharedApplication] keyWindow] windowController]; | 204 [[[NSApplication sharedApplication] keyWindow] windowController]; |
186 if ([keyWindowController isKindOfClass:[TabWindowController class]]) | 205 if ([keyWindowController isKindOfClass:[TabWindowController class]]) |
187 return (TabWindowController*)keyWindowController; | 206 return (TabWindowController*)keyWindowController; |
188 | 207 |
189 return nil; | 208 return nil; |
(...skipping 183 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
373 | 392 |
374 // User wants to exit. | 393 // User wants to exit. |
375 return YES; | 394 return YES; |
376 } | 395 } |
377 } | 396 } |
378 | 397 |
379 // No profiles or active downloads found, okay to exit. | 398 // No profiles or active downloads found, okay to exit. |
380 return YES; | 399 return YES; |
381 } | 400 } |
382 | 401 |
383 // We can't use the standard terminate: method because it will abruptly exit | |
384 // the app and leave things on the stack in an unfinalized state. We need to | |
385 // post a quit message to our run loop so the stack can gracefully unwind. | |
386 - (IBAction)quit:(id)sender { | |
387 if ([self applicationShouldTerminate:NSApp] == NSTerminateCancel) | |
388 return; | |
389 | |
390 // TODO(pinkerton): | |
391 // since we have to roll it ourselves, ask the delegate (ourselves, really) | |
392 // if we should terminate. For example, we might not want to if the user | |
393 // has ongoing downloads or multiple windows/tabs open. However, this would | |
394 // require posting UI and may require spinning up another run loop to | |
395 // handle it. If it says to continue, post the quit message, otherwise | |
396 // go back to normal. | |
397 | |
398 NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager]; | |
399 [em removeEventHandlerForEventClass:kInternetEventClass | |
400 andEventID:kAEGetURL]; | |
401 [em removeEventHandlerForEventClass:'WWW!' | |
402 andEventID:'OURL']; | |
403 [em removeEventHandlerForEventClass:kCoreEventClass | |
404 andEventID:kAEOpenDocuments]; | |
405 | |
406 // TODO(pinkerton): Not sure where this should live, including it here | |
407 // causes all sorts of asserts from the open renderers. On Windows, it | |
408 // lives in Browser::OnWindowClosing, but that's not appropriate on Mac | |
409 // since we don't shut down when we reach zero windows. | |
410 // browser_shutdown::OnShutdownStarting(browser_shutdown::WINDOW_CLOSE); | |
411 | |
412 // Close all the windows. | |
413 BrowserList::CloseAllBrowsers(true); | |
414 | |
415 // Release the reference to the browser process. Once all the browsers get | |
416 // dealloc'd, it will stop the RunLoop and fall back into main(). | |
417 g_browser_process->ReleaseModule(); | |
418 } | |
419 | |
420 // Called to determine if we should enable the "restore tab" menu item. | 402 // Called to determine if we should enable the "restore tab" menu item. |
421 // Checks with the TabRestoreService to see if there's anything there to | 403 // Checks with the TabRestoreService to see if there's anything there to |
422 // restore and returns YES if so. | 404 // restore and returns YES if so. |
423 - (BOOL)canRestoreTab { | 405 - (BOOL)canRestoreTab { |
424 TabRestoreService* service = [self defaultProfile]->GetTabRestoreService(); | 406 TabRestoreService* service = [self defaultProfile]->GetTabRestoreService(); |
425 return service && !service->entries().empty(); | 407 return service && !service->entries().empty(); |
426 } | 408 } |
427 | 409 |
428 // Called to validate menu items when there are no key windows. All the | 410 // Called to validate menu items when there are no key windows. All the |
429 // items we care about have been set with the |commandDispatch:| action and | 411 // items we care about have been set with the |commandDispatch:| action and |
430 // a target of FirstResponder in IB. If it's not one of those, let it | 412 // a target of FirstResponder in IB. If it's not one of those, let it |
431 // continue up the responder chain to be handled elsewhere. We pull out the | 413 // continue up the responder chain to be handled elsewhere. We pull out the |
432 // tag as the cross-platform constant to differentiate and dispatch the | 414 // tag as the cross-platform constant to differentiate and dispatch the |
433 // various commands. | 415 // various commands. |
434 - (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item { | 416 - (BOOL)validateUserInterfaceItem:(id<NSValidatedUserInterfaceItem>)item { |
435 SEL action = [item action]; | 417 SEL action = [item action]; |
436 BOOL enable = NO; | 418 BOOL enable = NO; |
437 if (action == @selector(commandDispatch:)) { | 419 if (action == @selector(commandDispatch:)) { |
438 NSInteger tag = [item tag]; | 420 NSInteger tag = [item tag]; |
439 if (menuState_->SupportsCommand(tag)) { | 421 if (menuState_->SupportsCommand(tag)) { |
440 switch (tag) { | 422 switch (tag) { |
441 case IDC_RESTORE_TAB: | 423 case IDC_RESTORE_TAB: |
442 enable = [self canRestoreTab]; | 424 enable = [self canRestoreTab]; |
443 break; | 425 break; |
444 default: | 426 default: |
445 enable = menuState_->IsCommandEnabled(tag) ? YES : NO; | 427 enable = menuState_->IsCommandEnabled(tag) ? YES : NO; |
446 } | 428 } |
447 } | 429 } |
448 } else if (action == @selector(quit:)) { | 430 } else if (action == @selector(terminate:)) { |
449 enable = YES; | 431 enable = YES; |
450 } else if (action == @selector(showPreferences:)) { | 432 } else if (action == @selector(showPreferences:)) { |
451 enable = YES; | 433 enable = YES; |
452 } else if (action == @selector(orderFrontStandardAboutPanel:)) { | 434 } else if (action == @selector(orderFrontStandardAboutPanel:)) { |
453 enable = YES; | 435 enable = YES; |
454 } | 436 } |
455 return enable; | 437 return enable; |
456 } | 438 } |
457 | 439 |
458 // Called when the user picks a menu item when there are no key windows. Calls | 440 // Called when the user picks a menu item when there are no key windows. Calls |
(...skipping 229 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
688 action:@selector(commandDispatch:) | 670 action:@selector(commandDispatch:) |
689 keyEquivalent:@""] autorelease]; | 671 keyEquivalent:@""] autorelease]; |
690 [item setTarget:self]; | 672 [item setTarget:self]; |
691 [item setTag:IDC_NEW_INCOGNITO_WINDOW]; | 673 [item setTag:IDC_NEW_INCOGNITO_WINDOW]; |
692 [result addItem:item]; | 674 [result addItem:item]; |
693 | 675 |
694 return result; | 676 return result; |
695 } | 677 } |
696 | 678 |
697 @end | 679 @end |
OLD | NEW |