Bug 636374 - Don't show multiple onbeforeunload prompts. r=bz, a=lsblakk
authorGijs Kruitbosch <gijskruitbosch@gmail.com>
Wed, 18 Dec 2013 18:36:12 +0000
changeset 175789 0032a5a180ff737ecd9a704f00cd71b79fef7b13
parent 175788 23e2f7e842115534bcae7b18bdaa04bfe33859e7
child 175790 31db6a06fc521b0558d0cc834604f409764c7493
push id445
push userffxbld
push dateMon, 10 Mar 2014 22:05:19 +0000
treeherdermozilla-release@dc38b741b04e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz, lsblakk
bugs636374
milestone28.0a2
Bug 636374 - Don't show multiple onbeforeunload prompts. r=bz, a=lsblakk
docshell/base/nsIContentViewer.idl
layout/base/nsDocumentViewer.cpp
--- a/docshell/base/nsIContentViewer.idl
+++ b/docshell/base/nsIContentViewer.idl
@@ -23,17 +23,17 @@ class nsDOMNavigationTiming;
 [ptr] native nsIWidgetPtr(nsIWidget);
 [ptr] native nsIDocumentPtr(nsIDocument);
 [ref] native nsIntRectRef(nsIntRect);
 [ptr] native nsIPresShellPtr(nsIPresShell);
 [ptr] native nsPresContextPtr(nsPresContext);
 [ptr] native nsViewPtr(nsView);
 [ptr] native nsDOMNavigationTimingPtr(nsDOMNavigationTiming);
 
-[scriptable, builtinclass, uuid(b9d92b8b-5623-4079-ae11-36bb341f322e)]
+[scriptable, builtinclass, uuid(1b22be51-efe8-42ac-a9a0-06f50f39beee)]
 interface nsIContentViewer : nsISupports
 {
 
   [noscript] void init(in nsIWidgetPtr aParentWidget,
                        [const] in nsIntRectRef aBounds);
 
   attribute nsIDocShell container;
 
@@ -46,16 +46,24 @@ interface nsIContentViewer : nsISupports
    *
    * @param aCallerClosesWindow indicates that the current caller will close the
    *        window. If the method returns true, all subsequent calls will be
    *        ignored.
    */
   boolean permitUnload([optional] in boolean aCallerClosesWindow);
 
   /**
+   * As above, but this passes around the aShouldPrompt argument to keep
+   * track of whether the user has responded to a prompt.
+   * Used internally by the scriptable version to ensure we only prompt once.
+   */
+  [noscript,nostdcall] boolean permitUnloadInternal(in boolean aCallerClosesWindow,
+                                                    inout boolean aShouldPrompt);
+
+  /**
    * Works in tandem with permitUnload, if the caller decides not to close the
    * window it indicated it will, it is the caller's responsibility to reset
    * that with this method.
    *
    * @Note this method is only meant to be called on documents for which the
    *  caller has indicated that it will close the window. If that is not the case
    *  the behavior of this method is undefined.
    */
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -1059,17 +1059,29 @@ nsDocumentViewer::LoadComplete(nsresult 
     mCachedPrintWebProgressListner = nullptr;
   }
 #endif
 
   return rv;
 }
 
 NS_IMETHODIMP
-nsDocumentViewer::PermitUnload(bool aCallerClosesWindow, bool *aPermitUnload)
+nsDocumentViewer::PermitUnload(bool aCallerClosesWindow,
+                               bool *aPermitUnload)
+{
+  bool shouldPrompt = true;
+  return PermitUnloadInternal(aCallerClosesWindow, &shouldPrompt,
+                              aPermitUnload);
+}
+
+
+nsresult
+nsDocumentViewer::PermitUnloadInternal(bool aCallerClosesWindow,
+                                       bool *aShouldPrompt,
+                                       bool *aPermitUnload)
 {
   *aPermitUnload = true;
 
   if (!mDocument
    || mInPermitUnload
    || mCallerIsClosingWindow
    || mInPermitUnloadPrompt) {
     return NS_OK;
@@ -1124,18 +1136,18 @@ nsDocumentViewer::PermitUnload(bool aCal
     if (dialogsWereEnabled) {
       utils->EnableDialogs();
     }
   }
 
   nsCOMPtr<nsIDocShellTreeNode> docShellNode(mContainer);
   nsAutoString text;
   beforeUnload->GetReturnValue(text);
-  if (event->GetInternalNSEvent()->mFlags.mDefaultPrevented ||
-      !text.IsEmpty()) {
+  if (*aShouldPrompt && (event->GetInternalNSEvent()->mFlags.mDefaultPrevented ||
+                         !text.IsEmpty())) {
     // Ask the user if it's ok to unload the current page
 
     nsCOMPtr<nsIPrompt> prompt = do_GetInterface(docShellNode);
 
     if (prompt) {
       nsXPIDLString title, message, stayLabel, leaveLabel;
       rv  = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
                                                "OnBeforeUnloadTitle",
@@ -1177,16 +1189,21 @@ nsDocumentViewer::PermitUnload(bool aCal
       rv = prompt->ConfirmEx(title, message, buttonFlags,
                              leaveLabel, stayLabel, nullptr, nullptr,
                              &dummy, &buttonPressed);
       mInPermitUnloadPrompt = false;
       NS_ENSURE_SUCCESS(rv, rv);
 
       // Button 0 == leave, button 1 == stay
       *aPermitUnload = (buttonPressed == 0);
+      // If the user decided to go ahead, make sure not to prompt the user again
+      // by toggling the internal prompting bool to false:
+      if (*aPermitUnload) {
+        *aShouldPrompt = false;
+      }
     }
   }
 
   if (docShellNode) {
     int32_t childCount;
     docShellNode->GetChildCount(&childCount);
 
     for (int32_t i = 0; i < childCount && *aPermitUnload; ++i) {
@@ -1195,17 +1212,18 @@ nsDocumentViewer::PermitUnload(bool aCal
 
       nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(item));
 
       if (docShell) {
         nsCOMPtr<nsIContentViewer> cv;
         docShell->GetContentViewer(getter_AddRefs(cv));
 
         if (cv) {
-          cv->PermitUnload(aCallerClosesWindow, aPermitUnload);
+          cv->PermitUnloadInternal(aCallerClosesWindow, aShouldPrompt,
+                                   aPermitUnload);
         }
       }
     }
   }
 
   if (aCallerClosesWindow && *aPermitUnload)
     mCallerIsClosingWindow = true;