Fix a few issues in bfcache and XBL when javascript is being toggled on and off. Bug 398668, r+sr+a=jst
authorbzbarsky@mit.edu
Fri, 05 Oct 2007 17:35:00 -0700
changeset 6685 6079bed5c21fcd9f29c87a754e032f9c258a926c
parent 6684 a5591c0e321a84cdc9a74494250c9dd67563f4bb
child 6686 6059dad7b7b4434037a2b5fdccc7005c137f9b49
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)
bugs398668
milestone1.9a9pre
Fix a few issues in bfcache and XBL when javascript is being toggled on and off. Bug 398668, r+sr+a=jst
content/xbl/src/nsXBLProtoImplMethod.cpp
docshell/base/nsDocShell.cpp
docshell/base/nsIContentViewer.idl
layout/base/nsDocumentViewer.cpp
--- a/content/xbl/src/nsXBLProtoImplMethod.cpp
+++ b/content/xbl/src/nsXBLProtoImplMethod.cpp
@@ -282,17 +282,23 @@ nsXBLProtoImplMethod::Traverse(nsCycleCo
   NS_ASSERTION(mIsCompiled, "Shouldn't traverse uncompiled method");
 
   cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, mJSMethodObject);
 }
 
 nsresult
 nsXBLProtoImplAnonymousMethod::Execute(nsIContent* aBoundElement)
 {
-  NS_PRECONDITION(mIsCompiled, "Can't execute uncompiled method");
+  if (!mIsCompiled) {
+    // Someone might have enabled script between when the binding would have
+    // been compiled and now.  If this is the constructor we shouldn't run it
+    // because our other scripted stuff is not set up, and if it's the
+    // destructor then the constructor never ran.  In either case, bail out.
+    return NS_OK;
+  }
   
   if (!mJSMethodObject) {
     // Nothing to do here
     return NS_OK;
   }
 
   // Get the script context the same way
   // nsXBLProtoImpl::InstallImplementation does.
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -5701,19 +5701,43 @@ nsDocShell::RestoreFromHistory()
     if (nsDoc) {
         const nsAFlatString &title = document->GetDocumentTitle();
         nsDoc->SetTitle(title);
     }
 
     // Now we simulate appending child docshells for subframes.
     for (i = 0; i < childShells.Count(); ++i) {
         nsIDocShellTreeItem *childItem = childShells.ObjectAt(i);
+        nsCOMPtr<nsIDocShell> childShell = do_QueryInterface(childItem);
+
+        // Make sure to not clobber the state of the child.  Since AddChild
+        // always clobbers it, save it off first.
+        PRBool allowPlugins;
+        childShell->GetAllowPlugins(&allowPlugins);
+
+        PRBool allowJavascript;
+        childShell->GetAllowJavascript(&allowJavascript);
+
+        PRBool allowRedirects;
+        childShell->GetAllowMetaRedirects(&allowRedirects);
+
+        PRBool allowSubframes;
+        childShell->GetAllowSubframes(&allowSubframes);
+
+        PRBool allowImages;
+        childShell->GetAllowImages(&allowImages);
+        
         AddChild(childItem);
 
-        nsCOMPtr<nsIDocShell> childShell = do_QueryInterface(childItem);
+        childShell->SetAllowPlugins(allowPlugins);
+        childShell->SetAllowJavascript(allowJavascript);
+        childShell->SetAllowMetaRedirects(allowRedirects);
+        childShell->SetAllowSubframes(allowSubframes);
+        childShell->SetAllowImages(allowImages);
+
         rv = childShell->BeginRestore(nsnull, PR_FALSE);
         NS_ENSURE_SUCCESS(rv, rv);
     }
 
     nsCOMPtr<nsIPresShell> shell;
     nsDocShell::GetPresShell(getter_AddRefs(shell));
 
     nsIViewManager *newVM = shell ? shell->GetViewManager() : nsnull;
@@ -6856,16 +6880,38 @@ nsDocShell::InternalLoad(nsIURI * aURI,
     // mLSHE for the real page.
     if (mLoadType != LOAD_ERROR_PAGE)
         SetHistoryEntry(&mLSHE, aSHEntry);
 
     mSavingOldViewer = savePresentation;
 
     // If we have a saved content viewer in history, restore and show it now.
     if (aSHEntry && (mLoadType & LOAD_CMD_HISTORY)) {
+        // It's possible that the previous viewer of mContentViewer is the
+        // viewer that will end up in aSHEntry when it gets closed.  If that's
+        // the case, we need to go ahead and force it into its shentry so we
+        // can restore it.
+        if (mContentViewer) {
+            nsCOMPtr<nsIContentViewer> prevViewer;
+            mContentViewer->GetPreviousViewer(getter_AddRefs(prevViewer));
+            if (prevViewer) {
+#ifdef DEBUG
+                nsCOMPtr<nsIContentViewer> prevPrevViewer;
+                prevViewer->GetPreviousViewer(getter_AddRefs(prevPrevViewer));
+                NS_ASSERTION(!prevPrevViewer, "Should never have viewer chain here");
+#endif
+                nsCOMPtr<nsISHEntry> viewerEntry;
+                prevViewer->GetHistoryEntry(getter_AddRefs(viewerEntry));
+                if (viewerEntry == aSHEntry) {
+                    // Make sure this viewer ends up in the right place
+                    mContentViewer->SetPreviousViewer(nsnull);
+                    prevViewer->Destroy();
+                }
+            }
+        }
         nsCOMPtr<nsISHEntry> oldEntry = mOSHE;
         PRBool restoring;
         rv = RestorePresentation(aSHEntry, &restoring);
         if (restoring)
             return rv;
 
         // We failed to restore the presentation, so clean up.
         // Both the old and new history entries could potentially be in
--- a/docshell/base/nsIContentViewer.idl
+++ b/docshell/base/nsIContentViewer.idl
@@ -10,17 +10,17 @@ class nsIWidget;
 class nsIDeviceContext;
 struct nsRect;
 %}
 
 [ptr] native nsIWidgetPtr(nsIWidget);
 [ptr] native nsIDeviceContextPtr(nsIDeviceContext);
 [ref] native nsRectRef(nsRect);
 
-[scriptable, uuid(1d587109-9df7-4614-9a96-309902269e9e)]
+[scriptable, uuid(89653afe-182f-401f-9f3c-8858d91387cd)]
 interface nsIContentViewer : nsISupports
 {
 
   [noscript] void init(in nsIWidgetPtr aParentWidget,
                        in nsIDeviceContextPtr aDeviceContext,
                        [const] in nsRectRef aBounds);
 
   attribute nsISupports container;
@@ -100,9 +100,15 @@ interface nsIContentViewer : nsISupports
    */
   void clearHistoryEntry();
 
   /*
    * Change the layout to view the document with page layout (like print preview), but
    * dynamic and editable (like Galley layout).
   */
   void setPageMode(in PRBool aPageMode, in nsIPrintSettings aPrintSettings);
+
+  /**
+   * Get the history entry that this viewer will save itself into when
+   * destroyed.  Can return null
+   */
+  readonly attribute nsISHEntry historyEntry;
 };
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -1722,16 +1722,19 @@ DocumentViewerImpl::SetPreviousViewer(ns
 
     // In a multiple chaining situation (which occurs when running a thrashing
     // test like i-bench or jrgm's tests with no delay), we can build up a
     // whole chain of viewers.  In order to avoid this, we always set our previous
     // viewer to the MOST previous viewer in the chain, and then dump the intermediate
     // link from the chain.  This ensures that at most only 2 documents are alive
     // and undestroyed at any given time (the one that is showing and the one that
     // is loading with painting suppressed).
+    // It's very important that if this ever gets changed the code
+    // before the RestorePresentation call in nsDocShell::InternalLoad
+    // be changed accordingly.
     nsCOMPtr<nsIContentViewer> prevViewer;
     aViewer->GetPreviousViewer(getter_AddRefs(prevViewer));
     if (prevViewer) {
       aViewer->SetPreviousViewer(nsnull);
       aViewer->Destroy();
       return SetPreviousViewer(prevViewer);
     }
   }
@@ -4054,8 +4057,15 @@ NS_IMETHODIMP DocumentViewerImpl::SetPag
     NS_ENSURE_SUCCESS(rv, rv);
   }
   InitInternal(mParentWidget, nsnull, mDeviceContext, bounds, PR_TRUE, PR_FALSE, PR_FALSE);
   mViewManager->EnableRefresh(NS_VMREFRESH_NO_SYNC);
 
   Show();
   return NS_OK;
 }
+
+NS_IMETHODIMP
+DocumentViewerImpl::GetHistoryEntry(nsISHEntry **aHistoryEntry)
+{
+  NS_IF_ADDREF(*aHistoryEntry = mSHEntry);
+  return NS_OK;
+}