Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(65)

Side by Side Diff: chrome/browser/chrome_browser_application_mac.mm

Issue 1520006: Mac: reform our shutdown routine. (Closed)
Patch Set: changed comment Created 10 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « chrome/browser/chrome_browser_application_mac.h ('k') | chrome/browser/js_modal_dialog_mac.mm » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2010 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/chrome_browser_application_mac.h" 5 #import "chrome/browser/chrome_browser_application_mac.h"
6 6
7 #import "base/histogram.h" 7 #import "base/histogram.h"
8 #import "base/logging.h" 8 #import "base/logging.h"
9 #import "base/scoped_nsobject.h" 9 #import "base/scoped_nsobject.h"
10 #import "base/sys_string_conversions.h" 10 #import "base/sys_string_conversions.h"
11 #import "chrome/app/breakpad_mac.h" 11 #import "chrome/app/breakpad_mac.h"
12 #import "chrome/browser/app_controller_mac.h"
12 #import "chrome/browser/cocoa/objc_method_swizzle.h" 13 #import "chrome/browser/cocoa/objc_method_swizzle.h"
13 14
14 // The implementation of NSExceptions break various assumptions in the 15 // The implementation of NSExceptions break various assumptions in the
15 // Chrome code. This category defines a replacement for 16 // Chrome code. This category defines a replacement for
16 // -initWithName:reason:userInfo: for purposes of forcing a break in 17 // -initWithName:reason:userInfo: for purposes of forcing a break in
17 // the debugger when an exception is raised. -raise sounds more 18 // the debugger when an exception is raised. -raise sounds more
18 // obvious to intercept, but it doesn't catch the original throw 19 // obvious to intercept, but it doesn't catch the original throw
19 // because the objc runtime doesn't use it. 20 // because the objc runtime doesn't use it.
20 @interface NSException (NSExceptionSwizzle) 21 @interface NSException (NSExceptionSwizzle)
21 - (id)chromeInitWithName:(NSString *)aName 22 - (id)chromeInitWithName:(NSString *)aName
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after
113 114
114 void RecordExceptionWithUma(NSException* exception) { 115 void RecordExceptionWithUma(NSException* exception) {
115 UMA_HISTOGRAM_ENUMERATION("OSX.NSException", 116 UMA_HISTOGRAM_ENUMERATION("OSX.NSException",
116 BinForException(exception), kUnknownNSException); 117 BinForException(exception), kUnknownNSException);
117 } 118 }
118 119
119 void Terminate() { 120 void Terminate() {
120 [NSApp terminate:nil]; 121 [NSApp terminate:nil];
121 } 122 }
122 123
124 void CancelTerminate() {
125 [NSApp cancelTerminate:nil];
126 }
127
123 } // namespace chrome_browser_application_mac 128 } // namespace chrome_browser_application_mac
124 129
125 namespace { 130 namespace {
126 131
127 // Helper to make it easy to get crash keys right. 132 // Helper to make it easy to get crash keys right.
128 // TODO(shess): Find a better home for this. app/breakpad_mac.h 133 // TODO(shess): Find a better home for this. app/breakpad_mac.h
129 // doesn't work. 134 // doesn't work.
130 class ScopedCrashKey { 135 class ScopedCrashKey {
131 public: 136 public:
132 ScopedCrashKey(NSString* key, NSString* value) 137 ScopedCrashKey(NSString* key, NSString* value)
(...skipping 21 matching lines...) Expand all
154 159
155 } // namespace 160 } // namespace
156 161
157 @implementation BrowserCrApplication 162 @implementation BrowserCrApplication
158 163
159 - init { 164 - init {
160 DCHECK(SwizzleNSExceptionInit()); 165 DCHECK(SwizzleNSExceptionInit());
161 return [super init]; 166 return [super init];
162 } 167 }
163 168
164 // -terminate: is the entry point for orderly "quit" operations in Cocoa. 169 ////////////////////////////////////////////////////////////////////////////////
165 // This includes the application menu's quit menu item and keyboard 170 // HISTORICAL COMMENT (by viettrungluu, from
166 // equivalent, the application's dock icon menu's quit menu item, "quit" (not 171 // http://codereview.chromium.org/1520006 with mild editing):
167 // "force quit") in the Activity Monitor, and quits triggered by user logout
168 // and system restart and shutdown.
169 // 172 //
170 // The default NSApplication -terminate: implementation will end the process 173 // A quick summary of the state of things (before the changes to shutdown):
171 // by calling exit(), and thus never leave the main run loop. This is
172 // unsuitable for Chrome's purposes. Chrome depends on leaving the main
173 // run loop to perform a proper orderly shutdown. This design is ingrained
174 // in the application and the assumptions that its code makes, and is
175 // entirely reasonable and works well on other platforms, but it's not
176 // compatible with the standard Cocoa quit sequence. Quits originated from
177 // within the application can be redirected to not use -terminate:, but
178 // quits from elsewhere cannot be.
179 // 174 //
180 // To allow the Cocoa-based Chrome to support the standard Cocoa -terminate: 175 // Currently, we are totally hosed (put in a bad state in which Cmd-W does the
181 // interface, and allow all quits to cause Chrome to shut down properly 176 // wrong thing, and which will probably eventually lead to a crash) if we begin
182 // regardless of their origin, -terminate: is overriden. The custom 177 // quitting but termination is aborted for some reason.
183 // -terminate: does not end the application with exit(). Instead, it simply
184 // returns after posting the normal NSApplicationWillTerminateNotification
185 // notification. The application is responsible for exiting on its own in
186 // whatever way it deems appropriate. In Chrome's case, the main run loop will
187 // end and the applicaton will exit by returning from main().
188 // 178 //
189 // This implementation of -terminate: is scaled back and is not as 179 // I currently know of two ways in which termination can be aborted:
190 // fully-featured as the implementation in NSApplication, nor is it a direct 180 // (1) Common case: a window has an onbeforeunload handler which pops up a
191 // drop-in replacement -terminate: in most applications. It is 181 // "leave web page" dialog, and the user answers "no, don't leave".
192 // purpose-specific to Chrome. 182 // (2) Uncommon case: popups are enabled (in Content Settings, i.e., the popup
183 // blocker is disabled), and some nasty web page pops up a new window on
184 // closure.
185 //
186 // I don't know of other ways in which termination can be aborted, but they may
187 // exist (or may be added in the future, for that matter).
188 //
189 // My CL [see above] does the following:
190 // a. Should prevent being put in a bad state (which breaks Cmd-W and leads to
191 // crash) under all circumstances.
192 // b. Should completely handle (1) properly.
193 // c. Doesn't (yet) handle (2) properly and puts it in a weird state (but not
194 // that bad).
195 // d. Any other ways of aborting termination would put it in that weird state.
196 //
197 // c. can be fixed by having the global flag reset on browser creation or
198 // similar (and doing so might also fix some possible d.'s as well). I haven't
199 // done this yet since I haven't thought about it carefully and since it's a
200 // corner case.
201 //
202 // The weird state: a state in which closing the last window quits the browser.
203 // This might be a bit annoying, but it's not dangerous in any way.
204 ////////////////////////////////////////////////////////////////////////////////
205
206 // |-terminate:| is the entry point for orderly "quit" operations in Cocoa. This
207 // includes the application menu's quit menu item and keyboard equivalent, the
208 // application's dock icon menu's quit menu item, "quit" (not "force quit") in
209 // the Activity Monitor, and quits triggered by user logout and system restart
210 // and shutdown.
211 //
212 // The default |-terminate:| implementation ends the process by calling exit(),
213 // and thus never leaves the main run loop. This is unsuitable for Chrome since
214 // Chrome depends on leaving the main run loop to perform an orderly shutdown.
215 // We support the normal |-terminate:| interface by overriding the default
216 // implementation. Our implementation, which is very specific to the needs of
217 // Chrome, works by asking the application delegate to terminate using its
218 // |-tryToTerminateApplication:| method.
219 //
220 // |-tryToTerminateApplication:| differs from the standard
221 // |-applicationShouldTerminate:| in that no special event loop is run in the
222 // case that immediate termination is not possible (e.g., if dialog boxes
223 // allowing the user to cancel have to be shown). Instead, this method sets a
224 // flag and tries to close all browsers. This flag causes the closure of the
225 // final browser window to begin actual tear-down of the application.
226 // Termination is cancelled by resetting this flag. The standard
227 // |-applicationShouldTerminate:| is not supported, and code paths leading to it
228 // must be redirected.
193 - (void)terminate:(id)sender { 229 - (void)terminate:(id)sender {
194 NSApplicationTerminateReply shouldTerminate = NSTerminateNow; 230 AppController* appController = static_cast<AppController*>([NSApp delegate]);
195 SEL selector = @selector(applicationShouldTerminate:); 231 if ([appController tryToTerminateApplication:self]) {
196 if ([[self delegate] respondsToSelector:selector]) 232 [[NSNotificationCenter defaultCenter]
197 shouldTerminate = [[self delegate] applicationShouldTerminate:self]; 233 postNotificationName:NSApplicationWillTerminateNotification
234 object:self];
235 }
198 236
199 // If shouldTerminate is NSTerminateLater, the application is expected to 237 // Return, don't exit. The application is responsible for exiting on its own.
200 // call -replyToApplicationShouldTerminate: when it knows whether or not it 238 }
201 // should terminate. If the argument is YES,
202 // -replyToApplicationShouldTerminate: will call -terminate:. This will
203 // result in another call to the delegate's -applicationShouldTerminate:,
204 // which would be expected to return NSTerminateNow at that point.
205 if (shouldTerminate != NSTerminateNow)
206 return;
207 239
208 [[NSNotificationCenter defaultCenter] 240 - (void)cancelTerminate:(id)sender {
209 postNotificationName:NSApplicationWillTerminateNotification 241 AppController* appController = static_cast<AppController*>([NSApp delegate]);
210 object:self]; 242 [appController stopTryingToTerminateApplication:self];
211
212 // Return, don't exit. The application is responsible for exiting on its
213 // own.
214 } 243 }
215 244
216 - (BOOL)sendAction:(SEL)anAction to:(id)aTarget from:(id)sender { 245 - (BOOL)sendAction:(SEL)anAction to:(id)aTarget from:(id)sender {
217 // The Dock menu contains an automagic section where you can select 246 // The Dock menu contains an automagic section where you can select
218 // amongst open windows. If a window is closed via JavaScript while 247 // amongst open windows. If a window is closed via JavaScript while
219 // the menu is up, the menu item for that window continues to exist. 248 // the menu is up, the menu item for that window continues to exist.
220 // When a window is selected this method is called with the 249 // When a window is selected this method is called with the
221 // now-freed window as |aTarget|. Short-circuit the call if 250 // now-freed window as |aTarget|. Short-circuit the call if
222 // |aTarget| is not a valid window. 251 // |aTarget| is not a valid window.
223 if (anAction == @selector(_selectWindow:)) { 252 if (anAction == @selector(_selectWindow:)) {
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after
302 SetCrashKeyValue(kLastExceptionKey, value); 331 SetCrashKeyValue(kLastExceptionKey, value);
303 } 332 }
304 333
305 reportingException = NO; 334 reportingException = NO;
306 } 335 }
307 336
308 [super reportException:anException]; 337 [super reportException:anException];
309 } 338 }
310 339
311 @end 340 @end
OLDNEW
« no previous file with comments | « chrome/browser/chrome_browser_application_mac.h ('k') | chrome/browser/js_modal_dialog_mac.mm » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698