зеркало из https://github.com/mozilla/pjs.git
Bug 28293: Implement defer attribute. r/sr=jst
This commit is contained in:
Родитель
8046aeb008
Коммит
0a472c2700
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -98,6 +98,7 @@ 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;
|
||||
|
@ -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<nsScriptLoadRequest> 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<nsIURI> 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<nsScriptLoader>(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<nsIRunnable> ev = new nsRunnableMethod<nsScriptLoader>(this,
|
||||
&nsScriptLoader::ProcessPendingRequests);
|
||||
|
||||
|
@ -627,9 +647,10 @@ void
|
|||
nsScriptLoader::ProcessPendingRequests()
|
||||
{
|
||||
nsRefPtr<nsScriptLoadRequest> 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();
|
||||
}
|
||||
|
|
|
@ -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<nsIScriptLoaderObserver> mObservers;
|
||||
nsCOMArray<nsScriptLoadRequest> mPendingRequests;
|
||||
nsCOMArray<nsScriptLoadRequest> mRequests;
|
||||
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;
|
||||
};
|
||||
|
||||
#endif //__nsScriptLoader_h__
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
function handleRequest(request, response)
|
||||
{
|
||||
response.setHeader("Content-Type", "text/plain", false);
|
||||
response.write(decodeURIComponent(request.queryString));
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=28293
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 28293</title>
|
||||
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
<script>
|
||||
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, "ABCDEFGHIJ1abcdefM2g34hi", "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>
|
||||
res += 'a';
|
||||
</script>
|
||||
<script defer src="data:text/plain,res+='b'"></script>
|
||||
<script defer>
|
||||
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() {
|
||||
res += '1'
|
||||
s = document.createElement('script');
|
||||
s.src="file_bug28293.sjs?res+='M';";
|
||||
document.body.appendChild(s);
|
||||
}, false);
|
||||
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?fHadExecuted=(res.indexOf('f')>=0);";
|
||||
document.body.appendChild(s);
|
||||
res += 'J';
|
||||
</script>
|
||||
<script defer>
|
||||
res += 'f';
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -337,7 +337,8 @@ public:
|
|||
virtual void GetScriptType(nsAString& type);
|
||||
virtual already_AddRefed<nsIURI> 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()
|
||||
{
|
||||
|
|
|
@ -78,7 +78,8 @@ public:
|
|||
virtual void GetScriptType(nsAString& type);
|
||||
virtual already_AddRefed<nsIURI> 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
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче