Don't allow loads to start in a docshell that's firing the unload event. Alsodon't allow document.open of the document in that docshell. Bug 371360,r=biesi, sr=jst
authorbzbarsky@mit.edu
Thu, 14 Jun 2007 11:18:05 -0700
changeset 2400 a8bd328a47fb6bc7fc91e6d12a46477d1eea2e51
parent 2399 2d8b8b5f9f95c2f96688775fb11788bbe144aa15
child 2401 1bd93b1df924cddba7a4a57796f0416d34a69a86
push id1
push userbsmedberg@mozilla.com
push dateThu, 20 Mar 2008 16:49:24 +0000
treeherdermozilla-central@61007906a1f8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbiesi, jst
bugs371360
milestone1.9a6pre
Don't allow loads to start in a docshell that's firing the unload event. Alsodon't allow document.open of the document in that docshell. Bug 371360,r=biesi, sr=jst
content/html/document/src/nsHTMLDocument.cpp
docshell/base/nsDocShell.cpp
docshell/base/nsDocShell.h
docshell/base/nsIDocShell.idl
docshell/base/nsWebShell.cpp
--- a/content/html/document/src/nsHTMLDocument.cpp
+++ b/content/html/document/src/nsHTMLDocument.cpp
@@ -2035,16 +2035,26 @@ nsHTMLDocument::OpenCommon(const nsACStr
   }
 
   if (!aContentType.EqualsLiteral("text/html") &&
       !aContentType.EqualsLiteral("text/plain")) {
     NS_WARNING("Unsupported type; fix the caller");
     return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
   }
 
+  // check whether we're in the middle of unload.  If so, ignore this call.
+  nsCOMPtr<nsIDocShell> shell = do_QueryReferent(mDocumentContainer);
+  if (shell) {
+    PRBool inUnload;
+    shell->GetIsInUnload(&inUnload);
+    if (inUnload) {
+      return NS_OK;
+    }
+  }
+
   // Note: We want to use GetDocumentFromContext here because this document
   // should inherit the security information of the document that's opening us,
   // (since if it's secure, then it's presumeably trusted).
   nsCOMPtr<nsIDocument> callerDoc =
     do_QueryInterface(nsContentUtils::GetDocumentFromContext());
 
   // Grab a reference to the calling documents security info (if any)
   // and principal as it may be lost in the call to Reset().
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -666,16 +666,19 @@ nsDocShell::ConvertLoadTypeToDocShellLoa
 // nsDocShell::nsIDocShell
 //*****************************************************************************   
 NS_IMETHODIMP
 nsDocShell::LoadURI(nsIURI * aURI,
                     nsIDocShellLoadInfo * aLoadInfo,
                     PRUint32 aLoadFlags,
                     PRBool aFirstParty)
 {
+    if (mFiredUnloadEvent) {
+      return NS_OK; // JS may not handle returning of an error code
+    }
     nsresult rv;
     nsCOMPtr<nsIURI> referrer;
     nsCOMPtr<nsIInputStream> postStream;
     nsCOMPtr<nsIInputStream> headersStream;
     nsCOMPtr<nsISupports> owner;
     PRBool inheritOwner = PR_FALSE;
     PRBool sendReferrer = PR_TRUE;
     nsCOMPtr<nsISHEntry> shEntry;
@@ -968,16 +971,17 @@ nsDocShell::FirePageHideNotification(PRB
         PRInt32 i, n = mChildList.Count();
         for (i = 0; i < n; i++) {
             nsCOMPtr<nsIDocShell> shell(do_QueryInterface(ChildAt(i)));
             if (shell) {
                 shell->FirePageHideNotification(aIsUnload);
             }
         }
     }
+
     return NS_OK;
 }
 
 //
 // Bug 13871: Prevent frameset spoofing
 //
 // This routine answers: 'Is origin's document from same domain as
 // target's document?'
