mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 11:25:00 +00:00
Bug 688580, run deferred scripts before DOMContentLoaded, r=hsivonen
--HG-- extra : rebase_source : e7e56d2d83c7b1bf29a3a5ea4efd7418cfbee1e0
This commit is contained in:
parent
2f082d5876
commit
452514e625
@ -127,8 +127,8 @@ typedef CallbackObjectHolder<NodeFilter, nsIDOMNodeFilter> 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,
|
||||
@ -2553,6 +2560,9 @@ protected:
|
||||
uint32_t mInSyncOperationCount;
|
||||
|
||||
nsRefPtr<mozilla::dom::XPathEvaluator> mXPathEvaluator;
|
||||
|
||||
uint32_t mBlockDOMContentLoaded;
|
||||
bool mDidFireDOMContentLoaded:1;
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocument, NS_IDOCUMENT_IID)
|
||||
|
@ -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<nsIRunnable> ev =
|
||||
NS_NewRunnableMethod(this, &nsDocument::DispatchContentLoadedEvents);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -291,6 +291,9 @@ private:
|
||||
uint32_t aStringLen,
|
||||
const uint8_t* aString);
|
||||
|
||||
void AddDeferRequest(nsScriptLoadRequest* aRequest);
|
||||
bool MaybeRemovedDeferRequests();
|
||||
|
||||
nsIDocument* mDocument; // [WEAK]
|
||||
nsCOMArray<nsIScriptLoaderObserver> mObservers;
|
||||
nsTArray<nsRefPtr<nsScriptLoadRequest> > mNonAsyncExternalScriptInsertedRequests;
|
||||
@ -325,6 +328,7 @@ private:
|
||||
bool mEnabled;
|
||||
bool mDeferEnabled;
|
||||
bool mDocumentParsingDone;
|
||||
bool mBlockingDOMContentLoaded;
|
||||
};
|
||||
|
||||
class nsAutoScriptLoaderDisabler
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
4
parser/htmlparser/tests/mochitest/file_bug688580.js
Normal file
4
parser/htmlparser/tests/mochitest/file_bug688580.js
Normal file
@ -0,0 +1,4 @@
|
||||
is(document.readyState, "interactive", "readyState should be interactive during defer.");
|
||||
is(state, "readyState interactive", "Bad state upon defer");
|
||||
state = "defer";
|
||||
|
@ -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]
|
||||
|
64
parser/htmlparser/tests/mochitest/test_bug688580.html
Normal file
64
parser/htmlparser/tests/mochitest/test_bug688580.html
Normal file
@ -0,0 +1,64 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=688580
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 688580</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript">
|
||||
|
||||
/** Test for Bug 688580 **/
|
||||
|
||||
// Expected order:
|
||||
// Test starting
|
||||
// readyState interactive
|
||||
// defer
|
||||
// DOMContentLoaded
|
||||
// readyState complete
|
||||
// load
|
||||
|
||||
var state = "Test starting";
|
||||
var readyStateCall = 0;
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
is(document.readyState, "loading", "Document should have been loading.");
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
is(document.readyState, "interactive", "readyState should be interactive during DOMContentLoaded.");
|
||||
is(state, "defer", "Bad state upon DOMContentLoaded");
|
||||
state = "DOMContentLoaded";
|
||||
});
|
||||
document.addEventListener("readystatechange", function () {
|
||||
readyStateCall++;
|
||||
if (readyStateCall == 1) {
|
||||
is(document.readyState, "interactive", "readyState should have changed to interactive.");
|
||||
is(state, "Test starting", "Bad state upon first readystatechange.");
|
||||
state = "readyState interactive";
|
||||
} else if (readyStateCall == 2) {
|
||||
is(document.readyState, "complete", "readyState should have changed to complete.");
|
||||
is(state, "DOMContentLoaded", "Bad state upon second readystatechange.");
|
||||
state = "readyState complete";
|
||||
} else {
|
||||
ok(false, "Too many readystatechanges");
|
||||
}
|
||||
});
|
||||
window.addEventListener("load", function () {
|
||||
is(document.readyState, "complete", "readyState should be complete during load.");
|
||||
is(state, "readyState complete", "Bad state upon load")
|
||||
state = "load";
|
||||
SimpleTest.finish();
|
||||
});
|
||||
</script>
|
||||
<script defer src="file_bug688580.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=688580">Mozilla Bug 688580</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
62
parser/htmlparser/tests/mochitest/test_bug688580.xhtml
Normal file
62
parser/htmlparser/tests/mochitest/test_bug688580.xhtml
Normal file
@ -0,0 +1,62 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=688580
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 688580</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript">
|
||||
|
||||
/** Test for Bug 688580 **/
|
||||
|
||||
// Expected order:
|
||||
// Test starting
|
||||
// readyState interactive
|
||||
// defer
|
||||
// DOMContentLoaded
|
||||
// readyState complete
|
||||
// load
|
||||
|
||||
var state = "Test starting";
|
||||
var readyStateCall = 0;
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
is(document.readyState, "loading", "Document should have been loading.");
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
is(document.readyState, "interactive", "readyState should be interactive during DOMContentLoaded.");
|
||||
is(state, "defer", "Bad state upon DOMContentLoaded");
|
||||
state = "DOMContentLoaded";
|
||||
});
|
||||
document.addEventListener("readystatechange", function () {
|
||||
readyStateCall++;
|
||||
if (readyStateCall == 1) {
|
||||
is(document.readyState, "interactive", "readyState should have changed to interactive.");
|
||||
is(state, "Test starting", "Bad state upon first readystatechange.");
|
||||
state = "readyState interactive";
|
||||
} else if (readyStateCall == 2) {
|
||||
is(document.readyState, "complete", "readyState should have changed to complete.");
|
||||
is(state, "DOMContentLoaded", "Bad state upon second readystatechange.");
|
||||
state = "readyState complete";
|
||||
} else {
|
||||
ok(false, "Too many readystatechanges");
|
||||
}
|
||||
});
|
||||
window.addEventListener("load", function () {
|
||||
is(document.readyState, "complete", "readyState should be complete during load.");
|
||||
is(state, "readyState complete", "Bad state upon load")
|
||||
state = "load";
|
||||
SimpleTest.finish();
|
||||
});
|
||||
</script>
|
||||
<script defer="" src="file_bug688580.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=688580">Mozilla Bug 688580</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -26,7 +26,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=762993
|
||||
|
||||
"use strict";
|
||||
|
||||
SimpleTest.expectAssertions(2);
|
||||
SimpleTest.expectAssertions(1);
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user