Bug 371576: Execute scripts syncronously when inserted in the tree whenever possible. r/sr=bz

This commit is contained in:
jonas%sicking.cc 2007-03-01 08:11:40 +00:00
Родитель 67c697f0d1
Коммит 25f98dc2f4
10 изменённых файлов: 76 добавлений и 69 удалений

Просмотреть файл

@ -41,7 +41,7 @@
interface nsIScriptElement;
interface nsIURI;
[scriptable, uuid(501209d3-7edf-437d-9948-3c6d1c08ef7f)]
[scriptable, uuid(7b787204-76fb-4764-96f1-fb7a666db4f4)]
interface nsIScriptLoaderObserver : nsISupports {
/**
@ -54,10 +54,6 @@ interface nsIScriptLoaderObserver : nsISupports {
* will not occur.
* @param aElement The element being processed.
* @param aIsInline Is this an inline script or externally loaded?
* @param aWasPending Did script processing have to be delayed,
* either for loading of an external script or
* because processing of an earlier scheduled
* script was delayed?
* @param aURI What is the URI of the script (the document URI if
* it is inline).
* @param aLineNo At what line does the script appear (generally 1
@ -66,7 +62,6 @@ interface nsIScriptLoaderObserver : nsISupports {
void scriptAvailable(in nsresult aResult,
in nsIScriptElement aElement,
in boolean aIsInline,
in boolean aWasPending,
in nsIURI aURI,
in PRInt32 aLineNo);
@ -77,14 +72,9 @@ interface nsIScriptLoaderObserver : nsISupports {
* the script evaluation.
* @param aElement The element being processed.
* @param aIsInline Is this an inline script or externally loaded?
* @param aWasPending Did script processing have to be delayed,
* either for loading of an external script or
* because processing of an earlier scheduled
* script was delayed?
*/
void scriptEvaluated(in nsresult aResult,
in nsIScriptElement aElement,
in boolean aIsInline,
in boolean aWasPending);
in boolean aIsInline);
};

Просмотреть файл

