Bug 28293: Implement defer attribute. r/sr=jst

This commit is contained in:
Jonas Sicking 2008-07-25 19:42:12 -07:00
Родитель 8046aeb008
Коммит 0a472c2700
9 изменённых файлов: 208 добавлений и 18 удалений

Просмотреть файл

@ -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