diff --git a/content/base/public/nsIDocument.h b/content/base/public/nsIDocument.h index a13b1bcb654..6a5533c2e19 100644 --- a/content/base/public/nsIDocument.h +++ b/content/base/public/nsIDocument.h @@ -127,8 +127,8 @@ typedef CallbackObjectHolder NodeFilterHolder; } // namespace mozilla #define NS_IDOCUMENT_IID \ -{ 0xa7679e4a, 0xa5ec, 0x45bf, \ - { 0x8f, 0xe4, 0xad, 0x4a, 0xb8, 0xc7, 0x7f, 0xc7 } } +{ 0x906d05e7, 0x39af, 0x4ff0, \ + { 0xbc, 0xcd, 0x30, 0x0c, 0x7f, 0xeb, 0x86, 0x21 } } // Flag for AddStyleSheet(). #define NS_STYLESHEET_FROM_CATALOG (1 << 0) @@ -1336,6 +1336,13 @@ public: */ virtual void UnblockOnload(bool aFireSync) = 0; + void BlockDOMContentLoaded() + { + ++mBlockDOMContentLoaded; + } + + virtual void UnblockDOMContentLoaded() = 0; + /** * Notification that the page has been shown, for documents which are loaded * into a DOM window. This corresponds to the completion of document load, @@ -2554,6 +2561,9 @@ protected: uint32_t mInSyncOperationCount; nsRefPtr mXPathEvaluator; + + uint32_t mBlockDOMContentLoaded; + bool mDidFireDOMContentLoaded:1; }; NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocument, NS_IDOCUMENT_IID) diff --git a/content/base/src/nsDocument.cpp b/content/base/src/nsDocument.cpp index 091edee9677..f849f90764a 100644 --- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -1527,7 +1527,8 @@ nsIDocument::nsIDocument() mAllowDNSPrefetch(true), mIsBeingUsedAsImage(false), mHasLinksToUpdate(false), - mPartID(0) + mPartID(0), + mDidFireDOMContentLoaded(true) { SetInDocument(); } @@ -4679,6 +4680,8 @@ nsDocument::BeginLoad() // Block onload here to prevent having to deal with blocking and // unblocking it while we know the document is loading. BlockOnload(); + mDidFireDOMContentLoaded = false; + BlockDOMContentLoaded(); if (mScriptLoader) { mScriptLoader->BeginDeferringScripts(); @@ -4920,6 +4923,19 @@ nsDocument::EndLoad() NS_DOCUMENT_NOTIFY_OBSERVERS(EndLoad, (this)); + UnblockDOMContentLoaded(); +} + +void +nsDocument::UnblockDOMContentLoaded() +{ + MOZ_ASSERT(mBlockDOMContentLoaded); + if (--mBlockDOMContentLoaded != 0 || mDidFireDOMContentLoaded) { + return; + } + mDidFireDOMContentLoaded = true; + + MOZ_ASSERT(mReadyState == READYSTATE_INTERACTIVE); if (!mSynchronousDOMContentLoaded) { nsRefPtr ev = NS_NewRunnableMethod(this, &nsDocument::DispatchContentLoadedEvents); diff --git a/content/base/src/nsDocument.h b/content/base/src/nsDocument.h index d20e49232a4..92cb4619311 100644 --- a/content/base/src/nsDocument.h +++ b/content/base/src/nsDocument.h @@ -1244,6 +1244,8 @@ public: mozilla::ErrorResult& rv) MOZ_OVERRIDE; virtual void UseRegistryFromDocument(nsIDocument* aDocument) MOZ_OVERRIDE; + virtual void UnblockDOMContentLoaded() MOZ_OVERRIDE; + protected: friend class nsNodeUtils; friend class nsDocumentOnStack; diff --git a/content/base/src/nsScriptLoader.cpp b/content/base/src/nsScriptLoader.cpp index e51a3eaa643..7edc8475213 100644 --- a/content/base/src/nsScriptLoader.cpp +++ b/content/base/src/nsScriptLoader.cpp @@ -124,7 +124,8 @@ nsScriptLoader::nsScriptLoader(nsIDocument *aDocument) mBlockerCount(0), mEnabled(true), mDeferEnabled(false), - mDocumentParsingDone(false) + mDocumentParsingDone(false), + mBlockingDOMContentLoaded(false) { // enable logging for CSP #ifdef PR_LOGGING @@ -656,7 +657,7 @@ nsScriptLoader::ProcessScriptElement(nsIScriptElement *aElement) NS_ASSERTION(mDocument->GetCurrentContentSink() || aElement->GetParserCreated() == FROM_PARSER_XSLT, "Non-XSLT Defer script on a document without an active parser; bug 592366."); - mDeferRequests.AppendElement(request); + AddDeferRequest(request); return false; } @@ -1171,6 +1172,9 @@ nsScriptLoader::ProcessPendingRequests() !mParserBlockingRequest && mAsyncRequests.IsEmpty() && mNonAsyncExternalScriptInsertedRequests.IsEmpty() && mXSLTRequests.IsEmpty() && mDeferRequests.IsEmpty()) { + if (MaybeRemovedDeferRequests()) { + return ProcessPendingRequests(); + } // No more pending scripts; time to unblock onload. // OK to unblock onload synchronously here, since callers must be // prepared for the world changing anyway. @@ -1488,3 +1492,27 @@ nsScriptLoader::PreloadURI(nsIURI *aURI, const nsAString &aCharset, pi->mRequest = request; pi->mCharset = aCharset; } + +void +nsScriptLoader::AddDeferRequest(nsScriptLoadRequest* aRequest) +{ + mDeferRequests.AppendElement(aRequest); + if (mDeferEnabled && mDeferRequests.Length() == 1 && mDocument && + !mBlockingDOMContentLoaded) { + MOZ_ASSERT(mDocument->GetReadyStateEnum() == nsIDocument::READYSTATE_LOADING); + mBlockingDOMContentLoaded = true; + mDocument->BlockDOMContentLoaded(); + } +} + +bool +nsScriptLoader::MaybeRemovedDeferRequests() +{ + if (mDeferRequests.Length() == 0 && mDocument && + mBlockingDOMContentLoaded) { + mBlockingDOMContentLoaded = false; + mDocument->UnblockDOMContentLoaded(); + return true; + } + return false; +} diff --git a/content/base/src/nsScriptLoader.h b/content/base/src/nsScriptLoader.h index c6f8ca85bc3..a734ab61c4d 100644 --- a/content/base/src/nsScriptLoader.h +++ b/content/base/src/nsScriptLoader.h @@ -291,6 +291,9 @@ private: uint32_t aStringLen, const uint8_t* aString); + void AddDeferRequest(nsScriptLoadRequest* aRequest); + bool MaybeRemovedDeferRequests(); + nsIDocument* mDocument; // [WEAK] nsCOMArray mObservers; nsTArray > mNonAsyncExternalScriptInsertedRequests; @@ -325,6 +328,7 @@ private: bool mEnabled; bool mDeferEnabled; bool mDocumentParsingDone; + bool mBlockingDOMContentLoaded; }; class nsAutoScriptLoaderDisabler diff --git a/dom/base/crashtests/crashtests.list b/dom/base/crashtests/crashtests.list index bd4f88c0fae..67747543b2a 100644 --- a/dom/base/crashtests/crashtests.list +++ b/dom/base/crashtests/crashtests.list @@ -21,7 +21,7 @@ load 473284.xul load 499006-1.html load 499006-2.html load 502617.html -asserts(1-2) load 504224.html # bug 564098 +load 504224.html load 603531.html load 601247.html load 609560-1.xhtml diff --git a/js/xpconnect/tests/mochitest/mochitest.ini b/js/xpconnect/tests/mochitest/mochitest.ini index 1dd4112c89a..325a2f2b47a 100644 --- a/js/xpconnect/tests/mochitest/mochitest.ini +++ b/js/xpconnect/tests/mochitest/mochitest.ini @@ -93,6 +93,6 @@ support-files = [test_frameWrapping.html] # The JS test component we use below is only available in debug builds. [test_getWebIDLCaller.html] -skip-if = debug == false +skip-if = (debug == false || os == "android") [test_nac.xhtml] [test_sameOriginPolicy.html] diff --git a/parser/html/nsHtml5TreeOpExecutor.cpp b/parser/html/nsHtml5TreeOpExecutor.cpp index 703d1929529..25ce068e138 100644 --- a/parser/html/nsHtml5TreeOpExecutor.cpp +++ b/parser/html/nsHtml5TreeOpExecutor.cpp @@ -95,14 +95,14 @@ nsHtml5TreeOpExecutor::WillParse() NS_IMETHODIMP nsHtml5TreeOpExecutor::WillBuildModel(nsDTDMode aDTDMode) { + mDocument->AddObserver(this); + WillBuildModelImpl(); + GetDocument()->BeginLoad(); if (mDocShell && !GetDocument()->GetWindow() && !IsExternalViewSource()) { // Not loading as data but script global object not ready return MarkAsBroken(NS_ERROR_DOM_INVALID_STATE_ERR); } - mDocument->AddObserver(this); - WillBuildModelImpl(); - GetDocument()->BeginLoad(); return NS_OK; } @@ -111,8 +111,6 @@ nsHtml5TreeOpExecutor::WillBuildModel(nsDTDMode aDTDMode) NS_IMETHODIMP nsHtml5TreeOpExecutor::DidBuildModel(bool aTerminated) { - NS_PRECONDITION(mStarted, "Bad life cycle."); - if (!aTerminated) { // This is needed to avoid unblocking loads too many times on one hand // and on the other hand to avoid destroying the frame constructor from @@ -162,7 +160,12 @@ nsHtml5TreeOpExecutor::DidBuildModel(bool aTerminated) // Return early to avoid unblocking the onload event too many times. return NS_OK; } - mDocument->EndLoad(); + + // We may not have called BeginLoad() if loading is terminated before + // OnStartRequest call. + if (mStarted) { + mDocument->EndLoad(); + } DropParserAndPerfHint(); #ifdef GATHER_DOCWRITE_STATISTICS printf("UNSAFE SCRIPTS: %d\n", sUnsafeDocWrites); diff --git a/parser/htmlparser/tests/mochitest/file_bug688580.js b/parser/htmlparser/tests/mochitest/file_bug688580.js new file mode 100644 index 00000000000..b567150f673 --- /dev/null +++ b/parser/htmlparser/tests/mochitest/file_bug688580.js @@ -0,0 +1,4 @@ +is(document.readyState, "interactive", "readyState should be interactive during defer."); +is(state, "readyState interactive", "Bad state upon defer"); +state = "defer"; + diff --git a/parser/htmlparser/tests/mochitest/mochitest.ini b/parser/htmlparser/tests/mochitest/mochitest.ini index 0eab64af4a8..78fdcbf97c8 100644 --- a/parser/htmlparser/tests/mochitest/mochitest.ini +++ b/parser/htmlparser/tests/mochitest/mochitest.ini @@ -25,6 +25,7 @@ support-files = file_bug672453_meta_restart.html file_bug672453_meta_unsupported.html file_bug672453_meta_utf16.html + file_bug688580.js file_bug672453_not_declared.html file_bug672453_meta_userdefined.html file_bug716579-16.html @@ -116,6 +117,8 @@ support-files = [test_bug655682.html] [test_bug667533.html] [test_bug672453.html] +[test_bug688580.html] +[test_bug688580.xhtml] [test_bug709083.html] [test_bug715112.html] [test_bug715739.html] diff --git a/parser/htmlparser/tests/mochitest/test_bug688580.html b/parser/htmlparser/tests/mochitest/test_bug688580.html new file mode 100644 index 00000000000..8bc75a892a8 --- /dev/null +++ b/parser/htmlparser/tests/mochitest/test_bug688580.html @@ -0,0 +1,64 @@ + + + + + + Test for Bug 688580 + + + + + + +Mozilla Bug 688580 +

+ +
+
+ + diff --git a/parser/htmlparser/tests/mochitest/test_bug688580.xhtml b/parser/htmlparser/tests/mochitest/test_bug688580.xhtml new file mode 100644 index 00000000000..1301376f2d6 --- /dev/null +++ b/parser/htmlparser/tests/mochitest/test_bug688580.xhtml @@ -0,0 +1,62 @@ + + + + Test for Bug 688580 + + + + + + +Mozilla Bug 688580 +

+ +
+
+ + diff --git a/toolkit/identity/tests/chrome/test_sandbox.xul b/toolkit/identity/tests/chrome/test_sandbox.xul index ebdd6b752b8..9aa15de0c62 100644 --- a/toolkit/identity/tests/chrome/test_sandbox.xul +++ b/toolkit/identity/tests/chrome/test_sandbox.xul @@ -26,7 +26,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=762993 "use strict"; -SimpleTest.expectAssertions(2); +SimpleTest.expectAssertions(1); SimpleTest.waitForExplicitFinish();