Bug 775467 - Make readyState progress through all states without duplicate transitions. r=bzbarsky.
authorHenri Sivonen <hsivonen@iki.fi>
Fri, 27 Jul 2012 16:35:09 +0300
changeset 100725 fa6a84e7ba5384d49144b2b2477f9f34dfe49772
parent 100724 c6b3dbee10d967878c9d7a3ef3502f929d7703b0
child 100726 436fce553cd0de23ee330e4a7449980e6c4cf9e0
push id12627
push userhsivonen@iki.fi
push dateFri, 27 Jul 2012 13:36:59 +0000
treeherdermozilla-inbound@fa6a84e7ba53 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbzbarsky
bugs775467
milestone17.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 775467 - Make readyState progress through all states without duplicate transitions. r=bzbarsky.
content/base/src/nsContentSink.cpp
content/html/document/src/ImageDocument.cpp
content/html/document/src/MediaDocument.cpp
content/html/document/src/MediaDocument.h
content/html/document/src/PluginDocument.cpp
content/html/document/src/VideoDocument.cpp
content/xml/document/src/nsXMLContentSink.cpp
content/xslt/src/xslt/txMozillaXSLTProcessor.cpp
docshell/base/nsDocShell.cpp
layout/base/nsDocumentViewer.cpp
--- a/content/base/src/nsContentSink.cpp
+++ b/content/base/src/nsContentSink.cpp
@@ -1450,17 +1450,17 @@ nsContentSink::EndUpdate(nsIDocument *aD
   if (!--mInNotification) {
     UpdateChildCounts();
   }
 }
 
 void
 nsContentSink::DidBuildModelImpl(bool aTerminated)
 {
-  if (mDocument && !aTerminated) {
+  if (mDocument) {
     MOZ_ASSERT(mDocument->GetReadyStateEnum() ==
                nsIDocument::READYSTATE_LOADING, "Bad readyState");
     mDocument->SetReadyStateInternal(nsIDocument::READYSTATE_INTERACTIVE);
   }
 
   if (mScriptLoader) {
     mScriptLoader->ParsingComplete(aTerminated);
   }
--- a/content/html/document/src/ImageDocument.cpp
+++ b/content/html/document/src/ImageDocument.cpp
@@ -342,20 +342,22 @@ ImageDocument::SetScriptGlobalObject(nsI
       target = do_QueryInterface(mImageContent);
       target->AddEventListener(NS_LITERAL_STRING("click"), this, false);
     }
 
     target = do_QueryInterface(aScriptGlobalObject);
     target->AddEventListener(NS_LITERAL_STRING("resize"), this, false);
     target->AddEventListener(NS_LITERAL_STRING("keypress"), this, false);
 
-    if (!nsContentUtils::IsChildOfSameType(this)) {
+    if (!nsContentUtils::IsChildOfSameType(this) &&
+        GetReadyStateEnum() != nsIDocument::READYSTATE_COMPLETE) {
       LinkStylesheet(NS_LITERAL_STRING("resource://gre/res/TopLevelImageDocument.css"));
       LinkStylesheet(NS_LITERAL_STRING("chrome://global/skin/TopLevelImageDocument.css"));
     }
+    BecomeInteractive();
   }
 }
 
 void
 ImageDocument::OnPageShow(bool aPersisted,
                           nsIDOMEventTarget* aDispatchStartTarget)
 {
   if (aPersisted) {
--- a/content/html/document/src/MediaDocument.cpp
+++ b/content/html/document/src/MediaDocument.cpp
@@ -189,16 +189,36 @@ MediaDocument::StartDocumentLoad(const c
   if (!charset.IsEmpty() && !charset.Equals("UTF-8")) {
     SetDocumentCharacterSet(charset);
     mCharacterSetSource = kCharsetFromUserDefault;
   }
 
   return NS_OK;
 }
 
+void
+MediaDocument::BecomeInteractive()
+{
+  // In principle, if we knew the readyState code to work, we could infer
+  // restoration from GetReadyStateEnum() == nsIDocument::READYSTATE_COMPLETE.
+  bool restoring = false;
+  nsPIDOMWindow* window = GetWindow();
+  if (window) {
+    nsIDocShell* docShell = window->GetDocShell();
+    if (docShell) {
+      docShell->GetRestoringDocument(&restoring);
+    }
+  }
+  if (!restoring) {
+    MOZ_ASSERT(GetReadyStateEnum() == nsIDocument::READYSTATE_LOADING,
+               "Bad readyState");
+    SetReadyStateInternal(nsIDocument::READYSTATE_INTERACTIVE);
+  }
+}
+
 nsresult
 MediaDocument::CreateSyntheticDocument()
 {
   // Synthesize an empty html document
   nsresult rv;
 
   nsCOMPtr<nsINodeInfo> nodeInfo;
   nodeInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::html, nsnull,
--- a/content/html/document/src/MediaDocument.h
+++ b/content/html/document/src/MediaDocument.h
@@ -30,16 +30,18 @@ public:
                                      nsISupports*        aContainer,
                                      nsIStreamListener** aDocListener,
                                      bool                aReset = true,
                                      nsIContentSink*     aSink = nsnull);
 
   virtual void SetScriptGlobalObject(nsIScriptGlobalObject* aGlobalObject);
 
 protected:
+  void BecomeInteractive();
+
   virtual nsresult CreateSyntheticDocument();
 
   friend class MediaDocumentStreamListener;
   nsresult StartLayout();
 
   void GetFileName(nsAString& aResult);
 
   nsresult LinkStylesheet(const nsAString& aStylesheet);
--- a/content/html/document/src/PluginDocument.cpp
+++ b/content/html/document/src/PluginDocument.cpp
@@ -174,16 +174,17 @@ PluginDocument::SetScriptGlobalObject(ns
     if (!mPluginContent) {
       // Create synthetic document
 #ifdef DEBUG
       nsresult rv =
 #endif
         CreateSyntheticPluginDocument();
       NS_ASSERTION(NS_SUCCEEDED(rv), "failed to create synthetic document");
     }
+    BecomeInteractive();
   } else {
     mStreamListener = nsnull;
   }
 }
 
 
 bool
 PluginDocument::CanSavePresentation(nsIRequest *aNewRequest)
--- a/content/html/document/src/VideoDocument.cpp
+++ b/content/html/document/src/VideoDocument.cpp
@@ -65,19 +65,23 @@ VideoDocument::StartDocumentLoad(const c
 
 void
 VideoDocument::SetScriptGlobalObject(nsIScriptGlobalObject* aScriptGlobalObject)
 {
   // Set the script global object on the superclass before doing
   // anything that might require it....
   MediaDocument::SetScriptGlobalObject(aScriptGlobalObject);
 
-  if (aScriptGlobalObject && !nsContentUtils::IsChildOfSameType(this)) {
-    LinkStylesheet(NS_LITERAL_STRING("resource://gre/res/TopLevelVideoDocument.css"));
-    LinkStylesheet(NS_LITERAL_STRING("chrome://global/skin/TopLevelVideoDocument.css"));
+  if (aScriptGlobalObject) {
+    if (!nsContentUtils::IsChildOfSameType(this) &&
+        GetReadyStateEnum() != nsIDocument::READYSTATE_COMPLETE) {
+      LinkStylesheet(NS_LITERAL_STRING("resource://gre/res/TopLevelVideoDocument.css"));
+      LinkStylesheet(NS_LITERAL_STRING("chrome://global/skin/TopLevelVideoDocument.css"));
+    }
+    BecomeInteractive();
   }
 }
 
 nsresult
 VideoDocument::CreateSyntheticVideoDocument(nsIChannel* aChannel,
                                             nsIStreamListener** aListener)
 {
   // make our generic document
--- a/content/xml/document/src/nsXMLContentSink.cpp
+++ b/content/xml/document/src/nsXMLContentSink.cpp
@@ -257,16 +257,25 @@ CheckXSLTParamPI(nsIDOMProcessingInstruc
       aProcessor->AddXSLTParam(name, namespaceAttr, select, value, doc);
     }
   }
 }
 
 NS_IMETHODIMP
 nsXMLContentSink::DidBuildModel(bool aTerminated)
 {
+  if (!mParser) {
+    // If mParser is null, this parse has already been terminated and must
+    // not been terminated again. However, nsDocument may still think that
+    // the parse has not been terminated and call back into here in the case
+    // where the XML parser has finished but the XSLT transform associated
+    // with the document has not.
+    return NS_OK;
+  }
+
   DidBuildModelImpl(aTerminated);
 
   if (mXSLTProcessor) {
     // stop observing in order to avoid crashing when replacing content
     mDocument->RemoveObserver(this);
     mIsDocumentObserver = false;
 
     // Check for xslt-param and xslt-param-namespace PIs
--- a/content/xslt/src/xslt/txMozillaXSLTProcessor.cpp
+++ b/content/xslt/src/xslt/txMozillaXSLTProcessor.cpp
@@ -1090,16 +1090,21 @@ txMozillaXSLTProcessor::notifyError()
 
     // Set up the document
     nsCOMPtr<nsIDocument> document = do_QueryInterface(errorDocument);
     if (!document) {
         return;
     }
     URIUtils::ResetWithSource(document, mSource);
 
+    MOZ_ASSERT(document->GetReadyStateEnum() ==
+                 nsIDocument::READYSTATE_UNINITIALIZED,
+               "Bad readyState.");
+    document->SetReadyStateInternal(nsIDocument::READYSTATE_LOADING);
+
     NS_NAMED_LITERAL_STRING(ns, "http://www.mozilla.org/newlayout/xml/parsererror.xml");
 
     nsCOMPtr<nsIDOMElement> element;
     rv = errorDocument->CreateElementNS(ns, NS_LITERAL_STRING("parsererror"),
                                         getter_AddRefs(element));
     if (NS_FAILED(rv)) {
         return;
     }
@@ -1141,16 +1146,21 @@ txMozillaXSLTProcessor::notifyError()
         }
     
         rv = sourceElement->AppendChild(text, getter_AddRefs(resultNode));
         if (NS_FAILED(rv)) {
             return;
         }
     }
 
+    MOZ_ASSERT(document->GetReadyStateEnum() ==
+                 nsIDocument::READYSTATE_LOADING,
+               "Bad readyState.");
+    document->SetReadyStateInternal(nsIDocument::READYSTATE_INTERACTIVE);
+
     mObserver->OnTransformDone(mTransformResult, document);
 }
 
 nsresult
 txMozillaXSLTProcessor::ensureStylesheet()
 {
     if (mStylesheet) {
         return NS_OK;
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -7248,17 +7248,19 @@ nsDocShell::RestoreFromHistory()
     mLSHE->GetViewerBounds(oldBounds);
 
     // Restore the refresh URI list.  The refresh timers will be restarted
     // when EndPageLoad() is called.
     nsCOMPtr<nsISupportsArray> refreshURIList;
     mLSHE->GetRefreshURIList(getter_AddRefs(refreshURIList));
 
     // Reattach to the window object.
+    mIsRestoringDocument = true; // for MediaDocument::BecomeInteractive
     rv = mContentViewer->Open(windowState, mLSHE);
+    mIsRestoringDocument = false;
 
     // Hack to keep nsDocShellEditorData alive across the
     // SetContentViewer(nsnull) call below.
     nsAutoPtr<nsDocShellEditorData> data(mLSHE->ForgetEditorData());
 
     // Now remove it from the cached presentation.
     mLSHE->SetContentViewer(nsnull);
     mEODForCurrentDocument = false;
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -986,33 +986,42 @@ DocumentViewerImpl::LoadComplete(nsresul
   // Now, fire either an OnLoad or OnError event to the document...
   bool restoring = false;
   // XXXbz imagelib kills off the document load for a full-page image with
   // NS_ERROR_PARSED_DATA_CACHED if it's in the cache.  So we want to treat
   // that one as a success code; otherwise whether we fire onload for the image
   // will depend on whether it's cached!
   if(window &&
      (NS_SUCCEEDED(aStatus) || aStatus == NS_ERROR_PARSED_DATA_CACHED)) {
-    if (mDocument)
-      mDocument->SetReadyStateInternal(nsIDocument::READYSTATE_COMPLETE);
     nsEventStatus status = nsEventStatus_eIgnore;
     nsEvent event(true, NS_LOAD);
     event.flags |= NS_EVENT_FLAG_CANT_BUBBLE;
      // XXX Dispatching to |window|, but using |document| as the target.
     event.target = mDocument;
 
     // If the document presentation is being restored, we don't want to fire
     // onload to the document content since that would likely confuse scripts
     // on the page.
 
     nsIDocShell *docShell = window->GetDocShell();
     NS_ENSURE_TRUE(docShell, NS_ERROR_UNEXPECTED);
 
     docShell->GetRestoringDocument(&restoring);
     if (!restoring) {
+      MOZ_ASSERT(mDocument->IsXUL() || // readyState for XUL is bogus
+                 mDocument->GetReadyStateEnum() ==
+                   nsIDocument::READYSTATE_INTERACTIVE ||
+                 // test_stricttransportsecurity.html has old-style
+                 // docshell-generated about:blank docs reach this code!
+                 (mDocument->GetReadyStateEnum() ==
+                    nsIDocument::READYSTATE_UNINITIALIZED &&
+                  NS_IsAboutBlank(mDocument->GetDocumentURI())),
+                 "Bad readystate");
+      mDocument->SetReadyStateInternal(nsIDocument::READYSTATE_COMPLETE);
+
       nsRefPtr<nsDOMNavigationTiming> timing(mDocument->GetNavigationTiming());
       if (timing) {
         timing->NotifyLoadEventStart();
       }
       nsEventDispatcher::Dispatch(window, mPresContext, &event, nsnull,
                                   &status);
       if (timing) {
         timing->NotifyLoadEventEnd();