Bug 461555: Don't clear out the parser until all deferred scripts have executed to ensure that a document.write in a deferred script doesn't clear the page. r/sr=mrbkap

This commit is contained in:
Jonas Sicking 2009-01-14 17:25:21 -08:00
parent a02192e467
commit 583673c3ab
14 changed files with 140 additions and 38 deletions

View File

@ -1102,6 +1102,11 @@ public:
virtual void EnumerateExternalResources(nsSubDocEnumFunc aCallback,
void* aData) = 0;
/**
* Dispatch DOMContentLoaded and DOMFrameContentLoaded events
*/
virtual void DispatchContentLoadedEvents() = 0;
/**
* Return whether the document is currently showing (in the sense of
* OnPageShow() having been called already and OnPageHide() not having been

View File

@ -349,19 +349,21 @@ nsContentSink::ScriptAvailable(nsresult aResult,
mParser->ScriptExecuting();
}
if (count == 0) {
return NS_OK;
}
// aElement will not be in mScriptElements if a <script> was added
// using the DOM during loading, or if the script was inline and thus
// never blocked.
NS_ASSERTION(mScriptElements.IndexOf(aElement) == count - 1 ||
NS_ASSERTION(count == 0 ||
mScriptElements.IndexOf(aElement) == count - 1 ||
mScriptElements.IndexOf(aElement) == PRUint32(-1),
"script found at unexpected position");
// Check if this is the element we were waiting for
if (aElement != mScriptElements[count - 1]) {
if (count == 0 || aElement != mScriptElements[count - 1]) {
if (mDidGetReadyToCallDidBuildModelCall &&
!mScriptLoader->HasPendingOrCurrentScripts()) {
ContinueInterruptedParsingAsyncIfEnabled();
}
return NS_OK;
}
@ -387,7 +389,7 @@ nsContentSink::ScriptAvailable(nsresult aResult,
// script load, assuming that that error code means that the user
// stopped the load through some action (like clicking a link). See
// http://bugzilla.mozilla.org/show_bug.cgi?id=243392.
ContinueInterruptedParsingAsync();
ContinueInterruptedParsingAsyncIfEnabled();
}
}
@ -406,6 +408,10 @@ nsContentSink::ScriptEvaluated(nsresult aResult,
// Check if this is the element we were waiting for
PRInt32 count = mScriptElements.Count();
if (count == 0 || aElement != mScriptElements[count - 1]) {
if (mDidGetReadyToCallDidBuildModelCall &&
!mScriptLoader->HasPendingOrCurrentScripts()) {
ContinueInterruptedParsingAsyncIfEnabled();
}
return NS_OK;
}
@ -416,9 +422,7 @@ nsContentSink::ScriptEvaluated(nsresult aResult,
PostEvaluateScript(aElement);
}
if (mParser && mParser->IsParserEnabled()) {
ContinueInterruptedParsingAsync();
}
ContinueInterruptedParsingAsyncIfEnabled();
return NS_OK;
}
@ -1741,12 +1745,27 @@ nsContentSink::ContinueInterruptedParsingIfEnabled()
}
void
nsContentSink::ContinueInterruptedParsingAsync()
nsContentSink::ContinueInterruptedParsingAsyncIfEnabled()
{
nsCOMPtr<nsIRunnable> ev = new nsRunnableMethod<nsContentSink>(this,
&nsContentSink::ContinueInterruptedParsingIfEnabled);
if (mParser && mParser->IsParserEnabled()) {
nsCOMPtr<nsIRunnable> ev = new nsRunnableMethod<nsContentSink>(this,
&nsContentSink::ContinueInterruptedParsingIfEnabled);
NS_DispatchToCurrentThread(ev);
NS_DispatchToCurrentThread(ev);
}
}
PRBool
nsContentSink::ReadyToCallDidBuildModelImpl()
{
if (!mDidGetReadyToCallDidBuildModelCall) {
mDidGetReadyToCallDidBuildModelCall = PR_TRUE;
mDocument->DispatchContentLoadedEvents();
mScriptLoader->EndDeferringScripts();
}
return !mScriptLoader->HasPendingOrCurrentScripts();
}
// URIs: action, href, src, longdesc, usemap, cite

View File

@ -137,6 +137,7 @@ class nsContentSink : public nsICSSLoaderObserver,
NS_HIDDEN_(nsresult) DidProcessATokenImpl(void);
NS_HIDDEN_(void) WillBuildModelImpl(void);
NS_HIDDEN_(void) DidBuildModelImpl(void);
NS_HIDDEN_(PRBool) ReadyToCallDidBuildModelImpl(void);
NS_HIDDEN_(void) DropParserAndPerfHint(void);
void NotifyAppend(nsIContent* aContent, PRUint32 aStartIndex);
@ -298,7 +299,7 @@ private:
protected:
void ContinueInterruptedParsingAsync();
void ContinueInterruptedParsingAsyncIfEnabled();
void ContinueInterruptedParsingIfEnabled();
nsCOMPtr<nsIDocument> mDocument;
@ -349,6 +350,7 @@ protected:
PRUint8 mDeferredLayoutStart : 1;
// If true, we deferred notifications until sheets load
PRUint8 mDeferredFlushTags : 1;
PRUint8 mDidGetReadyToCallDidBuildModelCall : 1;
// -- Can interrupt parsing members --
PRUint32 mDelayTimerStart;

View File

@ -3954,10 +3954,6 @@ nsDocument::DispatchContentLoadedEvents()
} while (parent);
}
if (mScriptLoader) {
mScriptLoader->EndDeferringScripts();
}
UnblockOnload(PR_TRUE);
}
@ -3975,15 +3971,6 @@ nsDocument::EndLoad()
NS_DOCUMENT_NOTIFY_OBSERVERS(EndLoad, (this));
SetReadyStateInternal(READYSTATE_INTERACTIVE);
if (!mSynchronousDOMContentLoaded) {
nsRefPtr<nsIRunnable> ev =
new nsRunnableMethod<nsDocument>(this,
&nsDocument::DispatchContentLoadedEvents);
NS_DispatchToCurrentThread(ev);
} else {
DispatchContentLoadedEvents();
}
}
void

View File

@ -978,6 +978,7 @@ public:
ExternalResourceLoad** aPendingLoad);
virtual NS_HIDDEN_(void)
EnumerateExternalResources(nsSubDocEnumFunc aCallback, void* aData);
virtual void DispatchContentLoadedEvents();
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsDocument, nsIDocument)
@ -1018,8 +1019,6 @@ protected:
static PRBool CheckGetElementByIdArg(const nsIAtom* aId);
nsIdentifierMapEntry* GetElementByIdInternal(nsIAtom* aID);
void DispatchContentLoadedEvents();
void RetrieveRelevantHeaders(nsIChannel *aChannel);
static PRBool TryChannelCharset(nsIChannel *aChannel,
@ -1175,8 +1174,6 @@ protected:
PRPackedBool mDelayFrameLoaderInitialization:1;
PRPackedBool mSynchronousDOMContentLoaded:1;
// If true, we have an input encoding. If this is false, then the
// document was created entirely in memory
PRPackedBool mHaveInputEncoding:1;

View File

@ -205,6 +205,14 @@ public:
*/
void EndDeferringScripts();
/**
* Returns the number of pending scripts, deferred or not.
*/
PRUint32 HasPendingOrCurrentScripts()
{
return mCurrentScript || GetFirstPendingRequest();
}
/**
* Adds aURI to the preload list and starts loading it.
*

View File

@ -279,6 +279,7 @@ _TEST_FILES = test_bug5141.html \
file_XHRSendData.sjs \
file_XHRSendData_doc.xml \
file_XHRSendData_doc.xml^headers^ \
test_bug461555.html \
$(NULL)
# Disabled for now. Mochitest isn't reliable enough for these.

View File

@ -0,0 +1,64 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=461555
-->
<head>
<title>Test for Bug 461555</title>
<script type="application/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body onload="done()">
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=461555">Mozilla Bug 461555</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<script>
SimpleTest.waitForExplicitFinish();
function writeIt(n) {
document.write("<span>" + n + "</span>");
}
var recur = 0;
function externalScript() {
if (++recur == 3) {
return;
}
base = (recur-1) * 4
writeIt(7 + base);
s = document.createElement("script");
s.src = "data:text/plain,writeIt(" + (9+base) + ");writeIt(" + (10+base) + ");externalScript();";
document.body.appendChild(s);
writeIt(8 + base);
}
function done() {
nodes = document.getElementsByTagName('span');
is(nodes.length, 14, "wrong length");
for (i = 0; i < nodes.length; ++i) {
is(nodes[i].textContent, i+1, "wrong order");
}
SimpleTest.finish();
}
document.addEventListener("DOMContentLoaded", function() {
writeIt(3);
}, false);
writeIt(1);
</script>
<script defer>
writeIt(4);
</script>
<script>
writeIt(2);
</script>
<script defer src="data:text/plain,writeIt(5);writeIt(6);"></script>
<script defer src="data:text/plain,externalScript();"></script>

View File

@ -184,6 +184,7 @@ public:
NS_IMETHOD WillParse(void);
NS_IMETHOD WillBuildModel(void);
NS_IMETHOD DidBuildModel(void);
virtual PRBool ReadyToCallDidBuildModel(void);
NS_IMETHOD WillInterrupt(void);
NS_IMETHOD WillResume(void);
NS_IMETHOD SetParser(nsIParser* aParser);
@ -1836,6 +1837,12 @@ HTMLContentSink::DidBuildModel(void)
return NS_OK;
}
PRBool
HTMLContentSink::ReadyToCallDidBuildModel()
{
return ReadyToCallDidBuildModelImpl();
}
NS_IMETHODIMP
HTMLContentSink::SetParser(nsIParser* aParser)
{

View File

@ -383,6 +383,12 @@ nsXMLContentSink::DidBuildModel()
return NS_OK;
}
PRBool
nsXMLContentSink::ReadyToCallDidBuildModel()
{
return ReadyToCallDidBuildModelImpl();
}
NS_IMETHODIMP
nsXMLContentSink::OnDocumentCreated(nsIDocument* aResultDocument)
{

View File

@ -93,6 +93,7 @@ public:
NS_IMETHOD WillParse(void);
NS_IMETHOD WillBuildModel(void);
NS_IMETHOD DidBuildModel(void);
virtual PRBool ReadyToCallDidBuildModel(void);
NS_IMETHOD WillInterrupt(void);
NS_IMETHOD WillResume(void);
NS_IMETHOD SetParser(nsIParser* aParser);

View File

@ -540,10 +540,8 @@ nsXMLDocument::EndLoad()
mChannelIsPending = PR_FALSE;
mLoopingForSyncLoad = PR_FALSE;
mSynchronousDOMContentLoaded = (mLoadedAsData || mLoadedAsInteractiveData);
nsDocument::EndLoad();
if (mSynchronousDOMContentLoaded) {
mSynchronousDOMContentLoaded = PR_FALSE;
if (mLoadedAsData || mLoadedAsInteractiveData) {
nsDocument::SetReadyStateInternal(nsIDocument::READYSTATE_COMPLETE);
// Generate a document load event for the case when an XML
// document was loaded as pure data without any presentation

View File

@ -56,8 +56,8 @@
class nsIParser;
#define NS_ICONTENT_SINK_IID \
{ 0x94ec4df1, 0x6885, 0x4b1f, \
{ 0x85, 0x10, 0xe3, 0x5f, 0x4f, 0x36, 0xea, 0xaa } }
{ 0xcfa3643b, 0xee60, 0x4bf0, \
{ 0xbc, 0x83, 0x49, 0x95, 0xdb, 0xbc, 0xda, 0x75 } }
class nsIContentSink : public nsISupports {
public:
@ -88,6 +88,12 @@ public:
*/
NS_IMETHOD DidBuildModel()=0;
/**
* Thie method gets caller right before DidBuildModel is called.
* If false
*/
virtual PRBool ReadyToCallDidBuildModel() { return PR_TRUE; };
/**
* This method gets called when the parser gets i/o blocked,
* and wants to notify the sink that it may be a while before

View File

@ -1525,7 +1525,8 @@ nsParser::DidBuildModel(nsresult anErrorCode)
if (IsComplete()) {
if (mParserContext && !mParserContext->mPrevContext) {
if (mParserContext->mDTD) {
if (mParserContext->mDTD && mSink &&
mSink->ReadyToCallDidBuildModel()) {
result = mParserContext->mDTD->DidBuildModel(anErrorCode,PR_TRUE,this,mSink);
}