@ -85,6 +85,7 @@
#include "nsIRequest.h"
#include "nsNodeUtils.h"
#include "nsIDOMNode.h"
#include "nsThreadUtils.h"
PRLogModuleInfo* gContentSinkLogModuleInfo;
@ -117,15 +118,14 @@ NS_IMETHODIMP
nsScriptLoaderObserverProxy::ScriptAvailable(nsresult aResult,
nsIScriptElement *aElement,
PRBool aIsInline,
PRBool aWasPending,
nsIURI *aURI,
PRInt32 aLineNo)
{
nsCOMPtr<nsIScriptLoaderObserver> inner = do_QueryReferent(mInner);
if (inner) {
return inner->ScriptAvailable(aResult, aElement, aIsInline, aWasPending,
aURI, aLineNo);
return inner->ScriptAvailable(aResult, aElement, aIsInline, aURI,
aLineNo);
}
return NS_OK;
@ -134,13 +134,12 @@ nsScriptLoaderObserverProxy::ScriptAvailable(nsresult aResult,
NS_IMETHODIMP
nsScriptLoaderObserverProxy::ScriptEvaluated(nsresult aResult,
nsIScriptElement *aElement,
PRBool aIsInline,
PRBool aWasPending)
PRBool aIsInline)
{
nsCOMPtr<nsIScriptLoaderObserver> inner = do_QueryReferent(mInner);
if (inner) {
return inner->ScriptEvaluated(aResult, aElement, aIsInline, aWasPending);
return inner->ScriptEvaluated(aResult, aElement, aIsInline);
}
return NS_OK;
@ -273,7 +272,6 @@ NS_IMETHODIMP
nsContentSink::ScriptAvailable(nsresult aResult,
nsIScriptElement *aElement,
PRBool aIsInline,
PRBool aWasPending,
nsIURI *aURI,
PRInt32 aLineNo)
{
@ -308,7 +306,7 @@ nsContentSink::ScriptAvailable(nsresult aResult,
} else {
mScriptElements.RemoveObjectAt(count - 1);
if (mParser && aWasPending && aResult != NS_BINDING_ABORTED) {
if (mParser && aResult != NS_BINDING_ABORTED) {
// Loading external script failed!. So, resume parsing since the parser
// got blocked when loading external script. See
// http://bugzilla.mozilla.org/show_bug.cgi?id=94903.
@ -317,7 +315,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.
mParser->ContinueInterruptedParsing();
ContinueInterruptedParsingAsync();
}
}
@ -327,8 +325,7 @@ nsContentSink::ScriptAvailable(nsresult aResult,
NS_IMETHODIMP
nsContentSink::ScriptEvaluated(nsresult aResult,
nsIScriptElement *aElement,
PRBool aIsInline,
PRBool aWasPending)
PRBool aIsInline)
{
// Check if this is the element we were waiting for
PRInt32 count = mScriptElements.Count();
@ -347,8 +344,8 @@ nsContentSink::ScriptEvaluated(nsresult aResult,
PostEvaluateScript(aElement);
}
if (mParser && mParser->IsParserEnabled() && aWasPending) {
mParser->ContinueInterruptedParsing();
if (mParser && mParser->IsParserEnabled()) {
ContinueInterruptedParsingAsync();
}
return NS_OK;
@ -1350,3 +1347,20 @@ nsContentSink::WillBuildModelImpl()
mScrolledToRefAlready = PR_FALSE;
}
void
nsContentSink::ContinueInterruptedParsing()
{
if (mParser) {
mParser->ContinueInterruptedParsing();
}
}
void
nsContentSink::ContinueInterruptedParsingAsync()
{
nsCOMPtr<nsIRunnable> ev = new nsRunnableMethod<nsContentSink>(this,
&nsContentSink::ContinueInterruptedParsing);
NS_DispatchToCurrentThread(ev);
}

Просмотреть файл

@ -209,6 +209,9 @@ private:
protected:
void ContinueInterruptedParsingAsync();
void ContinueInterruptedParsing();
nsCOMPtr<nsIDocument> mDocument;
nsCOMPtr<nsIParser> mParser;
nsCOMPtr<nsIURI> mDocumentURI;

Просмотреть файл

@ -66,7 +66,6 @@ NS_IMETHODIMP
nsScriptElement::ScriptAvailable(nsresult aResult,
nsIScriptElement *aElement,
PRBool aIsInline,
PRBool aWasPending,
nsIURI *aURI,
PRInt32 aLineNo)
{
@ -99,8 +98,7 @@ nsScriptElement::ScriptAvailable(nsresult aResult,
NS_IMETHODIMP
nsScriptElement::ScriptEvaluated(nsresult aResult,
nsIScriptElement *aElement,
PRBool aIsInline,
PRBool aWasPending)
PRBool aIsInline)
{
nsresult rv = NS_OK;
if (!aIsInline) {

Просмотреть файл

@ -121,7 +121,7 @@ public:
nsScriptLoadRequest(nsIScriptElement* aElement,
PRUint32 aVersion)
: mElement(aElement),
mLoading(PR_TRUE), mWasPending(PR_FALSE),
mLoading(PR_TRUE),
mIsInline(PR_TRUE),
mJSVersion(aVersion), mLineNo(1)
{
@ -131,17 +131,15 @@ public:
void FireScriptAvailable(nsresult aResult)
{
mElement->ScriptAvailable(aResult, mElement, mIsInline, mWasPending,
mURI, mLineNo);
mElement->ScriptAvailable(aResult, mElement, mIsInline, mURI, mLineNo);
}
void FireScriptEvaluated(nsresult aResult)
{
mElement->ScriptEvaluated(aResult, mElement, mIsInline, mWasPending);
mElement->ScriptEvaluated(aResult, mElement, mIsInline);
}
nsCOMPtr<nsIScriptElement> mElement;
PRPackedBool mLoading; // Are we still waiting for a load to complete?
PRPackedBool mWasPending; // Processed immediately or pending
PRPackedBool mIsInline; // Is the script inline or loaded?
nsString mScriptText; // Holds script for loaded scripts
PRUint32 mJSVersion;
@ -161,7 +159,8 @@ NS_IMPL_THREADSAFE_ISUPPORTS0(nsScriptLoadRequest)
nsScriptLoader::nsScriptLoader(nsIDocument *aDocument)
: mDocument(aDocument),
mBlockerCount(0),
mEnabled(PR_TRUE)
mEnabled(PR_TRUE),
mHadPendingScripts(PR_FALSE)
{
}
@ -226,7 +225,6 @@ IsScriptEventHandler(nsIScriptElement *aScriptElement)
return PR_FALSE;
}
/* void processScriptElement (in nsIScriptElement aElement, in nsIScriptLoaderObserver aObserver); */
nsresult
nsScriptLoader::ProcessScriptElement(nsIScriptElement *aElement)
{
@ -445,7 +443,6 @@ nsScriptLoader::ProcessScriptElement(nsIScriptElement *aElement)
request->mURI = scriptURI;
request->mIsInline = PR_FALSE;
request->mWasPending = PR_TRUE;
request->mLoading = PR_TRUE;
nsCOMPtr<nsILoadGroup> loadGroup = mDocument->GetDocumentLoadGroup();
@ -487,11 +484,8 @@ nsScriptLoader::ProcessScriptElement(nsIScriptElement *aElement)
// If we've got existing pending requests, add ourselves
// to this list.
if (ReadyToExecuteScripts() && mPendingRequests.Count() == 0) {
NS_ASSERTION(!request->mWasPending, "should not be pending");
return ProcessRequest(request);
}
request->mWasPending = PR_TRUE;
}
// Add the request to our pending requests list
@ -538,8 +532,8 @@ nsScriptLoader::FireScriptAvailable(nsresult aResult,
for (PRInt32 i = 0; i < mObservers.Count(); i++) {
nsCOMPtr<nsIScriptLoaderObserver> obs = mObservers[i];
obs->ScriptAvailable(aResult, aRequest->mElement,
aRequest->mIsInline, aRequest->mWasPending,
aRequest->mURI, aRequest->mLineNo);
aRequest->mIsInline, aRequest->mURI,
aRequest->mLineNo);
}
aRequest->FireScriptAvailable(aResult);
@ -552,7 +546,7 @@ nsScriptLoader::FireScriptEvaluated(nsresult aResult,
for (PRInt32 i = 0; i < mObservers.Count(); i++) {
nsCOMPtr<nsIScriptLoaderObserver> obs = mObservers[i];
obs->ScriptEvaluated(aResult, aRequest->mElement,
aRequest->mIsInline, aRequest->mWasPending);
aRequest->mIsInline);
}
aRequest->FireScriptEvaluated(aResult);

Просмотреть файл

@ -151,12 +151,25 @@ public:
*/
void AddExecuteBlocker()
{
++mBlockerCount;
if (!mBlockerCount++) {
mHadPendingScripts = mPendingRequests.Count() != 0;
}
}
void RemoveExecuteBlocker()
{
if (!--mBlockerCount) {
ProcessPendingRequestsAsync();
// If there were pending scripts then the newly added scripts will
// execute once whatever event triggers the pending scripts fires.
// However, due to syncronous loads and pushed event queues it's
// possible that the requests that were there have already been processed
// if so we need to process any new requests asyncronously.
// Ideally that should be fixed such that it can't happen.
if (mHadPendingScripts) {
ProcessPendingRequestsAsync();
}
else {
ProcessPendingRequests();
}
}
}
@ -214,6 +227,7 @@ protected:
nsCOMPtr<nsIScriptElement> mCurrentScript;
PRUint32 mBlockerCount;
PRPackedBool mEnabled;
PRPackedBool mHadPendingScripts;
};
#endif //__nsScriptLoader_h__

Просмотреть файл

@ -849,6 +849,10 @@ nsGenericHTMLElement::GetInnerHTML(nsAString& aInnerHTML)
nsresult
nsGenericHTMLElement::SetInnerHTML(const nsAString& aInnerHTML)
{
// This BeginUpdate/EndUpdate pair is important to make us reenable the
// scriptloader before the last EndUpdate call.
mozAutoDocUpdate updateBatch(GetCurrentDoc(), UPDATE_CONTENT_MODEL, PR_TRUE);
// Remove childnodes
nsContentUtils::SetNodeTextContent(this, EmptyString(), PR_FALSE);

Просмотреть файл

@ -18,8 +18,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=300691
<pre id="test">
<script type="text/javascript">
// First, setup. We'll be toggling these variables as we go.
// Note that scripts don't execute immediately when you put text in them --
// they wait for the currently-running script to finish.
var test1Ran = false;
var test2Ran = false;
var test3Ran = false;
@ -61,17 +59,17 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=300691
<script class="testbody" type="text/javascript">
/** Test for Bug 300691 **/
$("test2").appendChild(document.createTextNode("test2Ran = true"));
is(test2Ran, false, "Not yet 2!");
is(test2Ran, true, "Should be 2!");
$("test3").appendChild(document.createTextNode("test3Ran = true"));
is(test3Ran, false, "Not yet 3!");
is(test3Ran, false, "Should have run already 3!");
$("test4").appendChild(document.createTextNode("test4Ran = true"));
is(test4Ran, false, "Not yet 4!");
is(test4Ran, false, "Should have run already 4!");
$("test5").appendChild(document.createTextNode(" "));
$("test5").appendChild(document.createTextNode("test5Ran = true"));
is(test5Ran, false, "Not yet 5!");
is(test5Ran, false, "Should have run already 5!");
$("test6").appendChild(document.createTextNode(" "));
@ -101,31 +99,27 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=300691
<script type="text/javascript">
// Follow up on some of those
$("test6").appendChild(document.createTextNode("test6Ran = true"));
is(test6Ran, false, "Not yet 6!");
is(test6Ran, false, "Should have run already 6!");
$("test7").appendChild(document.createTextNode("test7Ran = true"));
is(test7Ran, false, "Not yet 7!");
is(test7Ran, true, "Should be 7!");
$("test8").insertBefore(document.createTextNode("test8Ran = true"),
$("test8").firstChild);
is(test8Ran, false, "Not yet 8!");
is(test8Ran, true, "Should be 8!");
$("test9").firstChild.data = "test9Ran = true";
is(test9Ran, false, "Not yet 9!");
is(test9Ran, true, "Should be 9!");
</script>
<script type="text/javascript">
is(test1Ran, true, "Should have run!");
is(test2Ran, true, "Should execute empty script on child append");
is(test3Ran, false, "Already executed test3 script once");
is(test4Ran, false,
"Shouldn't execute whitespace-only script on child append; should have executed it before");
is(test5Ran, true,
"By the time it tries to execute, it's got both textnodes");
"Should have executed whitespace-only script already");
is(test5Ran, false,
"Should have executed once the whitespace node was added");
is(test6Ran, false,
"Shouldn't execute whitespace-only script on child append 2; should have executed it when it became nonempty");
is(test7Ran, true, "Should execute empty script on child append 2");
is(test8Ran, true, "Should execute empty script on child insert");
is(test9Ran, true, "Should execute empty script if child gets some text");
"Should have executed once the whitespace node was added 2");
is(test10Ran, false, "Has an src; inline part shouldn't run");
is(test11Ran, true, "Script with src should have run");
is(test12Ran, true, "Setting src should execute script");

Просмотреть файл

@ -30,7 +30,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=300691
<![CDATA[
/** Test for Bug 300691 **/
$("test2").appendChild(document.createCDATASection("test2Ran = true"));
is(test2Ran, false, "Not yet 2!");
is(test2Ran, true, "Should be 2!");
$("test3").appendChild(document.createCDATASection(""));
]]>
@ -38,12 +38,10 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=300691
<script type="text/javascript">
// Follow up on some of those
$("test3").firstChild.data = "test3Ran = true";
is(test3Ran, false, "Not yet 3!");
is(test3Ran, true, "Should be 3!");
</script>
<script type="text/javascript">
is(test1Ran, true, "Should have run!");
is(test2Ran, true, "Should execute empty script on child append");
is(test3Ran, true, "Should execute empty script if child gets some text");
</script>
</pre>
</body>

Просмотреть файл

@ -1020,7 +1020,6 @@ NS_IMETHODIMP
txTransformNotifier::ScriptAvailable(nsresult aResult,
nsIScriptElement *aElement,
PRBool aIsInline,
PRBool aWasPending,
nsIURI *aURI,
PRInt32 aLineNo)
{
@ -1035,8 +1034,7 @@ txTransformNotifier::ScriptAvailable(nsresult aResult,
NS_IMETHODIMP
txTransformNotifier::ScriptEvaluated(nsresult aResult,
nsIScriptElement *aElement,
PRBool aIsInline,
PRBool aWasPending)
PRBool aIsInline)
{
if (mScriptElements.RemoveObject(aElement)) {
SignalTransformEnd();