mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
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:
parent
7f0c710d23
commit
27395b2f77
@ -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).
|
||||
*/
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -59,6 +59,11 @@ public:
|
||||
NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
|
||||
NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
|
||||
|
||||
nsScriptElement(PRUint32 aFromParser)
|
||||
: nsIScriptElement(aFromParser)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
// Internal methods
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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__
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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);
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
|
32
content/test/reftest/bug591981-1.html
Normal file
32
content/test/reftest/bug591981-1.html
Normal 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>
|
32
content/test/reftest/bug591981-2.html
Normal file
32
content/test/reftest/bug591981-2.html
Normal 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>
|
9
content/test/reftest/bug591981-ref.html
Normal file
9
content/test/reftest/bug591981-ref.html
Normal 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>
|
1
content/test/reftest/bug591981-script.js
Normal file
1
content/test/reftest/bug591981-script.js
Normal file
@ -0,0 +1 @@
|
||||
log("external");
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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|.
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user