@@ -2193,16 +2197,23 @@ nsDocShell::SetTreeOwner(nsIDocShellTree
 
 NS_IMETHODIMP
 nsDocShell::SetChildOffset(PRUint32 aChildOffset)
 {
     mChildOffset = aChildOffset;
     return NS_OK;
 }
 
+NS_IMETHODIMP
+nsDocShell::GetIsInUnload(PRBool* aIsInUnload)
+{
+    *aIsInUnload = mFiredUnloadEvent;
+    return NS_OK;
+}
+
 //*****************************************************************************
 // nsDocShell::nsIDocShellTreeNode
 //*****************************************************************************   
 
 NS_IMETHODIMP
 nsDocShell::GetChildCount(PRInt32 * aChildCount)
 {
     NS_ENSURE_ARG_POINTER(aChildCount);
@@ -2625,89 +2636,95 @@ nsDocShell::IsPrintingOrPP(PRBool aDispl
 {
   if (mIsPrintingOrPP && aDisplayErrorDialog) {
     DisplayLoadError(NS_ERROR_DOCUMENT_IS_PRINTMODE, nsnull, nsnull);
   }
 
   return mIsPrintingOrPP;
 }
 
+PRBool
+nsDocShell::IsNavigationAllowed(PRBool aDisplayPrintErrorDialog)
+{
+    return !IsPrintingOrPP(aDisplayPrintErrorDialog) && !mFiredUnloadEvent;
+}
+
 //*****************************************************************************
 // nsDocShell::nsIWebNavigation
 //*****************************************************************************   
 
 NS_IMETHODIMP
 nsDocShell::GetCanGoBack(PRBool * aCanGoBack)
 {
-    if (IsPrintingOrPP(PR_FALSE)) {
+    if (!IsNavigationAllowed(PR_FALSE)) {
       *aCanGoBack = PR_FALSE;
       return NS_OK; // JS may not handle returning of an error code
     }
     nsresult rv;
     nsCOMPtr<nsISHistory> rootSH;
     rv = GetRootSessionHistory(getter_AddRefs(rootSH));
     nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
     NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
     rv = webnav->GetCanGoBack(aCanGoBack);   
     return rv;
 
 }
 
 NS_IMETHODIMP
 nsDocShell::GetCanGoForward(PRBool * aCanGoForward)
 {
-    if (IsPrintingOrPP(PR_FALSE)) {
+    if (!IsNavigationAllowed(PR_FALSE)) {
       *aCanGoForward = PR_FALSE;
       return NS_OK; // JS may not handle returning of an error code
     }
     nsresult rv;
     nsCOMPtr<nsISHistory> rootSH;
     rv = GetRootSessionHistory(getter_AddRefs(rootSH)); 
     nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
     NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
     rv = webnav->GetCanGoForward(aCanGoForward);
     return rv;
 
 }
 
 NS_IMETHODIMP
 nsDocShell::GoBack()
 {
-    if (IsPrintingOrPP()) {
+    if (!IsNavigationAllowed()) {
       return NS_OK; // JS may not handle returning of an error code
     }
     nsresult rv;
     nsCOMPtr<nsISHistory> rootSH;
     rv = GetRootSessionHistory(getter_AddRefs(rootSH));
     nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
     NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
     rv = webnav->GoBack();
     return rv;
 
 }
 
 NS_IMETHODIMP
 nsDocShell::GoForward()
 {
-    if (IsPrintingOrPP()) {
+    if (!IsNavigationAllowed()) {
       return NS_OK; // JS may not handle returning of an error code
     }
     nsresult rv;
     nsCOMPtr<nsISHistory> rootSH;
     rv = GetRootSessionHistory(getter_AddRefs(rootSH));
     nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
     NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
     rv = webnav->GoForward();
     return rv;
 
 }
 
 NS_IMETHODIMP nsDocShell::GotoIndex(PRInt32 aIndex)
 {
-    if (IsPrintingOrPP()) {
+    if (!IsNavigationAllowed()) {
       return NS_OK; // JS may not handle returning of an error code
     }
     nsresult rv;
     nsCOMPtr<nsISHistory> rootSH;
     rv = GetRootSessionHistory(getter_AddRefs(rootSH));
     nsCOMPtr<nsIWebNavigation> webnav(do_QueryInterface(rootSH));
     NS_ENSURE_TRUE(webnav, NS_ERROR_FAILURE);
     rv = webnav->GotoIndex(aIndex);
@@ -2718,17 +2735,17 @@ NS_IMETHODIMP nsDocShell::GotoIndex(PRIn
 
 NS_IMETHODIMP
 nsDocShell::LoadURI(const PRUnichar * aURI,
                     PRUint32 aLoadFlags,
                     nsIURI * aReferringURI,
                     nsIInputStream * aPostStream,
                     nsIInputStream * aHeaderStream)
 {
-    if (IsPrintingOrPP()) {
+    if (!IsNavigationAllowed()) {
       return NS_OK; // JS may not handle returning of an error code
     }
     nsCOMPtr<nsIURI> uri;
     nsresult rv = NS_OK;
 
     // Create a URI from our string; if that succeeds, we want to
     // change aLoadFlags to not include the ALLOW_THIRD_PARTY_FIXUP
     // flag.
@@ -3102,17 +3119,17 @@ nsDocShell::LoadErrorPage(nsIURI *aURI, 
                         nsnull, nsnull, LOAD_ERROR_PAGE,
                         nsnull, PR_TRUE, nsnull, nsnull);
 }
 
 
 NS_IMETHODIMP
 nsDocShell::Reload(PRUint32 aReloadFlags)
 {
-    if (IsPrintingOrPP()) {
+    if (!IsNavigationAllowed()) {
       return NS_OK; // JS may not handle returning of an error code
     }
     nsresult rv;
     NS_ASSERTION(((aReloadFlags & 0xf) == 0),
                  "Reload command not updated to use load flags!");
 
     PRUint32 loadType = MAKE_LOAD_TYPE(LOAD_RELOAD_NORMAL, aReloadFlags);
     NS_ENSURE_TRUE(IsValidLoadType(loadType), NS_ERROR_INVALID_ARG);
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -375,16 +375,17 @@ protected:
     nsresult   RefreshURIFromQueue();
     NS_IMETHOD DisplayLoadError(nsresult aError, nsIURI *aURI,
                                 const PRUnichar *aURL,
                                 nsIChannel* aFailedChannel = nsnull);
     NS_IMETHOD LoadErrorPage(nsIURI *aURI, const PRUnichar *aURL,
                              const PRUnichar *aPage,
                              const PRUnichar *aDescription,
                              nsIChannel* aFailedChannel);
+    PRBool IsNavigationAllowed(PRBool aDisplayPrintErrorDialog = PR_TRUE);
     PRBool IsPrintingOrPP(PRBool aDisplayErrorDialog = PR_TRUE);
 
     nsresult SetBaseUrlForWyciwyg(nsIContentViewer * aContentViewer);
 
     static  inline  PRUint32
     PRTimeToSeconds(PRTime t_usec)
     {
       PRTime usec_per_sec;
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -63,17 +63,17 @@ interface nsIWebNavigation;
 interface nsISimpleEnumerator;
 interface nsIInputStream;
 interface nsIRequest;
 interface nsISHEntry;
 interface nsILayoutHistoryState;
 interface nsISecureBrowserUI;
 interface nsIDOMStorage;
 
-[scriptable, uuid(db67b973-ba1a-49fa-b5b4-7670d203fa0e)]
+[scriptable, uuid(10ed386d-8598-408c-b571-e75ad18edeb0)]
 interface nsIDocShell : nsISupports
 {
   /**
    * Loads a given URI.  This will give priority to loading the requested URI
    * in the object implementing	this interface.  If it can't be loaded here
    * however, the URL dispatcher will go through its normal process of content
    * loading.
    *
@@ -179,16 +179,19 @@ interface nsIDocShell : nsISupports
    * property on nsIWebNavigation.
    */ 
   void setCurrentURI(in nsIURI aURI);
 
   /**
    * Notify the associated content viewer and all child docshells that they are
    * about to be hidden.  If |isUnload| is true, then the document is being
    * unloaded as well.
+   *
+   * @param isUnload if true, fire the unload event in addition to the pagehide
+   *                 event.
    */
   [noscript] void firePageHideNotification(in boolean isUnload);
 
   /**
    * Presentation context for the currently loaded document.  This may be null.
    */
   [noscript] readonly attribute nsPresContext presContext;
 
@@ -433,10 +436,17 @@ interface nsIDocShell : nsISupports
    * until after OnLocationChange fires.
    */
   readonly attribute nsIChannel currentDocumentChannel;
 
   /**
    * Set the offset of this child in its container.
    */
   [noscript] void setChildOffset(in unsigned long offset);
+
+  /**
+   * Find out whether the docshell is currently in the middle of a page
+   * transition (after the onunload event has fired, but before the new
+   * document has been set up)
+   */
+  readonly attribute boolean isInUnload;
 };
 
--- a/docshell/base/nsWebShell.cpp
+++ b/docshell/base/nsWebShell.cpp
@@ -763,16 +763,21 @@ OnLinkClickEvent::OnLinkClickEvent(nsWeb
 NS_IMETHODIMP
 nsWebShell::OnLinkClick(nsIContent* aContent,
                         nsIURI* aURI,
                         const PRUnichar* aTargetSpec,
                         nsIInputStream* aPostDataStream,
                         nsIInputStream* aHeadersDataStream)
 {
   NS_ASSERTION(NS_IsMainThread(), "wrong thread");
+
+  if (mFiredUnloadEvent) {
+    return NS_OK;
+  }
+  
   nsCOMPtr<nsIRunnable> ev =
       new OnLinkClickEvent(this, aContent, aURI, aTargetSpec,
                            aPostDataStream, aHeadersDataStream);
   return NS_DispatchToCurrentThread(ev);
 }
 
 NS_IMETHODIMP
 nsWebShell::OnLinkClickSync(nsIContent *aContent,
@@ -786,16 +791,20 @@ nsWebShell::OnLinkClickSync(nsIContent *
   // Initialize the DocShell / Request
   if (aDocShell) {
     *aDocShell = nsnull;
   }
   if (aRequest) {
     *aRequest = nsnull;
   }
 
+  if (mFiredUnloadEvent) {
+    return NS_OK;
+  }
+
   {
     // defer to an external protocol handler if necessary...
     nsCOMPtr<nsIExternalProtocolService> extProtService = do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID);
     if (extProtService) {
       nsCAutoString scheme;
       aURI->GetScheme(scheme);
       if (!scheme.IsEmpty()) {
         // if the URL scheme does not correspond to an exposed protocol, then we