author | Jonas Sicking <jonas@sicking.cc> |
Mon, 20 Sep 2010 15:47:57 -0700 | |
changeset 54374 | 2b4ecab1e93a0631ef8fa068cac48e8f0bb436aa |
parent 54373 | 925c97269a4ca6131387c5a7b750fceecb15eaa3 |
child 54376 | f4978004fda110ae7874899d8ef9bb64dc4b4618 |
push id | 1 |
push user | root |
push date | Tue, 26 Apr 2011 22:38:44 +0000 |
treeherder | mozilla-beta@bfdb6e623a36 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | backout |
bugs | 591981 |
milestone | 2.0b7pre |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/content/base/public/nsIScriptElement.h +++ b/content/base/public/nsIScriptElement.h @@ -40,38 +40,36 @@ #define nsIScriptElement_h___ #include "nsISupports.h" #include "nsIURI.h" #include "nsCOMPtr.h" #include "nsIScriptLoaderObserver.h" #include "nsWeakPtr.h" #include "nsIParser.h" -#include "nsContentCreatorFunctions.h" #define NS_ISCRIPTELEMENT_IID \ { 0x6d625b30, 0xfac4, 0x11de, \ { 0x8a, 0x39, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66 } } /** * Internal interface implemented by script elements */ class nsIScriptElement : public nsIScriptLoaderObserver { public: NS_DECLARE_STATIC_IID_ACCESSOR(NS_ISCRIPTELEMENT_IID) - nsIScriptElement(PRUint32 aFromParser) + nsIScriptElement() : mLineNumber(0), - mAlreadyStarted(PR_FALSE), + mIsEvaluated(PR_FALSE), mMalformed(PR_FALSE), mDoneAddingChildren(PR_TRUE), mFrozen(PR_FALSE), mDefer(PR_FALSE), mAsync(PR_FALSE), - mParserCreated((PRUint8)aFromParser), mCreatorParser(nsnull) { } /** * Content type identifying the scripting language. Can be empty, in * which case javascript will be assumed. */ @@ -114,25 +112,16 @@ public: * Is the script async. Currently only supported by HTML scripts. */ PRBool GetScriptAsync() { NS_PRECONDITION(mFrozen, "Not ready for this call yet!"); 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; } PRUint32 GetScriptLineNumber() { return mLineNumber; } @@ -143,25 +132,17 @@ public: } PRBool IsMalformed() { return mMalformed; } void PreventExecution() { - mAlreadyStarted = PR_TRUE; - } - - void LoseParserInsertedness() - { - mFrozen = PR_FALSE; - mUri = nsnull; - mCreatorParser = nsnull; - mParserCreated = NS_NOT_FROM_PARSER; + mIsEvaluated = PR_TRUE; } void SetCreatorParser(nsIParser* aParser) { mCreatorParser = getter_AddRefs(NS_GetWeakReference(aParser)); } /** @@ -199,17 +180,17 @@ protected: /** * The start line number of the script. */ PRUint32 mLineNumber; /** * The "already started" flag per HTML5. */ - PRPackedBool mAlreadyStarted; + PRPackedBool mIsEvaluated; /** * The script didn't have an end tag. */ PRPackedBool mMalformed; /** * False if parser-inserted but the parser hasn't triggered running yet. @@ -227,21 +208,16 @@ protected: PRPackedBool mDefer; /** * The effective asyncness. */ PRPackedBool mAsync; /** - * Whether this element was parser-created. - */ - PRUint8 mParserCreated; - - /** * The effective src (or null if no src). */ nsCOMPtr<nsIURI> mUri; /** * The creator parser of a non-defer, non-async parser-inserted script. */ nsWeakPtr mCreatorParser;
--- a/content/base/src/nsContentSink.cpp +++ b/content/base/src/nsContentSink.cpp @@ -361,18 +361,18 @@ nsContentSink::ScriptAvailable(nsresult nsIScriptElement *aElement, PRBool aIsInline, nsIURI *aURI, PRInt32 aLineNo) { PRUint32 count = mScriptElements.Count(); // aElement will not be in mScriptElements if a <script> was added - // using the DOM during loading or if DoneAddingChildren did not return - // NS_ERROR_HTMLPARSER_BLOCK. + // using the DOM during loading, or if the script was inline and thus + // never blocked. NS_ASSERTION(count == 0 || mScriptElements.IndexOf(aElement) == PRInt32(count - 1) || mScriptElements.IndexOf(aElement) == -1, "script found at unexpected position"); // Check if this is the element we were waiting for if (count == 0 || aElement != mScriptElements[count - 1]) { return NS_OK;
--- a/content/base/src/nsScriptElement.cpp +++ b/content/base/src/nsScriptElement.cpp @@ -173,32 +173,32 @@ nsresult nsScriptElement::MaybeProcessScript() { nsCOMPtr<nsIContent> cont = do_QueryInterface((nsIScriptElement*) this); NS_ASSERTION(cont->DebugGetSlots()->mMutationObservers.Contains(this), "You forgot to add self as observer"); - if (mAlreadyStarted || !mDoneAddingChildren || !cont->IsInDoc() || + if (mIsEvaluated || !mDoneAddingChildren || !cont->IsInDoc() || mMalformed || !HasScriptContent()) { return NS_OK; } FreezeUriAsyncDefer(); if (InNonScriptingContainer(cont)) { // Make sure to flag ourselves as evaluated - mAlreadyStarted = PR_TRUE; + mIsEvaluated = PR_TRUE; return NS_OK; } nsresult scriptresult = NS_OK; nsRefPtr<nsScriptLoader> loader = cont->GetOwnerDoc()->ScriptLoader(); - mAlreadyStarted = PR_TRUE; + mIsEvaluated = PR_TRUE; scriptresult = loader->ProcessScriptElement(this); // The only error we don't ignore is NS_ERROR_HTMLPARSER_BLOCK // However we don't want to override other success values // (such as NS_CONTENT_SCRIPT_IS_EVENTHANDLER) if (NS_FAILED(scriptresult) && scriptresult != NS_ERROR_HTMLPARSER_BLOCK) { scriptresult = NS_OK;
--- a/content/base/src/nsScriptElement.h +++ b/content/base/src/nsScriptElement.h @@ -54,21 +54,16 @@ public: NS_DECL_NSISCRIPTLOADEROBSERVER // nsIMutationObserver NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED - nsScriptElement(PRUint32 aFromParser) - : nsIScriptElement(aFromParser) - { - } - protected: // Internal methods /** * Check if this element contains any script, linked or inline */ virtual PRBool HasScriptContent() = 0;
--- a/content/base/src/nsScriptLoader.cpp +++ b/content/base/src/nsScriptLoader.cpp @@ -70,17 +70,16 @@ #include "nsIParser.h" #include "nsThreadUtils.h" #include "nsDocShellCID.h" #include "nsIContentSecurityPolicy.h" #include "prlog.h" #include "nsIChannelPolicy.h" #include "nsChannelPolicy.h" #include "nsCRT.h" -#include "nsContentCreatorFunctions.h" #include "mozilla/FunctionTimer.h" #ifdef PR_LOGGING static PRLogModuleInfo* gCspPRLog; #endif using namespace mozilla::dom; @@ -113,16 +112,17 @@ public: PRBool IsPreload() { return mElement == nsnull; } 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; nsCOMPtr<nsIURI> mURI; nsCOMPtr<nsIURI> mFinalURI; PRInt32 mLineNo; }; @@ -135,35 +135,31 @@ NS_IMPL_THREADSAFE_ISUPPORTS0(nsScriptLo // ////////////////////////////////////////////////////////////// nsScriptLoader::nsScriptLoader(nsIDocument *aDocument) : mDocument(aDocument), mBlockerCount(0), mEnabled(PR_TRUE), mDeferEnabled(PR_FALSE), - mDocumentParsingDone(PR_FALSE) + mUnblockOnloadWhenDoneProcessing(PR_FALSE) { // enable logging for CSP #ifdef PR_LOGGING if (!gCspPRLog) gCspPRLog = PR_NewLogModule("CSP"); #endif } nsScriptLoader::~nsScriptLoader() { mObservers.Clear(); - 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 < mRequests.Count(); i++) { + mRequests[i]->FireScriptAvailable(NS_ERROR_ABORT); } for (PRInt32 i = 0; i < mAsyncRequests.Count(); i++) { mAsyncRequests[i]->FireScriptAvailable(NS_ERROR_ABORT); } // Unblock the kids, in case any of them moved to a different document // subtree in the meantime and therefore aren't actually going away. @@ -338,33 +334,16 @@ PRBool nsScriptLoader::PreloadURIComparator::Equals(const PreloadInfo &aPi, nsIURI * const &aURI) const { PRBool same; return NS_SUCCEEDED(aPi.mRequest->mURI->Equals(aURI, &same)) && 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) { // We need a document to evaluate scripts. NS_ENSURE_TRUE(mDocument, NS_ERROR_FAILURE); // Check to see if scripts has been turned off. if (!mEnabled || !mDocument->IsScriptEnabled()) { @@ -531,153 +510,145 @@ nsScriptLoader::ProcessScriptElement(nsI !nsContentUtils::IsChromeDoc(mDocument)) { NS_WARNING("Untrusted language called from non-chrome - ignored"); return NS_ERROR_NOT_AVAILABLE; } nsCOMPtr<nsIContent> eltContent(do_QueryInterface(aElement)); eltContent->SetScriptTypeID(typeID); - // Step 9. in the HTML5 spec + PRBool hadPendingRequests = !!GetFirstPendingRequest(); + // 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; - // XXX what if the charset attribute of the element and the charset - // of the preload don't match? + request->mJSVersion = version; + request->mDefer = mDeferEnabled && aElement->GetScriptDeferred() && + !aElement->GetScriptAsync(); mPreloads.RemoveElementAt(i); + rv = CheckContentPolicy(mDocument, aElement, request->mURI, type); - 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(); + if (NS_FAILED(rv)) { + // Note, we're dropping our last ref to request here. + return rv; + } - // 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(); - } - 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) { + // 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 ProcessRequest(request); } - // 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; - } - // inline script - nsCOMPtr<nsIContentSecurityPolicy> csp; - rv = mDocument->NodePrincipal()->GetCsp(getter_AddRefs(csp)); - NS_ENSURE_SUCCESS(rv, rv); + // Not done loading yet. Move into the real requests queue and wait. + if (aElement->GetScriptAsync()) { + mAsyncRequests.AppendObject(request); + } + else { + mRequests.AppendObject(request); + } - 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 (readyToRun) { + nsContentUtils::AddScriptRunner(NS_NewRunnableMethod(this, + &nsScriptLoader::ProcessPendingRequests)); + } - if (!inlineOK) { - PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("CSP blocked inline scripts (2)")); - return NS_ERROR_FAILURE; + return request->mDefer || aElement->GetScriptAsync() ? + NS_OK : 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); - request->mJSVersion = version; - request->mLoading = PR_FALSE; - request->mIsInline = PR_TRUE; - request->mURI = mDocument->GetDocumentURI(); - request->mLineNo = aElement->GetScriptLineNumber(); + + // 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)); + 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 (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)); + 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); + } + } + + // Add the request to our requests list + NS_ENSURE_TRUE(aElement->GetScriptAsync() ? + mAsyncRequests.AppendObject(request) : + mRequests.AppendObject(request), + NS_ERROR_OUT_OF_MEMORY); + + if (request->mDefer || aElement->GetScriptAsync()) { return NS_OK; } - 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; + + // 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)); } - // 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); + + // Added as pending request, now we can send blocking back + return NS_ERROR_HTMLPARSER_BLOCK; } nsresult nsScriptLoader::ProcessRequest(nsScriptLoadRequest* aRequest) { - NS_ASSERTION(nsContentUtils::IsSafeToRunScript(), - "Processing requests when running scripts is unsafe."); + NS_ASSERTION(ReadyToExecuteScripts() && nsContentUtils::IsSafeToRunScript(), + "Caller forgot to check ReadyToExecuteScripts()"); NS_ENSURE_ARG(aRequest); nsAFlatString* script; nsAutoString textData; NS_TIME_FUNCTION; nsCOMPtr<nsIDocument> doc; @@ -828,71 +799,81 @@ nsScriptLoader::EvaluateScript(nsScriptL if (stid == nsIProgrammingLanguage::JAVASCRIPT) { NS_ASSERTION(!::JS_IsExceptionPending(cx), "JS_ReportPendingException wasn't called"); ::JS_EndRequest(cx); } 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 (mParserBlockingRequest || !mPendingChildLoaders.IsEmpty()) { + if (GetFirstPendingRequest() || !mPendingChildLoaders.IsEmpty()) { nsCOMPtr<nsIRunnable> ev = NS_NewRunnableMethod(this, &nsScriptLoader::ProcessPendingRequests); NS_DispatchToCurrentThread(ev); } } void nsScriptLoader::ProcessPendingRequests() { - nsCOMPtr<nsScriptLoadRequest> request; - if (mParserBlockingRequest && - !mParserBlockingRequest->mLoading && - ReadyToExecuteScripts()) { - request.swap(mParserBlockingRequest); - // nsContentSink::ScriptAvailable unblocks the parser - ProcessRequest(request); - } + while (1) { + nsRefPtr<nsScriptLoadRequest> request; + if (ReadyToExecuteScripts()) { + request = GetFirstPendingRequest(); + if (request && !request->mLoading) { + mRequests.RemoveObject(request); + } + else { + request = nsnull; + } + } - PRInt32 i = 0; - while (mEnabled && i < mAsyncRequests.Count()) { - if (!mAsyncRequests[i]->mLoading) { - request = mAsyncRequests[i]; - mAsyncRequests.RemoveObjectAt(i); - ProcessRequest(request); - continue; + for (PRInt32 i = 0; + !request && mEnabled && i < mAsyncRequests.Count(); + ++i) { + if (!mAsyncRequests[i]->mLoading) { + request = mAsyncRequests[i]; + mAsyncRequests.RemoveObjectAt(i); + } } - ++i; - } - if (mDocumentParsingDone) { - while (mDeferRequests.Count() && !mDeferRequests[0]->mLoading) { - request = mDeferRequests[0]; - mDeferRequests.RemoveObjectAt(0); - ProcessRequest(request); - } + if (!request) + break; + + ProcessRequest(request); } while (!mPendingChildLoaders.IsEmpty() && ReadyToExecuteScripts()) { nsRefPtr<nsScriptLoader> child = mPendingChildLoaders[0]; mPendingChildLoaders.RemoveElementAt(0); child->RemoveExecuteBlocker(); } - if (mDocumentParsingDone && mDocument && - !mParserBlockingRequest && !mAsyncRequests.Count() && - !mDeferRequests.Count()) { + if (mUnblockOnloadWhenDoneProcessing && mDocument && + !GetFirstPendingRequest() && !mAsyncRequests.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. - mDocumentParsingDone = PR_FALSE; + mUnblockOnloadWhenDoneProcessing = PR_FALSE; mDocument->UnblockOnload(PR_TRUE); } } PRBool nsScriptLoader::ReadyToExecuteScripts() { // Make sure the SelfReadyToExecuteScripts check is first, so that @@ -1047,23 +1028,19 @@ nsScriptLoader::OnStreamComplete(nsIStre { nsScriptLoadRequest* request = static_cast<nsScriptLoadRequest*>(aContext); NS_ASSERTION(request, "null request in stream complete handler"); NS_ENSURE_TRUE(request, NS_ERROR_FAILURE); nsresult rv = PrepareLoadedRequest(request, aLoader, aStatus, aStringLen, aString); if (NS_FAILED(rv)) { - if (mDeferRequests.RemoveObject(request) || + if (mRequests.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()); } } // Process our request and/or any pending ones ProcessPendingRequests(); @@ -1124,20 +1101,19 @@ nsScriptLoader::PrepareLoadedRequest(nsS return NS_ERROR_NOT_AVAILABLE; } } // This assertion could fire errorously if we ran out of memory when // 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(mDeferRequests.IndexOf(aRequest) >= 0 || + NS_ASSERTION(mRequests.IndexOf(aRequest) >= 0 || mAsyncRequests.IndexOf(aRequest) >= 0 || - mPreloads.Contains(aRequest, PreloadRequestComparator()) || - mParserBlockingRequest, + mPreloads.Contains(aRequest, PreloadRequestComparator()), "aRequest should be pending!"); // Mark this as loaded aRequest->mLoading = PR_FALSE; return NS_OK; } @@ -1172,23 +1148,25 @@ nsScriptLoader::ShouldExecuteScript(nsID } void nsScriptLoader::ParsingComplete(PRBool aTerminated) { if (mDeferEnabled) { // Have to check because we apparently get ParsingComplete // without BeginDeferringScripts in some cases - mDocumentParsingDone = PR_TRUE; + mUnblockOnloadWhenDoneProcessing = PR_TRUE; } mDeferEnabled = PR_FALSE; if (aTerminated) { - mDeferRequests.Clear(); - mAsyncRequests.Clear(); - mParserBlockingRequest = nsnull; + mRequests.Clear(); + } else { + for (PRUint32 i = 0; i < (PRUint32)mRequests.Count(); ++i) { + mRequests[i]->mDefer = PR_FALSE; + } } // Have to call this even if aTerminated so we'll correctly unblock // onload and all. ProcessPendingRequests(); } void @@ -1198,16 +1176,18 @@ nsScriptLoader::PreloadURI(nsIURI *aURI, nsRefPtr<nsScriptLoadRequest> request = new nsScriptLoadRequest(nsnull, 0); if (!request) { return; } 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; } PreloadInfo *pi = mPreloads.AppendElement(); pi->mRequest = request; pi->mCharset = aCharset;
--- a/content/base/src/nsScriptLoader.h +++ b/content/base/src/nsScriptLoader.h @@ -55,17 +55,16 @@ class nsScriptLoadRequest; ////////////////////////////////////////////////////////////// // Script loader implementation ////////////////////////////////////////////////////////////// class nsScriptLoader : public nsIStreamLoaderObserver { - friend class nsScriptRequestProcessor; public: nsScriptLoader(nsIDocument* aDocument); virtual ~nsScriptLoader(); NS_DECL_ISUPPORTS NS_DECL_NSISTREAMLOADEROBSERVER /** @@ -220,30 +219,30 @@ public: */ void ParsingComplete(PRBool aTerminated); /** * Returns the number of pending scripts, deferred or not. */ PRUint32 HasPendingOrCurrentScripts() { - return mCurrentScript || mParserBlockingRequest; + return mCurrentScript || GetFirstPendingRequest(); } /** * Adds aURI to the preload list and starts loading it. * * @param aURI The URI of the external script. * @param aCharset The charset parameter for the script. * @param aType The type parameter for the script. */ virtual void PreloadURI(nsIURI *aURI, const nsAString &aCharset, const nsAString &aType); -private: +protected: /** * Helper function to check the content policy for a given request. */ static nsresult CheckContentPolicy(nsIDocument* aDocument, nsISupports *aContext, nsIURI *aURI, const nsAString &aType); @@ -290,21 +289,23 @@ private: const nsAFlatString& aScript); nsresult PrepareLoadedRequest(nsScriptLoadRequest* aRequest, nsIStreamLoader* aLoader, nsresult aStatus, 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 { nsRefPtr<nsScriptLoadRequest> mRequest; nsString mCharset; }; struct PreloadRequestComparator { @@ -320,12 +321,12 @@ private: nsTArray<PreloadInfo> mPreloads; nsCOMPtr<nsIScriptElement> mCurrentScript; // XXXbz do we want to cycle-collect these or something? Not sure. nsTArray< nsRefPtr<nsScriptLoader> > mPendingChildLoaders; PRUint32 mBlockerCount; PRPackedBool mEnabled; PRPackedBool mDeferEnabled; - PRPackedBool mDocumentParsingDone; + PRPackedBool mUnblockOnloadWhenDoneProcessing; }; #endif //__nsScriptLoader_h__
--- a/content/base/test/test_bug28293.html +++ b/content/base/test/test_bug28293.html @@ -4,42 +4,46 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=28293 --> <head> <title>Test for Bug 28293</title> <script type="text/javascript" src="/MochiKit/packed.js"></script> <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(); onload = function () { res+='2'; s = document.createElement('script'); s.textContent="res+='g';"; s.defer = true; document.body.appendChild(s); 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; document.body.appendChild(s); res+='4'; } function done() { - is(res, "AacBCDEFGeHIJb1M2g3i", "scripts executed in the wrong order"); - ok(scriptInsertedExternalExecuted, "Dynamic script did not block load"); + is(res, "AacBCDEFGeHIJfbd1M2g34hi", "scripts executed in the wrong order"); + ok(!fHadExecuted, "Dynamic script executed too late"); SimpleTest.finish(); } </script> </head> <body> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=28293">Mozilla Bug 28293</a> <script defer> @@ -51,16 +55,21 @@ res += 'c'; </script> <script> res += 'B'; </script> <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); res += 'E'; </script> <script> res += 'F'; document.addEventListener("DOMContentLoaded", function() { @@ -73,15 +82,18 @@ res += 'G'; </script> <script defer> res += 'e'; </script> <script src="file_bug28293.sjs?res+='H';"></script> <script> res += 'I'; s = document.createElement('script'); -s.src="file_bug28293.sjs?scriptInsertedExternalExecuted=true;"; +s.src="file_bug28293.sjs?fHadExecuted=(res.indexOf('f')>=0);"; document.body.appendChild(s); res += 'J'; </script> +<script defer> +res += 'f'; +</script> </body> </html>
--- a/content/base/test/test_bug28293.xhtml +++ b/content/base/test/test_bug28293.xhtml @@ -3,42 +3,46 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=28293 --> <head> <title>Test for Bug 28293</title> <script type="text/javascript" src="/MochiKit/packed.js"></script> <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(); onload = function () { res+='2'; s = document.createElement('script'); s.textContent="res+='g';"; s.defer = true; document.body.appendChild(s); 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; document.body.appendChild(s); res+='4'; } function done() { - is(res, "AacBCDEFGeHIJb1M2g3i", "scripts executed in the wrong order"); - ok(scriptInsertedExternalExecuted, "Dynamic script did not block load"); + is(res, "AacBCDEFGeHIJfbd1M2g34hi", "scripts executed in the wrong order"); + ok(!fHadExecuted, "Dynamic script executed too late"); SimpleTest.finish(); } </script> </head> <body> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=28293">Mozilla Bug 28293</a> <script defer="defer"> @@ -50,16 +54,21 @@ res += 'c'; </script> <script> res += 'B'; </script> <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); res += 'E'; </script> <script> res += 'F'; document.addEventListener("DOMContentLoaded", function() { @@ -73,16 +82,19 @@ res += 'G'; <script defer="defer"> res += 'e'; </script> <script src="file_bug28293.sjs?res+='H';"></script> <script> <![CDATA[ res += 'I'; s = document.createElement('script'); -s.src="file_bug28293.sjs?scriptInsertedExternalExecuted=true;"; +s.src="file_bug28293.sjs?fHadExecuted=(res.indexOf('f')>=0);"; document.body.appendChild(s); res += 'J'; ]]> </script> +<script defer="defer"> +res += 'f'; +</script> </body> </html>
--- a/content/html/content/src/nsHTMLScriptElement.cpp +++ b/content/html/content/src/nsHTMLScriptElement.cpp @@ -358,17 +358,16 @@ protected: 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); } nsHTMLScriptElement::~nsHTMLScriptElement() { } @@ -424,17 +423,17 @@ nsHTMLScriptElement::Clone(nsINodeInfo * return NS_ERROR_OUT_OF_MEMORY; } nsCOMPtr<nsINode> kungFuDeathGrip = it; nsresult rv = CopyInnerTo(it); NS_ENSURE_SUCCESS(rv, rv); // The clone should be marked evaluated if we are. - it->mAlreadyStarted = mAlreadyStarted; + it->mIsEvaluated = mIsEvaluated; it->mLineNumber = mLineNumber; it->mMalformed = mMalformed; kungFuDeathGrip.swap(*aResult); return NS_OK; } @@ -473,20 +472,21 @@ nsHTMLScriptElement::SetInnerHTML(const return nsContentUtils::SetNodeTextContent(this, aInnerHTML, PR_TRUE); } nsresult nsHTMLScriptElement::DoneAddingChildren(PRBool aHaveNotified) { mDoneAddingChildren = PR_TRUE; nsresult rv = MaybeProcessScript(); - if (!mAlreadyStarted) { - // Need to lose parser-insertedness here to allow another script to cause + if (!mIsEvaluated) { + // Need to thaw the script uri here to allow another script to cause // execution later. - LoseParserInsertedness(); + mFrozen = PR_FALSE; + mUri = nsnull; } return rv; } PRBool nsHTMLScriptElement::IsDoneAddingChildren() { return mDoneAddingChildren; @@ -550,17 +550,17 @@ nsHTMLScriptElement::MaybeProcessScript( { nsresult rv = nsScriptElement::MaybeProcessScript(); if (rv == NS_CONTENT_SCRIPT_IS_EVENTHANDLER) { // Don't return NS_CONTENT_SCRIPT_IS_EVENTHANDLER since callers can't deal rv = NS_OK; // We tried to evaluate the script but realized it was an eventhandler // mEvaluated will already be set at this point - NS_ASSERTION(mAlreadyStarted, "should have set mIsEvaluated already"); + NS_ASSERTION(mIsEvaluated, "should have set mIsEvaluated already"); NS_ASSERTION(!mScriptEventHandler, "how could we have an SEH already?"); mScriptEventHandler = new nsHTMLScriptEventHandler(this); NS_ENSURE_TRUE(mScriptEventHandler, NS_ERROR_OUT_OF_MEMORY); nsAutoString event_val; GetAttr(kNameSpaceID_None, nsGkAtoms::event, event_val); mScriptEventHandler->ParseEventString(event_val);
--- a/content/html/content/test/test_bug300691-2.html +++ b/content/html/content/test/test_bug300691-2.html @@ -4,25 +4,24 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=300691 --> <head> <title>Test for Bug 300691</title> <script type="text/javascript" src="/MochiKit/packed.js"></script> <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> </head> -<body onload="done();"> +<body> <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"> </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; var test3Ran = false; var test4Ran = false; var test5Ran = false; var test6Ran = false; var test7Ran = false; @@ -108,17 +107,16 @@ https://bugzilla.mozilla.org/show_bug.cg $("test8").insertBefore(document.createTextNode("test8Ran = true"), $("test8").firstChild); is(test8Ran, true, "Should be 8!"); $("test9").firstChild.data = "test9Ran = true"; 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, "Should have executed whitespace-only script already"); is(test5Ran, false, "Should have executed once the whitespace node was added"); is(test6Ran, false, "Should have executed once the whitespace node was added 2"); @@ -127,15 +125,13 @@ function done() { is(test12Ran, true, "Setting src should execute script"); is(test13Ran, true, "Setting src attribute should execute script"); is(test14aRan, true, "src attribute takes precedence over inline content"); is(test14bRan, false, "src attribute takes precedence over inline content 2"); is(test15aRan, true, "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> </html>
--- a/content/svg/content/src/nsSVGScriptElement.cpp +++ b/content/svg/content/src/nsSVGScriptElement.cpp @@ -130,17 +130,16 @@ NS_INTERFACE_TABLE_HEAD(nsSVGScriptEleme NS_INTERFACE_MAP_END_INHERITING(nsSVGScriptElementBase) //---------------------------------------------------------------------- // Implementation nsSVGScriptElement::nsSVGScriptElement(already_AddRefed<nsINodeInfo> aNodeInfo, PRUint32 aFromParser) : nsSVGScriptElementBase(aNodeInfo) - , nsScriptElement(aFromParser) { mDoneAddingChildren = !aFromParser; AddMutationObserver(this); } //---------------------------------------------------------------------- // nsIDOMNode methods @@ -156,17 +155,17 @@ nsSVGScriptElement::Clone(nsINodeInfo *a } nsCOMPtr<nsINode> kungFuDeathGrip = it; nsresult rv = it->Init(); rv |= CopyInnerTo(it); NS_ENSURE_SUCCESS(rv, rv); // The clone should be marked evaluated if we are. - it->mAlreadyStarted = mAlreadyStarted; + it->mIsEvaluated = mIsEvaluated; it->mLineNumber = mLineNumber; it->mMalformed = mMalformed; kungFuDeathGrip.swap(*aResult); return NS_OK; } @@ -274,20 +273,21 @@ nsSVGScriptElement::GetStringInfo() //---------------------------------------------------------------------- // nsIContent methods nsresult nsSVGScriptElement::DoneAddingChildren(PRBool aHaveNotified) { mDoneAddingChildren = PR_TRUE; nsresult rv = MaybeProcessScript(); - if (!mAlreadyStarted) { - // Need to lose parser-insertedness here to allow another script to cause + if (!mIsEvaluated) { + // Need to thaw the script uri here to allow another script to cause // execution later. - LoseParserInsertedness(); + mFrozen = PR_FALSE; + mUri = nsnull; } return rv; } PRBool nsSVGScriptElement::IsDoneAddingChildren() { return mDoneAddingChildren;
deleted file mode 100644 --- a/content/test/reftest/bug591981-1.html +++ /dev/null @@ -1,32 +0,0 @@ -<!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>
deleted file mode 100644 --- a/content/test/reftest/bug591981-2.html +++ /dev/null @@ -1,32 +0,0 @@ -<!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>
deleted file mode 100644 --- a/content/test/reftest/bug591981-ref.html +++ /dev/null @@ -1,9 +0,0 @@ -<!DOCTYPE html> -<html> -<head> -<title>Script-inserted script</title> -</head> -<body> -<div><p>internal</p><p>external</p></div> -</body> -</html>
deleted file mode 100644 --- a/content/test/reftest/bug591981-script.js +++ /dev/null @@ -1,1 +0,0 @@ -log("external");
--- a/content/test/reftest/reftest.list +++ b/content/test/reftest/reftest.list @@ -1,8 +1,6 @@ == bug453105.html bug453105-ref.html == optiontext.html optiontext-ref.html == bug456008.xhtml bug456008-ref.html == 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
--- a/dom/tests/mochitest/bugs/child_bug260264.html +++ b/dom/tests/mochitest/bugs/child_bug260264.html @@ -1,11 +1,10 @@ <html> <head> - <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script> <script type="application/javascript" src="utils_bug260264.js"></script> </head> <body> <iframe id="frame"></iframe> <script type="application/javascript"> document.getElementById("frame").src = alter_file(alter_host(location.href, "mochi.test:8888"), "grandchild_bug260264.html");
--- a/dom/tests/mochitest/bugs/grandchild_bug260264.html +++ b/dom/tests/mochitest/bugs/grandchild_bug260264.html @@ -1,11 +1,10 @@ <html> <head> - <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script> <script type="application/javascript" src="utils_bug260264.js"></script> </head> <body> <a id="nested link" href="javascript:(function(){})()">nested link</a> <script type="application/javascript"> var event = location.hash.split("#").pop(); send(document.getElementById("nested link"), event, function() { var popup = window.open("http://example.com"),
--- a/dom/tests/mochitest/bugs/test_bug260264.html +++ b/dom/tests/mochitest/bugs/test_bug260264.html @@ -2,17 +2,16 @@ <html> <!-- https://bugzilla.mozilla.org/show_bug.cgi?id=260264 --> <head> <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> <body> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=260264">Mozilla Bug 260264</a> <p id="display"> <a id="link" href="javascript:(function(){})()">link</a> </p>
--- a/dom/tests/mochitest/bugs/utils_bug260264.js +++ b/dom/tests/mochitest/bugs/utils_bug260264.js @@ -1,8 +1,15 @@ +(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|. */ function send(element, event, handler) { function unique_handler() { return handler.apply(this, arguments) } element.addEventListener(event, unique_handler, false); try { sendMouseEvent({ type: event }, element.id) } finally { element.removeEventListener(event, unique_handler, false) }