BUG=65795

Review URL: http://codereview.chromium.org/5758001

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@68804 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/app_controller_mac.mm b/chrome/browser/app_controller_mac.mm
index 3b43753..184b1969 100644
--- a/chrome/browser/app_controller_mac.mm
+++ b/chrome/browser/app_controller_mac.mm
@@ -293,76 +293,92 @@
   // 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) {
-    // Show the info panel that explains what the user must to do confirm quit.
-    [[ConfirmQuitPanelController sharedController] 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;
+  // 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 {
-      // Dequeue events until a key up is received.
-      nextEvent = [app nextEventMatchingMask:NSKeyUpMask
-                                   untilDate:nil
-                                      inMode:NSEventTrackingRunLoopMode
-                                     dequeue:YES];
+  // This logic is only for keyboard-initiated quits.
+  if ([[app currentEvent] type] != NSKeyDown)
+    return NSTerminateNow;
 
-      // 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;
+  // If this is the second of two such attempts to quit within a certain time
+  // interval, then just quit.
+  // Time of last quit attempt, if any.
+  static NSDate* lastQuitAttempt; // Initially nil, as it's static.
+  NSDate* timeNow = [NSDate date];
+  if (lastQuitAttempt &&
+      [timeNow timeIntervalSinceDate:lastQuitAttempt] < kTimeDeltaFuzzFactor) {
+    return NSTerminateNow;
+  } else {
+    [lastQuitAttempt release]; // Harmless if already nil.
+    lastQuitAttempt = [timeNow retain]; // Record this attempt for next time.
+  }
 
-          // 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];
-            }
+  // Show the info panel that explains what the user must to do confirm quit.
+  [[ConfirmQuitPanelController sharedController] showWindow:self];
+
+  // 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 {
+    // 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];
         }
+        [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.
-      [[ConfirmQuitPanelController sharedController] dismissPanel];
-      return NSTerminateCancel;
     }
-  }  // if event type is KeyDown
+  } 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.
+    [[ConfirmQuitPanelController sharedController] dismissPanel];
+    return NSTerminateCancel;
+  }
 
   // Default case: terminate.
   return NSTerminateNow;