Bug 591981 - Make script-inserted inline scripts run right away, make script-inserted external scripts behave like async scripts and make document.write writing an inline script return at a predictable time. r=sicking a=blocking2.0-beta7

This commit is contained in:
Henri Sivonen 2010-09-21 12:48:40 -07:00
parent 7f0c710d23
commit 27395b2f77
20 changed files with 319 additions and 219 deletions

View File

@ -45,6 +45,7 @@
#include "nsIScriptLoaderObserver.h"
#include "nsWeakPtr.h"
#include "nsIParser.h"
#include "nsContentCreatorFunctions.h"
#define NS_ISCRIPTELEMENT_IID \
{ 0x6d625b30, 0xfac4, 0x11de, \
@ -57,14 +58,15 @@ class nsIScriptElement : public nsIScriptLoaderObserver {
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_ISCRIPTELEMENT_IID)
nsIScriptElement()
nsIScriptElement(PRUint32 aFromParser)
: mLineNumber(0),
mIsEvaluated(PR_FALSE),
mAlreadyStarted(PR_FALSE),
mMalformed(PR_FALSE),
mDoneAddingChildren(PR_TRUE),
mFrozen(PR_FALSE),
mDefer(PR_FALSE),
mAsync(PR_FALSE),
mParserCreated((PRUint8)aFromParser),
mCreatorParser(nsnull)
{
}
@ -117,6 +119,15 @@ public:
return mAsync;
}
/**
* Returns a constant defined in nsContentCreatorFunctions.h. Non-zero
* values mean parser-created and zero means not parser-created.
*/
PRUint32 GetParserCreated()
{
return mParserCreated;
}
void SetScriptLineNumber(PRUint32 aLineNumber)
{
mLineNumber = aLineNumber;
@ -137,7 +148,15 @@ public:
void PreventExecution()
{
mIsEvaluated = PR_TRUE;
mAlreadyStarted = PR_TRUE;
}
void LoseParserInsertedness()
{
mFrozen = PR_FALSE;
mUri = nsnull;
mCreatorParser = nsnull;
mParserCreated = NS_NOT_FROM_PARSER;
}
void SetCreatorParser(nsIParser* aParser)
@ -185,7 +204,7 @@ protected:
/**
* The "already started" flag per HTML5.
*/
PRPackedBool mIsEvaluated;
PRPackedBool mAlreadyStarted;
/**
* The script didn't have an end tag.
@ -212,6 +231,11 @@ protected:
*/
PRPackedBool mAsync;
/**
* Whether this element was parser-created.
*/
PRUint8 mParserCreated;
/**
* The effective src (or null if no src).
*/

View File

@ -366,8 +366,8 @@ nsContentSink::ScriptAvailable(nsresult aResult,
PRUint32 count = mScriptElements.Count();
// 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.
// using the DOM during loading or if DoneAddingChildren did not return
// NS_ERROR_HTMLPARSER_BLOCK.
NS_ASSERTION(count == 0 ||
mScriptElements.IndexOf(aElement) == PRInt32(count - 1) ||
mScriptElements.IndexOf(aElement) == -1,

View File

@ -178,7 +178,7 @@ nsScriptElement::MaybeProcessScript()
NS_ASSERTION(cont->DebugGetSlots()->mMutationObservers.Contains(this),
"You forgot to add self as observer");
if (mIsEvaluated || !mDoneAddingChildren || !cont->IsInDoc() ||
if (mAlreadyStarted || !mDoneAddingChildren || !cont->IsInDoc() ||
mMalformed || !HasScriptContent()) {
return NS_OK;
}
@ -187,13 +187,13 @@ nsScriptElement::MaybeProcessScript()
if (InNonScriptingContainer(cont)) {
// Make sure to flag ourselves as evaluated
mIsEvaluated = PR_TRUE;
mAlreadyStarted = PR_TRUE;
return NS_OK;
}
nsresult scriptresult = NS_OK;
nsRefPtr<nsScriptLoader> loader = cont->GetOwnerDoc()->ScriptLoader();
mIsEvaluated = PR_TRUE;
mAlreadyStarted = PR_TRUE;
scriptresult = loader->ProcessScriptElement(this);
// The only error we don't ignore is NS_ERROR_HTMLPARSER_BLOCK

View File

@ -59,6 +59,11 @@ public:
NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
nsScriptElement(PRUint32 aFromParser)
: nsIScriptElement(aFromParser)
{
}
protected:
// Internal methods

View File

@ -75,6 +75,7 @@
#include "nsIChannelPolicy.h"
#include "nsChannelPolicy.h"
#include "nsCRT.h"
#include "nsContentCreatorFunctions.h"
#include "mozilla/FunctionTimer.h"
@ -117,7 +118,6 @@ public:
nsCOMPtr<nsIScriptElement> mElement;
PRPackedBool mLoading; // Are we still waiting for a load to complete?
PRPackedBool mDefer; // Is execution defered?
PRPackedBool mIsInline; // Is the script inline or loaded?
nsString mScriptText; // Holds script for loaded scripts
PRUint32 mJSVersion;
@ -140,7 +140,7 @@ nsScriptLoader::nsScriptLoader(nsIDocument *aDocument)
mBlockerCount(0),
mEnabled(PR_TRUE),
mDeferEnabled(PR_FALSE),
mUnblockOnloadWhenDoneProcessing(PR_FALSE)
mDocumentParsingDone(PR_FALSE)
{
// enable logging for CSP
#ifdef PR_LOGGING
@ -153,8 +153,12 @@ nsScriptLoader::~nsScriptLoader()
{
mObservers.Clear();
for (PRInt32 i = 0; i < mRequests.Count(); i++) {
mRequests[i]->FireScriptAvailable(NS_ERROR_ABORT);
if (mParserBlockingRequest) {
mParserBlockingRequest->FireScriptAvailable(NS_ERROR_ABORT);
}
for (PRInt32 i = 0; i < mDeferRequests.Count(); i++) {
mDeferRequests[i]->FireScriptAvailable(NS_ERROR_ABORT);
}
for (PRInt32 i = 0; i < mAsyncRequests.Count(); i++) {
@ -339,6 +343,23 @@ nsScriptLoader::PreloadURIComparator::Equals(const PreloadInfo &aPi,
same;
}
class nsScriptRequestProcessor : public nsRunnable
{
private:
nsRefPtr<nsScriptLoader> mLoader;
nsRefPtr<nsScriptLoadRequest> mRequest;
public:
nsScriptRequestProcessor(nsScriptLoader* aLoader,
nsScriptLoadRequest* aRequest)
: mLoader(aLoader)
, mRequest(aRequest)
{}
NS_IMETHODIMP Run()
{
return mLoader->ProcessRequest(mRequest);
}
};
nsresult
nsScriptLoader::ProcessScriptElement(nsIScriptElement *aElement)
{
@ -515,135 +536,143 @@ nsScriptLoader::ProcessScriptElement(nsIScriptElement *aElement)
nsCOMPtr<nsIContent> eltContent(do_QueryInterface(aElement));
eltContent->SetScriptTypeID(typeID);
PRBool hadPendingRequests = !!GetFirstPendingRequest();
// Step 9. in the HTML5 spec
// Did we preload this request?
nsCOMPtr<nsIURI> scriptURI = aElement->GetScriptURI();
nsRefPtr<nsScriptLoadRequest> request;
if (scriptURI) {
// external script
nsTArray<PreloadInfo>::index_type i =
mPreloads.IndexOf(scriptURI.get(), 0, PreloadURIComparator());
if (i != nsTArray<PreloadInfo>::NoIndex) {
// preloaded
// note that a script-inserted script can steal a preload!
request = mPreloads[i].mRequest;
request->mElement = aElement;
request->mJSVersion = version;
request->mDefer = mDeferEnabled && aElement->GetScriptDeferred() &&
!aElement->GetScriptAsync();
// XXX what if the charset attribute of the element and the charset
// of the preload don't match?
mPreloads.RemoveElementAt(i);
rv = CheckContentPolicy(mDocument, aElement, request->mURI, type);
if (NS_FAILED(rv)) {
// Note, we're dropping our last ref to request here.
return rv;
NS_ENSURE_SUCCESS(rv, rv);
} else {
// not preloaded
request = new nsScriptLoadRequest(aElement, version);
NS_ENSURE_TRUE(request, NS_ERROR_OUT_OF_MEMORY);
request->mURI = scriptURI;
request->mIsInline = PR_FALSE;
request->mLoading = PR_TRUE;
rv = StartLoad(request, type);
NS_ENSURE_SUCCESS(rv, rv);
}
request->mJSVersion = version;
PRBool async = !aElement->GetParserCreated() || aElement->GetScriptAsync();
// we now have a request that may or may not be still loading
if (!async && aElement->GetScriptDeferred()) {
// We don't want to run this yet.
// If we come here, the script is a parser-created script and it has
// the defer attribute but not the async attribute. Since a
// a parser-inserted script is being run, we came here by the parser
// running the script, which means the parser is still alive and the
// parse is ongoing.
NS_ASSERTION(mDocument->GetCurrentContentSink(),
"Defer script on a document without an active parser; bug 592366.");
mDeferRequests.AppendObject(request);
return NS_OK;
}
if (async) {
mAsyncRequests.AppendObject(request);
if (!request->mLoading) {
// The script is available already. Run it ASAP when the event
// loop gets a chance to spin.
ProcessPendingRequestsAsync();
}
// Can we run the script now?
// This is true if we're done loading, the script isn't deferred and
// there are either no scripts or stylesheets to wait for, or the
// script is async
PRBool readyToRun =
!request->mLoading && !request->mDefer &&
((!hadPendingRequests && ReadyToExecuteScripts()) ||
aElement->GetScriptAsync());
if (readyToRun && nsContentUtils::IsSafeToRunScript()) {
return NS_OK;
}
if (!request->mLoading) {
// The request has already been loaded. If the script comes from the
// network stream, cheat for performance reasons and avoid a trip
// through the event loop.
if (aElement->GetParserCreated() == NS_FROM_PARSER_NETWORK) {
return ProcessRequest(request);
}
// Not done loading yet. Move into the real requests queue and wait.
if (aElement->GetScriptAsync()) {
mAsyncRequests.AppendObject(request);
}
else {
mRequests.AppendObject(request);
}
if (readyToRun) {
nsContentUtils::AddScriptRunner(NS_NewRunnableMethod(this,
&nsScriptLoader::ProcessPendingRequests));
}
return request->mDefer || aElement->GetScriptAsync() ?
NS_OK : NS_ERROR_HTMLPARSER_BLOCK;
// Otherwise, we've got a document.written script, make a trip through
// the event loop to hide the preload effects from the scripts on the
// Web page.
NS_ASSERTION(!mParserBlockingRequest,
"There can be only one parser-blocking script at a time");
mParserBlockingRequest = request;
ProcessPendingRequestsAsync();
return NS_ERROR_HTMLPARSER_BLOCK;
}
// The script hasn't loaded yet and is parser-inserted and non-async.
// It'll be executed when it has loaded.
NS_ASSERTION(!mParserBlockingRequest,
"There can be only one parser-blocking script at a time");
mParserBlockingRequest = request;
return NS_ERROR_HTMLPARSER_BLOCK;
}
// Create a request object for this script
request = new nsScriptLoadRequest(aElement, version);
NS_ENSURE_TRUE(request, NS_ERROR_OUT_OF_MEMORY);
// inline script
nsCOMPtr<nsIContentSecurityPolicy> csp;
rv = mDocument->NodePrincipal()->GetCsp(getter_AddRefs(csp));
NS_ENSURE_SUCCESS(rv, rv);
// First check to see if this is an external script
if (scriptURI) {
request->mDefer = mDeferEnabled && aElement->GetScriptDeferred() &&
!aElement->GetScriptAsync();
request->mURI = scriptURI;
request->mIsInline = PR_FALSE;
request->mLoading = PR_TRUE;
rv = StartLoad(request, type);
if (NS_FAILED(rv)) {
return rv;
}
} else {
// in-line script
nsCOMPtr<nsIContentSecurityPolicy> csp;
nsresult rv = mDocument->NodePrincipal()->GetCsp(getter_AddRefs(csp));
if (csp) {
PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("New ScriptLoader i ****with CSP****"));
PRBool inlineOK;
// this call will send violation reports when necessary
rv = csp->GetAllowsInlineScript(&inlineOK);
NS_ENSURE_SUCCESS(rv, rv);
if (csp) {
PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("New ScriptLoader i ****with CSP****"));
PRBool inlineOK;
// this call will send violation reports when necessary
rv = csp->GetAllowsInlineScript(&inlineOK);
NS_ENSURE_SUCCESS(rv, rv);
if (!inlineOK) {
PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("CSP blocked inline scripts (2)"));
return NS_ERROR_FAILURE;
}
}
request->mDefer = PR_FALSE;
request->mLoading = PR_FALSE;
request->mIsInline = PR_TRUE;
request->mURI = mDocument->GetDocumentURI();
request->mLineNo = aElement->GetScriptLineNumber();
// If we've got existing pending requests, add ourselves
// to this list.
if (!hadPendingRequests && ReadyToExecuteScripts() &&
nsContentUtils::IsSafeToRunScript()) {
return ProcessRequest(request);
if (!inlineOK) {
PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("CSP blocked inline scripts (2)"));
return NS_ERROR_FAILURE;
}
}
// Add the request to our requests list
NS_ENSURE_TRUE(aElement->GetScriptAsync() ?
mAsyncRequests.AppendObject(request) :
mRequests.AppendObject(request),
NS_ERROR_OUT_OF_MEMORY);
request = new nsScriptLoadRequest(aElement, version);
NS_ENSURE_TRUE(request, NS_ERROR_OUT_OF_MEMORY);
request->mJSVersion = version;
request->mLoading = PR_FALSE;
request->mIsInline = PR_TRUE;
request->mURI = mDocument->GetDocumentURI();
request->mLineNo = aElement->GetScriptLineNumber();
if (request->mDefer || aElement->GetScriptAsync()) {
if (aElement->GetParserCreated() == NS_NOT_FROM_PARSER) {
NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
"A script-inserted script is inserted without an update batch?");
nsContentUtils::AddScriptRunner(new nsScriptRequestProcessor(this,
request));
return NS_OK;
}
// If there weren't any pending requests before, and this one is
// ready to execute, do that as soon as it's safe.
if (!request->mLoading && !hadPendingRequests && ReadyToExecuteScripts()) {
nsContentUtils::AddScriptRunner(NS_NewRunnableMethod(this,
&nsScriptLoader::ProcessPendingRequests));
if (aElement->GetParserCreated() == NS_FROM_PARSER_NETWORK &&
!ReadyToExecuteScripts()) {
NS_ASSERTION(!mParserBlockingRequest,
"There can be only one parser-blocking script at a time");
mParserBlockingRequest = request;
return NS_ERROR_HTMLPARSER_BLOCK;
}
// Added as pending request, now we can send blocking back
return NS_ERROR_HTMLPARSER_BLOCK;
// We now have a document.written inline script or we have an inline script
// from the network but there is no style sheet that is blocking scripts.
// Don't check for style sheets blocking scripts in the document.write
// case to avoid style sheet network activity affecting when
// document.write returns. It's not really necessary to do this if
// there's no document.write currently on the call stack. However,
// this way matches IE more closely than checking if document.write
// is on the call stack.
NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
"Not safe to run a parser-inserted script?");
return ProcessRequest(request);
}
nsresult
nsScriptLoader::ProcessRequest(nsScriptLoadRequest* aRequest)
{
NS_ASSERTION(ReadyToExecuteScripts() && nsContentUtils::IsSafeToRunScript(),
"Caller forgot to check ReadyToExecuteScripts()");
NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
"Processing requests when running scripts is unsafe.");
NS_ENSURE_ARG(aRequest);
nsAFlatString* script;
@ -804,22 +833,10 @@ nsScriptLoader::EvaluateScript(nsScriptLoadRequest* aRequest,
return rv;
}
nsScriptLoadRequest*
nsScriptLoader::GetFirstPendingRequest()
{
for (PRInt32 i = 0; i < mRequests.Count(); ++i) {
if (!mRequests[i]->mDefer) {
return mRequests[i];
}
}
return nsnull;
}
void
nsScriptLoader::ProcessPendingRequestsAsync()
{
if (GetFirstPendingRequest() || !mPendingChildLoaders.IsEmpty()) {
if (mParserBlockingRequest || !mPendingChildLoaders.IsEmpty()) {
nsCOMPtr<nsIRunnable> ev = NS_NewRunnableMethod(this,
&nsScriptLoader::ProcessPendingRequests);
@ -830,45 +847,47 @@ nsScriptLoader::ProcessPendingRequestsAsync()
void
nsScriptLoader::ProcessPendingRequests()
{
while (1) {
nsRefPtr<nsScriptLoadRequest> request;
if (ReadyToExecuteScripts()) {
request = GetFirstPendingRequest();
if (request && !request->mLoading) {
mRequests.RemoveObject(request);
}
else {
request = nsnull;
}
}
for (PRInt32 i = 0;
!request && mEnabled && i < mAsyncRequests.Count();
++i) {
if (!mAsyncRequests[i]->mLoading) {
request = mAsyncRequests[i];
mAsyncRequests.RemoveObjectAt(i);
}
}
if (!request)
break;
nsCOMPtr<nsScriptLoadRequest> request;
if (mParserBlockingRequest &&
!mParserBlockingRequest->mLoading &&
ReadyToExecuteScripts()) {
request.swap(mParserBlockingRequest);
// nsContentSink::ScriptAvailable unblocks the parser
ProcessRequest(request);
}
PRInt32 i = 0;
while (mEnabled && i < mAsyncRequests.Count()) {
if (!mAsyncRequests[i]->mLoading) {
request = mAsyncRequests[i];
mAsyncRequests.RemoveObjectAt(i);
ProcessRequest(request);
continue;
}
++i;
}
if (mDocumentParsingDone) {
while (mDeferRequests.Count() && !mDeferRequests[0]->mLoading) {
request = mDeferRequests[0];
mDeferRequests.RemoveObjectAt(0);
ProcessRequest(request);
}
}
while (!mPendingChildLoaders.IsEmpty() && ReadyToExecuteScripts()) {
nsRefPtr<nsScriptLoader> child = mPendingChildLoaders[0];
mPendingChildLoaders.RemoveElementAt(0);
child->RemoveExecuteBlocker();
}
if (mUnblockOnloadWhenDoneProcessing && mDocument &&
!GetFirstPendingRequest() && !mAsyncRequests.Count()) {
if (mDocumentParsingDone && mDocument &&
!mParserBlockingRequest && !mAsyncRequests.Count() &&
!mDeferRequests.Count()) {
// No more pending scripts; time to unblock onload.
// OK to unblock onload synchronously here, since callers must be
// prepared for the world changing anyway.
mUnblockOnloadWhenDoneProcessing = PR_FALSE;
mDocumentParsingDone = PR_FALSE;
mDocument->UnblockOnload(PR_TRUE);
}
}
@ -1033,9 +1052,13 @@ nsScriptLoader::OnStreamComplete(nsIStreamLoader* aLoader,
nsresult rv = PrepareLoadedRequest(request, aLoader, aStatus, aStringLen,
aString);
if (NS_FAILED(rv)) {
if (mRequests.RemoveObject(request) ||
if (mDeferRequests.RemoveObject(request) ||
mAsyncRequests.RemoveObject(request)) {
FireScriptAvailable(rv, request);
} else if (mParserBlockingRequest == request) {
mParserBlockingRequest = nsnull;
// nsContentSink::ScriptAvailable unblocks the parser
FireScriptAvailable(rv, request);
} else {
mPreloads.RemoveElement(request, PreloadRequestComparator());
}
@ -1106,9 +1129,10 @@ nsScriptLoader::PrepareLoadedRequest(nsScriptLoadRequest* aRequest,
// inserting the request in the array. However it's an unlikely case
// so if you see this assertion it is likely something else that is
// wrong, especially if you see it more than once.
NS_ASSERTION(mRequests.IndexOf(aRequest) >= 0 ||
NS_ASSERTION(mDeferRequests.IndexOf(aRequest) >= 0 ||
mAsyncRequests.IndexOf(aRequest) >= 0 ||
mPreloads.Contains(aRequest, PreloadRequestComparator()),
mPreloads.Contains(aRequest, PreloadRequestComparator()) ||
mParserBlockingRequest,
"aRequest should be pending!");
// Mark this as loaded
@ -1153,15 +1177,13 @@ nsScriptLoader::ParsingComplete(PRBool aTerminated)
if (mDeferEnabled) {
// Have to check because we apparently get ParsingComplete
// without BeginDeferringScripts in some cases
mUnblockOnloadWhenDoneProcessing = PR_TRUE;
mDocumentParsingDone = PR_TRUE;
}
mDeferEnabled = PR_FALSE;
if (aTerminated) {
mRequests.Clear();
} else {
for (PRUint32 i = 0; i < (PRUint32)mRequests.Count(); ++i) {
mRequests[i]->mDefer = PR_FALSE;
}
mDeferRequests.Clear();
mAsyncRequests.Clear();
mParserBlockingRequest = nsnull;
}
// Have to call this even if aTerminated so we'll correctly unblock
@ -1181,8 +1203,6 @@ nsScriptLoader::PreloadURI(nsIURI *aURI, const nsAString &aCharset,
request->mURI = aURI;
request->mIsInline = PR_FALSE;
request->mLoading = PR_TRUE;
request->mDefer = PR_FALSE; // This is computed later when we go to execute the
// script.
nsresult rv = StartLoad(request, aType);
if (NS_FAILED(rv)) {
return;

View File

@ -60,6 +60,7 @@ class nsScriptLoadRequest;
class nsScriptLoader : public nsIStreamLoaderObserver
{
friend class nsScriptRequestProcessor;
public:
nsScriptLoader(nsIDocument* aDocument);
virtual ~nsScriptLoader();
@ -224,7 +225,7 @@ public:
*/
PRUint32 HasPendingOrCurrentScripts()
{
return mCurrentScript || GetFirstPendingRequest();
return mCurrentScript || mParserBlockingRequest;
}
/**
@ -237,7 +238,7 @@ public:
virtual void PreloadURI(nsIURI *aURI, const nsAString &aCharset,
const nsAString &aType);
protected:
private:
/**
* Helper function to check the content policy for a given request.
*/
@ -294,13 +295,11 @@ protected:
PRUint32 aStringLen,
const PRUint8* aString);
// Returns the first pending (non deferred) request
nsScriptLoadRequest* GetFirstPendingRequest();
nsIDocument* mDocument; // [WEAK]
nsCOMArray<nsIScriptLoaderObserver> mObservers;
nsCOMArray<nsScriptLoadRequest> mRequests;
nsCOMArray<nsScriptLoadRequest> mAsyncRequests;
nsCOMArray<nsScriptLoadRequest> mDeferRequests;
nsCOMPtr<nsScriptLoadRequest> mParserBlockingRequest;
// In mRequests, the additional information here is stored by the element.
struct PreloadInfo {
@ -326,7 +325,7 @@ protected:
PRUint32 mBlockerCount;
PRPackedBool mEnabled;
PRPackedBool mDeferEnabled;
PRPackedBool mUnblockOnloadWhenDoneProcessing;
PRPackedBool mDocumentParsingDone;
};
#endif //__nsScriptLoader_h__

View File

@ -9,6 +9,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=28293
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<script>
scriptInsertedExternalExecuted = false;
res = 'A';
SimpleTest.waitForExplicitFinish();
@ -23,11 +24,6 @@ onload = function () {
res+='3';
s = document.createElement('script');
s.src="file_bug28293.sjs?res+='h';";
s.defer = true;
document.body.appendChild(s);
s = document.createElement('script');
s.textContent="res+='i';done()";
s.defer = true;
@ -37,8 +33,8 @@ onload = function () {
}
function done() {
is(res, "AacBCDEFGeHIJfbd1M2g34hi", "scripts executed in the wrong order");
ok(!fHadExecuted, "Dynamic script executed too late");
is(res, "AacBCDEFGeHIJb1M2g3i", "scripts executed in the wrong order");
ok(scriptInsertedExternalExecuted, "Dynamic script did not block load");
SimpleTest.finish();
}
</script>
@ -59,11 +55,6 @@ res += 'B';
<script>
res += 'C';
s = document.createElement('script');
s.src="file_bug28293.sjs?res+='d';";
s.defer = true;
document.body.appendChild(s);
s = document.createElement('script');
s.textContent="res+='D';";
document.body.appendChild(s);
@ -87,13 +78,10 @@ res += 'e';
<script>
res += 'I';
s = document.createElement('script');
s.src="file_bug28293.sjs?fHadExecuted=(res.indexOf('f')>=0);";
s.src="file_bug28293.sjs?scriptInsertedExternalExecuted=true;";
document.body.appendChild(s);
res += 'J';
</script>
<script defer>
res += 'f';
</script>
</body>
</html>

View File

@ -8,6 +8,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=28293
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<script>
scriptInsertedExternalExecuted = false;
res = 'A';
SimpleTest.waitForExplicitFinish();
@ -22,11 +23,6 @@ onload = function () {
res+='3';
s = document.createElement('script');
s.src="file_bug28293.sjs?res+='h';";
s.defer = true;
document.body.appendChild(s);
s = document.createElement('script');
s.textContent="res+='i';done()";
s.defer = true;
@ -36,8 +32,8 @@ onload = function () {
}
function done() {
is(res, "AacBCDEFGeHIJfbd1M2g34hi", "scripts executed in the wrong order");
ok(!fHadExecuted, "Dynamic script executed too late");
is(res, "AacBCDEFGeHIJb1M2g3i", "scripts executed in the wrong order");
ok(scriptInsertedExternalExecuted, "Dynamic script did not block load");
SimpleTest.finish();
}
</script>
@ -58,11 +54,6 @@ res += 'B';
<script>
res += 'C';
s = document.createElement('script');
s.src="file_bug28293.sjs?res+='d';";
s.defer = true;
document.body.appendChild(s);
s = document.createElement('script');
s.textContent="res+='D';";
document.body.appendChild(s);
@ -87,14 +78,11 @@ res += 'e';
<![CDATA[
res += 'I';
s = document.createElement('script');
s.src="file_bug28293.sjs?fHadExecuted=(res.indexOf('f')>=0);";
s.src="file_bug28293.sjs?scriptInsertedExternalExecuted=true;";
document.body.appendChild(s);
res += 'J';
]]>
</script>
<script defer="defer">
res += 'f';
</script>
</body>
</html>

View File

@ -363,6 +363,7 @@ NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(Script)
nsHTMLScriptElement::nsHTMLScriptElement(already_AddRefed<nsINodeInfo> aNodeInfo,
PRUint32 aFromParser)
: nsGenericHTMLElement(aNodeInfo)
, nsScriptElement(aFromParser)
{
mDoneAddingChildren = !aFromParser;
AddMutationObserver(this);
@ -428,7 +429,7 @@ nsHTMLScriptElement::Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const
NS_ENSURE_SUCCESS(rv, rv);
// The clone should be marked evaluated if we are.
it->mIsEvaluated = mIsEvaluated;
it->mAlreadyStarted = mAlreadyStarted;
it->mLineNumber = mLineNumber;
it->mMalformed = mMalformed;
@ -477,11 +478,10 @@ nsHTMLScriptElement::DoneAddingChildren(PRBool aHaveNotified)
{
mDoneAddingChildren = PR_TRUE;
nsresult rv = MaybeProcessScript();
if (!mIsEvaluated) {
// Need to thaw the script uri here to allow another script to cause
if (!mAlreadyStarted) {
// Need to lose parser-insertedness here to allow another script to cause
// execution later.
mFrozen = PR_FALSE;
mUri = nsnull;
LoseParserInsertedness();
}
return rv;
}
@ -555,7 +555,7 @@ nsHTMLScriptElement::MaybeProcessScript()
// We tried to evaluate the script but realized it was an eventhandler
// mEvaluated will already be set at this point
NS_ASSERTION(mIsEvaluated, "should have set mIsEvaluated already");
NS_ASSERTION(mAlreadyStarted, "should have set mIsEvaluated already");
NS_ASSERTION(!mScriptEventHandler, "how could we have an SEH already?");
mScriptEventHandler = new nsHTMLScriptEventHandler(this);

View File

@ -9,7 +9,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=300691
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<body onload="done();">
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=300691">Mozilla Bug 300691</a>
<p id="display"></p>
<div id="content" style="display: none">
@ -17,6 +17,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=300691
</div>
<pre id="test">
<script type="text/javascript">
SimpleTest.waitForExplicitFinish();
// First, setup. We'll be toggling these variables as we go.
var test1Ran = false;
var test2Ran = false;
@ -112,6 +113,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=300691
is(test9Ran, true, "Should be 9!");
</script>
<script type="text/javascript">
function done() {
is(test1Ran, true, "Should have run!");
is(test3Ran, false, "Already executed test3 script once");
is(test4Ran, false,
@ -130,6 +132,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=300691
"src attribute load should have started before the attribute got removed");
is(test15bRan, false,
"src attribute still got executed, so this shouldn't have been");
SimpleTest.finish();
}
</script>
</pre>
</body>

View File

@ -135,6 +135,7 @@ NS_INTERFACE_MAP_END_INHERITING(nsSVGScriptElementBase)
nsSVGScriptElement::nsSVGScriptElement(already_AddRefed<nsINodeInfo> aNodeInfo,
PRUint32 aFromParser)
: nsSVGScriptElementBase(aNodeInfo)
, nsScriptElement(aFromParser)
{
mDoneAddingChildren = !aFromParser;
AddMutationObserver(this);
@ -160,7 +161,7 @@ nsSVGScriptElement::Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const
NS_ENSURE_SUCCESS(rv, rv);
// The clone should be marked evaluated if we are.
it->mIsEvaluated = mIsEvaluated;
it->mAlreadyStarted = mAlreadyStarted;
it->mLineNumber = mLineNumber;
it->mMalformed = mMalformed;
@ -278,11 +279,10 @@ nsSVGScriptElement::DoneAddingChildren(PRBool aHaveNotified)
{
mDoneAddingChildren = PR_TRUE;
nsresult rv = MaybeProcessScript();
if (!mIsEvaluated) {
// Need to thaw the script uri here to allow another script to cause
if (!mAlreadyStarted) {
// Need to lose parser-insertedness here to allow another script to cause
// execution later.
mFrozen = PR_FALSE;
mUri = nsnull;
LoseParserInsertedness();
}
return rv;
}

View File

@ -0,0 +1,32 @@
<!DOCTYPE html>
<html>
<head>
<title>Script-inserted script</title>
</head>
<body>
<div></div>
<script>
function log(text) {
var p = document.createElement("p");
p.appendChild(document.createTextNode(text));
document.getElementsByTagName("div")[0].appendChild(p);
}
var head = document.getElementsByTagName("head")[0];
var external = document.createElement("script");
external.src = "bug591981-script.js";
head.insertBefore(external, head.firstChild); // what jQuery does
var internal = document.createElement("script");
var data = "log('internal')";
try {
internal.text = data;
} catch(e) {
internal.appendChild(document.createTextNode(data));
}
head.insertBefore(internal, head.firstChild); // what jQuery does
</script>
</body>
</html>

View File

@ -0,0 +1,32 @@
<!DOCTYPE html>
<html>
<head>
<title>Script trying to execute parser-inserted non-executed scripts</title>
</head>
<body>
<div></div>
<script></script>
<script></script>
<script>
function log(text) {
var p = document.createElement("p");
p.appendChild(document.createTextNode(text));
document.getElementsByTagName("div")[0].appendChild(p);
}
var head = document.getElementsByTagName("head")[0];
var external = document.getElementsByTagName("script")[0];
external.src = "bug591981-script.js";
var internal = document.getElementsByTagName("script")[1];
var data = "log('internal')";
try {
internal.text = data;
} catch(e) {
internal.appendChild(document.createTextNode(data));
}
</script>
</body>
</html>

View File

@ -0,0 +1,9 @@
<!DOCTYPE html>
<html>
<head>
<title>Script-inserted script</title>
</head>
<body>
<div><p>internal</p><p>external</p></div>
</body>
</html>

View File

@ -0,0 +1 @@
log("external");

View File

@ -4,3 +4,5 @@
== bug439965.html bug439965-ref.html
== bug427779.xml bug427779-ref.xml
== bug559996.html bug559996-ref.html
== bug591981-1.html bug591981-ref.html
== bug591981-2.html bug591981-ref.html

View File

@ -1,5 +1,6 @@
<html>
<head>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<script type="application/javascript" src="utils_bug260264.js"></script>
</head>
<body>

View File

@ -1,5 +1,6 @@
<html>
<head>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<script type="application/javascript" src="utils_bug260264.js"></script>
</head>
<body>

View File

@ -7,6 +7,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=260264
<title>Test for Bug 260264</title>
<script type="application/javascript" src="/MochiKit/packed.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<script type="application/javascript" src="utils_bug260264.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>

View File

@ -1,10 +1,3 @@
(function() {
// For sendMouseEvent:
document.getElementsByTagName("head").item(0)
.appendChild(document.createElement("script")).src =
"/tests/SimpleTest/EventUtils.js";
})();
/**
* Dispatches |handler| to |element|, as if fired in response to |event|.
*/