Chromium Code Reviews| Index: chrome/browser/app_controller_mac.mm |
| diff --git a/chrome/browser/app_controller_mac.mm b/chrome/browser/app_controller_mac.mm |
| index 22de42f0cc5840cf7293179a4161417939784489..aa5af1fe870a9c6e42e391d6ea1eb2e5f5d3c1c8 100644 |
| --- a/chrome/browser/app_controller_mac.mm |
| +++ b/chrome/browser/app_controller_mac.mm |
| @@ -27,6 +27,7 @@ |
| #import "chrome/browser/cocoa/browser_window_controller.h" |
| #import "chrome/browser/cocoa/bug_report_window_controller.h" |
| #import "chrome/browser/cocoa/clear_browsing_data_controller.h" |
| +#import "chrome/browser/cocoa/confirm_quit_panel_controller.h" |
| #import "chrome/browser/cocoa/encoding_menu_controller_delegate_mac.h" |
| #import "chrome/browser/cocoa/history_menu_bridge.h" |
| #import "chrome/browser/cocoa/import_settings_dialog.h" |
| @@ -242,6 +243,12 @@ void RecordLastRunAppBundlePath() { |
| // them in, but I'm not sure about UX; we'd also want to disable other things |
| // though.) http://crbug.com/40861 |
| + // Check if the user really wants to quit by employing the confirm-to-quit |
| + // mechanism. |
| + if (!browser_shutdown::IsTryingToQuit() && |
| + [self applicationShouldTerminate:app] != NSTerminateNow) |
| + return NO; |
| + |
| size_t num_browsers = BrowserList::size(); |
| // Give any print jobs in progress time to finish. |
| @@ -267,6 +274,92 @@ void RecordLastRunAppBundlePath() { |
| } |
| } |
| +- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)app { |
| + // Check if the experiment is enabled. |
|
viettrungluu
2010/11/02 17:28:13
Probably you should say which experiment.
|
| + const CommandLine* commandLine(CommandLine::ForCurrentProcess()); |
| + if (!commandLine->HasSwitch(switches::kEnableConfirmToQuit)) |
| + return NSTerminateNow; |
| + |
| + // If the application is going to terminate as the result of a Cmd+Q |
| + // invocation, use the special sauce to prevent accidental quitting. |
| + // http://dev.chromium.org/developers/design-documents/confirm-to-quit-experiment |
| + NSEvent* currentEvent = [app currentEvent]; |
| + if ([currentEvent type] == NSKeyDown) { |
| + ConfirmQuitPanelController* quitPanel = |
| + [[ConfirmQuitPanelController alloc] init]; // Releases self. |
| + // Show the info panel that explains what the user must to do confirm quit. |
| + [quitPanel showWindow:self]; |
| + |
| + // How long the user must hold down Cmd+Q to confirm the quit. |
| + const NSTimeInterval kTimeToConfirmQuit = 1.5; |
| + // Leeway between the |targetDate| and the current time that will confirm a |
| + // quit. |
| + const NSTimeInterval kTimeDeltaFuzzFactor = 1.0; |
| + // Duration of the window fade out animation. |
| + const NSTimeInterval kWindowFadeAnimationDuration = 0.2; |
| + |
| + // Spin a nested run loop until the |targetDate| is reached or a KeyUp event |
| + // is sent. |
| + NSDate* targetDate = |
| + [NSDate dateWithTimeIntervalSinceNow:kTimeToConfirmQuit]; |
| + BOOL willQuit = NO; |
| + NSEvent* nextEvent = nil; |
| + do { |
|
viettrungluu
2010/11/02 17:28:13
Generically, I'm kind of scared of this, though I
Robert Sesek
2010/11/02 20:05:58
Open new window: OK
Extension close window: OK
JS
|
| + // Dequeue events until a key up is received. |
| + nextEvent = [app nextEventMatchingMask:NSKeyUpMask |
| + untilDate:nil |
| + inMode:NSEventTrackingRunLoopMode |
| + dequeue:YES]; |
| + |
| + // Wait for the time expiry to happen. Once past the hold threshold, |
| + // commit to quitting and hide all the open windows. |
| + if (!willQuit) { |
| + NSDate* now = [NSDate date]; |
| + NSTimeInterval difference = [targetDate timeIntervalSinceDate:now]; |
| + if (difference < kTimeDeltaFuzzFactor) { |
| + willQuit = YES; |
| + |
| + // At this point, the quit has been confirmed and windows should all |
| + // fade out to convince the user to release the key combo to finalize |
| + // the quit. |
| + [NSAnimationContext beginGrouping]; |
| + [[NSAnimationContext currentContext] setDuration: |
| + kWindowFadeAnimationDuration]; |
| + for (NSWindow* aWindow in [app windows]) { |
| + // Windows that are set to animate and have a delegate do not |
| + // expect to be animated by other things and could result in an |
| + // invalid state. If a window is set up like so, just force the |
| + // alpha value to 0. Otherwise, animate all pretty and stuff. |
| + if (![[aWindow animationForKey:@"alphaValue"] delegate]) { |
| + [[aWindow animator] setAlphaValue:0.0]; |
| + } else { |
| + [aWindow setAlphaValue:0.0]; |
| + } |
| + } |
| + [NSAnimationContext endGrouping]; |
| + } |
| + } |
| + } while (!nextEvent); |
| + |
| + // The user has released the key combo. Discard any events (i.e. the |
| + // repeated KeyDown Cmd+Q). |
| + [app discardEventsMatchingMask:NSAnyEventMask beforeEvent:nextEvent]; |
| + if (willQuit) { |
| + // The user held down the combination long enough that quitting should |
| + // happen. |
| + return NSTerminateNow; |
| + } else { |
| + // Slowly fade the confirm window out in case the user doesn't |
| + // understand what they have to do to quit. |
| + [quitPanel dismissPanel]; |
| + return NSTerminateCancel; |
| + } |
| + } // if event type is KeyDown |
| + |
| + // Default case: terminate. |
| + return NSTerminateNow; |
| +} |
| + |
| // Called when the app is shutting down. Clean-up as appropriate. |
| - (void)applicationWillTerminate:(NSNotification*)aNotification { |
| NSAppleEventManager* em = [NSAppleEventManager sharedAppleEventManager]; |