diff --git a/content/base/public/nsIScriptElement.h b/content/base/public/nsIScriptElement.h index 8e65e1a07f4..ca7fc30040c 100644 --- a/content/base/public/nsIScriptElement.h +++ b/content/base/public/nsIScriptElement.h @@ -81,7 +81,12 @@ public: virtual void GetScriptText(nsAString& text) = 0; virtual void GetScriptCharset(nsAString& charset) = 0; - + + /** + * Is the script deferred. Currently only supported by HTML scripts. + */ + virtual PRBool GetScriptDeferred() = 0; + void SetScriptLineNumber(PRUint32 aLineNumber) { mLineNumber = aLineNumber; diff --git a/content/base/src/nsDocument.cpp b/content/base/src/nsDocument.cpp index a44f6395378..4a9e46430aa 100644 --- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -3174,6 +3174,10 @@ nsDocument::BeginLoad() // unblocking it while we know the document is loading. BlockOnload(); + if (mScriptLoader) { + mScriptLoader->BeginDeferringScripts(); + } + NS_DOCUMENT_NOTIFY_OBSERVERS(BeginLoad, (this)); } @@ -3417,6 +3421,10 @@ nsDocument::DispatchContentLoadedEvents() } while (parent); } + if (mScriptLoader) { + mScriptLoader->EndDeferringScripts(); + } + UnblockOnload(PR_TRUE); } diff --git a/content/base/src/nsScriptLoader.cpp b/content/base/src/nsScriptLoader.cpp index 7fb1f2bd2fb..ee224962345 100644 --- a/content/base/src/nsScriptLoader.cpp +++ b/content/base/src/nsScriptLoader.cpp @@ -98,6 +98,7 @@ public: nsCOMPtr 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; @@ -125,8 +126,8 @@ nsScriptLoader::~nsScriptLoader() { mObservers.Clear(); - for (PRInt32 i = 0; i < mPendingRequests.Count(); i++) { - mPendingRequests[i]->FireScriptAvailable(NS_ERROR_ABORT); + for (PRInt32 i = 0; i < mRequests.Count(); i++) { + mRequests[i]->FireScriptAvailable(NS_ERROR_ABORT); } // Unblock the kids, in case any of them moved to a different document @@ -379,6 +380,10 @@ nsScriptLoader::ProcessScriptElement(nsIScriptElement *aElement) nsRefPtr request = new nsScriptLoadRequest(aElement, version); NS_ENSURE_TRUE(request, NS_ERROR_OUT_OF_MEMORY); + request->mDefer = mDeferEnabled && aElement->GetScriptDeferred(); + + PRBool hadPendingRequests = !!GetFirstPendingRequest(); + // First check to see if this is an external script nsCOMPtr scriptURI = aElement->GetScriptURI(); if (scriptURI) { @@ -448,20 +453,23 @@ nsScriptLoader::ProcessScriptElement(nsIScriptElement *aElement) // If we've got existing pending requests, add ourselves // to this list. - if (mPendingRequests.Count() == 0 && ReadyToExecuteScripts() && - nsContentUtils::IsSafeToRunScript()) { + if (!request->mDefer && !hadPendingRequests && + ReadyToExecuteScripts() && nsContentUtils::IsSafeToRunScript()) { return ProcessRequest(request); } } - // Add the request to our pending requests list - NS_ENSURE_TRUE(mPendingRequests.AppendObject(request), + // Add the request to our requests list + NS_ENSURE_TRUE(mRequests.AppendObject(request), NS_ERROR_OUT_OF_MEMORY); + if (request->mDefer) { + 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 (mPendingRequests.Count() == 1 && !request->mLoading && - ReadyToExecuteScripts()) { + if (!request->mLoading && !hadPendingRequests && ReadyToExecuteScripts()) { nsContentUtils::AddScriptRunner(new nsRunnableMethod(this, &nsScriptLoader::ProcessPendingRequests)); } @@ -612,10 +620,22 @@ 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 (mPendingRequests.Count() || !mPendingChildLoaders.IsEmpty()) { + if (GetFirstPendingRequest() || !mPendingChildLoaders.IsEmpty()) { nsCOMPtr ev = new nsRunnableMethod(this, &nsScriptLoader::ProcessPendingRequests); @@ -627,9 +647,10 @@ void nsScriptLoader::ProcessPendingRequests() { nsRefPtr request; - while (mPendingRequests.Count() && ReadyToExecuteScripts() && - !(request = mPendingRequests[0])->mLoading) { - mPendingRequests.RemoveObjectAt(0); + while (ReadyToExecuteScripts() && + (request = GetFirstPendingRequest()) && + !request->mLoading) { + mRequests.RemoveObject(request); ProcessRequest(request); } @@ -800,7 +821,7 @@ nsScriptLoader::OnStreamComplete(nsIStreamLoader* aLoader, nsresult rv = PrepareLoadedRequest(request, aLoader, aStatus, aStringLen, aString); if (NS_FAILED(rv)) { - mPendingRequests.RemoveObject(request); + mRequests.RemoveObject(request); FireScriptAvailable(rv, request); } @@ -863,7 +884,7 @@ 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(mPendingRequests.IndexOf(aRequest) >= 0, + NS_ASSERTION(mRequests.IndexOf(aRequest) >= 0, "aRequest should be pending!"); // Mark this as loaded @@ -901,3 +922,14 @@ nsScriptLoader::ShouldExecuteScript(nsIDocument* aDocument, rv = channelPrincipal->Subsumes(docPrincipal, &subsumes); return NS_SUCCEEDED(rv) && subsumes; } + +void +nsScriptLoader::EndDeferringScripts() +{ + mDeferEnabled = PR_FALSE; + for (PRUint32 i = 0; i < mRequests.Count(); ++i) { + mRequests[i]->mDefer = PR_FALSE; + } + + ProcessPendingRequests(); +} diff --git a/content/base/src/nsScriptLoader.h b/content/base/src/nsScriptLoader.h index 04e76cfe8ea..d62de39558d 100644 --- a/content/base/src/nsScriptLoader.h +++ b/content/base/src/nsScriptLoader.h @@ -187,6 +187,24 @@ public: static PRBool ShouldExecuteScript(nsIDocument* aDocument, nsIChannel* aChannel); + /** + * Starts deferring deferred scripts and puts them in the mDeferredRequests + * queue instead. + */ + void BeginDeferringScripts() + { + mDeferEnabled = PR_TRUE; + } + + /** + * Stops defering scripts and immediately processes the mDeferredRequests + * queue. + * + * WARNING: This function will syncronously execute content scripts, so be + * prepared that the world might change around you. + */ + void EndDeferringScripts(); + protected: /** * Process any pending requests asyncronously (i.e. off an event) if there @@ -229,14 +247,18 @@ protected: PRUint32 aStringLen, const PRUint8* aString); + // Returns the first pending (non deferred) request + nsScriptLoadRequest* GetFirstPendingRequest(); + nsIDocument* mDocument; // [WEAK] nsCOMArray mObservers; - nsCOMArray mPendingRequests; + nsCOMArray mRequests; nsCOMPtr mCurrentScript; // XXXbz do we want to cycle-collect these or something? Not sure. nsTArray< nsRefPtr > mPendingChildLoaders; PRUint32 mBlockerCount; PRPackedBool mEnabled; + PRPackedBool mDeferEnabled; }; #endif //__nsScriptLoader_h__ diff --git a/content/base/test/Makefile.in b/content/base/test/Makefile.in index 3bb38224d7a..2903934dcf2 100644 --- a/content/base/test/Makefile.in +++ b/content/base/test/Makefile.in @@ -192,6 +192,8 @@ _TEST_FILES = test_bug5141.html \ test_NodeIterator_basics_filters.xhtml \ test_NodeIterator_mutations_1.xhtml \ test_NodeIterator_mutations_2.html \ + test_bug28293.html \ + file_bug28293.sjs \ $(NULL) libs:: $(_TEST_FILES) diff --git a/content/base/test/file_bug28293.sjs b/content/base/test/file_bug28293.sjs new file mode 100644 index 00000000000..0954ec105ee --- /dev/null +++ b/content/base/test/file_bug28293.sjs @@ -0,0 +1,5 @@ +function handleRequest(request, response) +{ + response.setHeader("Content-Type", "text/plain", false); + response.write(decodeURIComponent(request.queryString)); +} diff --git a/content/base/test/test_bug28293.html b/content/base/test/test_bug28293.html new file mode 100644 index 00000000000..a14c016d861 --- /dev/null +++ b/content/base/test/test_bug28293.html @@ -0,0 +1,99 @@ + + + + + Test for Bug 28293 + + + + + + +Mozilla Bug 28293 + + + + + + + + + + + + + + diff --git a/content/html/content/src/nsHTMLScriptElement.cpp b/content/html/content/src/nsHTMLScriptElement.cpp index 6f33059bad4..5e33af9e651 100644 --- a/content/html/content/src/nsHTMLScriptElement.cpp +++ b/content/html/content/src/nsHTMLScriptElement.cpp @@ -337,7 +337,8 @@ public: virtual void GetScriptType(nsAString& type); virtual already_AddRefed GetScriptURI(); virtual void GetScriptText(nsAString& text); - virtual void GetScriptCharset(nsAString& charset); + virtual void GetScriptCharset(nsAString& charset); + virtual PRBool GetScriptDeferred(); // nsIContent virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent, @@ -524,6 +525,15 @@ nsHTMLScriptElement::GetScriptCharset(nsAString& charset) GetCharset(charset); } +PRBool +nsHTMLScriptElement::GetScriptDeferred() +{ + PRBool defer; + GetDefer(&defer); + + return defer; +} + PRBool nsHTMLScriptElement::HasScriptContent() { diff --git a/content/svg/content/src/nsSVGScriptElement.cpp b/content/svg/content/src/nsSVGScriptElement.cpp index ff70d76f47f..c952f378641 100644 --- a/content/svg/content/src/nsSVGScriptElement.cpp +++ b/content/svg/content/src/nsSVGScriptElement.cpp @@ -78,7 +78,8 @@ public: virtual void GetScriptType(nsAString& type); virtual already_AddRefed GetScriptURI(); virtual void GetScriptText(nsAString& text); - virtual void GetScriptCharset(nsAString& charset); + virtual void GetScriptCharset(nsAString& charset); + virtual PRBool GetScriptDeferred(); // nsScriptElement virtual PRBool HasScriptContent(); @@ -212,6 +213,12 @@ nsSVGScriptElement::GetScriptCharset(nsAString& charset) charset.Truncate(); } +PRBool +nsSVGScriptElement::GetScriptDeferred() +{ + return PR_FALSE; +} + //---------------------------------------------------------------------- // nsScriptElement methods