mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 19:35:51 +00:00
Bug 1053321 - Correctly pass info about 'defer' and 'async' attributes to HTML5 parser invoked script preload. r=bkelly
This commit is contained in:
parent
43fe35a023
commit
d8659dd692
@ -52,6 +52,8 @@ ScriptLoadRequest::ScriptLoadRequest(ScriptKind aKind,
|
||||
, mHasSourceMapURL(false)
|
||||
, mIsDefer(false)
|
||||
, mIsAsync(false)
|
||||
, mPreloadAsAsync(false)
|
||||
, mPreloadAsDefer(false)
|
||||
, mIsNonAsyncScriptInserted(false)
|
||||
, mIsXSLT(false)
|
||||
, mIsCanceled(false)
|
||||
|
@ -155,6 +155,8 @@ public:
|
||||
bool mHasSourceMapURL; // Does the HTTP header have a source map url?
|
||||
bool mIsDefer; // True if we live in mDeferRequests.
|
||||
bool mIsAsync; // True if we live in mLoadingAsyncRequests or mLoadedAsyncRequests.
|
||||
bool mPreloadAsAsync; // True if this is a preload request and the script is async
|
||||
bool mPreloadAsDefer; // True if this is a preload request and the script is defer
|
||||
bool mIsNonAsyncScriptInserted; // True if we live in mNonAsyncExternalScriptInsertedRequests
|
||||
bool mIsXSLT; // True if we live in mXSLTRequests.
|
||||
bool mIsCanceled; // True if we have been explicitly canceled.
|
||||
|
@ -972,15 +972,16 @@ ScriptLoader::StartLoad(ScriptLoadRequest* aRequest)
|
||||
}
|
||||
|
||||
nsIScriptElement* script = aRequest->mElement;
|
||||
nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(channel));
|
||||
bool async = script ? script->GetScriptAsync() : aRequest->mPreloadAsAsync;
|
||||
bool defer = script ? script->GetScriptDeferred() : aRequest->mPreloadAsDefer;
|
||||
|
||||
nsCOMPtr<nsIClassOfService> cos(do_QueryInterface(channel));
|
||||
if (cos) {
|
||||
if (aRequest->mScriptFromHead &&
|
||||
!(script && (script->GetScriptAsync() || script->GetScriptDeferred()))) {
|
||||
if (aRequest->mScriptFromHead && !async && !defer) {
|
||||
// synchronous head scripts block loading of most other non js/css
|
||||
// content such as images
|
||||
cos->AddClassFlags(nsIClassOfService::Leader);
|
||||
} else if (!(script && script->GetScriptDeferred())) {
|
||||
} else if (!defer) {
|
||||
// other scripts are neither blocked nor prioritized unless marked deferred
|
||||
cos->AddClassFlags(nsIClassOfService::Unblocked);
|
||||
}
|
||||
@ -2925,7 +2926,7 @@ ScriptLoader::PreloadURI(nsIURI* aURI, const nsAString& aCharset,
|
||||
const nsAString& aType,
|
||||
const nsAString& aCrossOrigin,
|
||||
const nsAString& aIntegrity,
|
||||
bool aScriptFromHead,
|
||||
bool aScriptFromHead, bool aAsync, bool aDefer,
|
||||
const mozilla::net::ReferrerPolicy aReferrerPolicy)
|
||||
{
|
||||
NS_ENSURE_TRUE_VOID(mDocument);
|
||||
@ -2958,6 +2959,8 @@ ScriptLoader::PreloadURI(nsIURI* aURI, const nsAString& aCharset,
|
||||
request->mIsInline = false;
|
||||
request->mReferrerPolicy = aReferrerPolicy;
|
||||
request->mScriptFromHead = aScriptFromHead;
|
||||
request->mPreloadAsAsync = aAsync;
|
||||
request->mPreloadAsDefer = aDefer;
|
||||
|
||||
nsresult rv = StartLoad(request);
|
||||
if (NS_FAILED(rv)) {
|
||||
|
@ -300,7 +300,7 @@ public:
|
||||
const nsAString& aType,
|
||||
const nsAString& aCrossOrigin,
|
||||
const nsAString& aIntegrity,
|
||||
bool aScriptFromHead,
|
||||
bool aScriptFromHead, bool aAsync, bool aDefer,
|
||||
const mozilla::net::ReferrerPolicy aReferrerPolicy);
|
||||
|
||||
/**
|
||||
|
@ -42,4 +42,8 @@ LOCAL_INCLUDES += [
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
MOCHITEST_MANIFESTS += [
|
||||
'test/mochitest.ini',
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
50
dom/script/test/file_blocked_script.sjs
Normal file
50
dom/script/test/file_blocked_script.sjs
Normal file
@ -0,0 +1,50 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
function setGlobalState(data, key)
|
||||
{
|
||||
x = { data: data, QueryInterface: function(iid) { return this } };
|
||||
x.wrappedJSObject = x;
|
||||
setObjectState(key, x);
|
||||
}
|
||||
|
||||
function getGlobalState(key)
|
||||
{
|
||||
var data;
|
||||
getObjectState(key, function(x) {
|
||||
data = x.wrappedJSObject.data;
|
||||
});
|
||||
return data;
|
||||
}
|
||||
|
||||
function handleRequest(request, response)
|
||||
{
|
||||
var query = request.queryString.split('&');
|
||||
switch (query[0]) {
|
||||
case "blocked":
|
||||
setGlobalState(response, query[1]);
|
||||
response.processAsync();
|
||||
break;
|
||||
|
||||
case "unblock":
|
||||
response.setStatusLine(request.httpVersion, 200, "OK");
|
||||
response.setHeader("Cache-Control", "no-cache", false);
|
||||
response.setHeader("Content-Type", "image/png", false);
|
||||
response.write("\x89PNG"); // just a broken image is enough for our purpose
|
||||
|
||||
var blockedResponse = getGlobalState(query[1]);
|
||||
blockedResponse.setStatusLine(request.httpVersion, 200, "OK");
|
||||
blockedResponse.setHeader("Cache-Control", "no-cache", false);
|
||||
blockedResponse.setHeader("Content-Type", "application/javascript", false);
|
||||
blockedResponse.write("window.script_executed_" + query[1] + " = true; ok(true, 'Script " + query[1] + " executed');");
|
||||
blockedResponse.finish();
|
||||
break;
|
||||
|
||||
default:
|
||||
response.setStatusLine(request.httpVersion, 400, "Bad request");
|
||||
break;
|
||||
}
|
||||
}
|
5
dom/script/test/mochitest.ini
Normal file
5
dom/script/test/mochitest.ini
Normal file
@ -0,0 +1,5 @@
|
||||
[DEFAULT]
|
||||
support-files =
|
||||
file_blocked_script.sjs
|
||||
|
||||
[test_bug1053321.html]
|
40
dom/script/test/test_bug1053321.html
Normal file
40
dom/script/test/test_bug1053321.html
Normal file
@ -0,0 +1,40 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!--
|
||||
This test confirms we don't block body referred sub-resources by head-referenced defer and async scripts.
|
||||
If this test times out, the two image requests, that unblock the two script requests, never happen, hence
|
||||
are unexpectedly blocked.
|
||||
-->
|
||||
|
||||
<head>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
|
||||
<!-- this script is not loaded until file_blocked_script.sjs?unblock&defer request is made,
|
||||
when this script is executed, it sets window.script_executed_defer to true
|
||||
-->
|
||||
<script defer src="file_blocked_script.sjs?blocked&defer"></script>
|
||||
|
||||
<!-- this script is not loaded until file_blocked_script.sjs?unblock&async request is made,
|
||||
when this script is executed, it sets window.script_executed_async to true
|
||||
-->
|
||||
<script async src="file_blocked_script.sjs?blocked&async"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script>
|
||||
// No need for an async test, we make it all before window.onload.
|
||||
//
|
||||
// We can't test whether the two scripts have not been executed here, since
|
||||
// preloads of the two images below (that unblock the two tested <head>
|
||||
// scripts) may happen sooner than this script executes.
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
ok(window.script_executed_defer, "Deferred script executed before DOMContentLoaded");
|
||||
});
|
||||
window.addEventListener("load", function() {
|
||||
ok(window.script_executed_async, "Async script executed before onload");
|
||||
}, true);
|
||||
</script>
|
||||
<img src="file_blocked_script.sjs?unblock&defer"/>
|
||||
<img src="file_blocked_script.sjs?unblock&async"/>
|
||||
</body>
|
@ -6,9 +6,12 @@
|
||||
#include "nsHtml5TreeOpExecutor.h"
|
||||
|
||||
nsHtml5SpeculativeLoad::nsHtml5SpeculativeLoad()
|
||||
:
|
||||
#ifdef DEBUG
|
||||
: mOpCode(eSpeculativeLoadUninitialized)
|
||||
mOpCode(eSpeculativeLoadUninitialized),
|
||||
#endif
|
||||
mIsAsync(false),
|
||||
mIsDefer(false)
|
||||
{
|
||||
MOZ_COUNT_CTOR(nsHtml5SpeculativeLoad);
|
||||
}
|
||||
@ -48,11 +51,11 @@ nsHtml5SpeculativeLoad::Perform(nsHtml5TreeOpExecutor* aExecutor)
|
||||
break;
|
||||
case eSpeculativeLoadScript:
|
||||
aExecutor->PreloadScript(mUrl, mCharset, mTypeOrCharsetSourceOrDocumentMode,
|
||||
mCrossOrigin, mIntegrity, false);
|
||||
mCrossOrigin, mIntegrity, false, mIsAsync, mIsDefer);
|
||||
break;
|
||||
case eSpeculativeLoadScriptFromHead:
|
||||
aExecutor->PreloadScript(mUrl, mCharset, mTypeOrCharsetSourceOrDocumentMode,
|
||||
mCrossOrigin, mIntegrity, true);
|
||||
mCrossOrigin, mIntegrity, true, mIsAsync, mIsDefer);
|
||||
break;
|
||||
case eSpeculativeLoadStyle:
|
||||
aExecutor->PreloadStyle(mUrl, mCharset, mCrossOrigin, mIntegrity);
|
||||
|
@ -128,7 +128,9 @@ class nsHtml5SpeculativeLoad {
|
||||
nsHtml5String aType,
|
||||
nsHtml5String aCrossOrigin,
|
||||
nsHtml5String aIntegrity,
|
||||
bool aParserInHead)
|
||||
bool aParserInHead,
|
||||
bool aAsync,
|
||||
bool aDefer)
|
||||
{
|
||||
NS_PRECONDITION(mOpCode == eSpeculativeLoadUninitialized,
|
||||
"Trying to reinitialize a speculative load!");
|
||||
@ -139,6 +141,8 @@ class nsHtml5SpeculativeLoad {
|
||||
aType.ToString(mTypeOrCharsetSourceOrDocumentMode);
|
||||
aCrossOrigin.ToString(mCrossOrigin);
|
||||
aIntegrity.ToString(mIntegrity);
|
||||
mIsAsync = aAsync;
|
||||
mIsDefer = aDefer;
|
||||
}
|
||||
|
||||
inline void InitStyle(nsHtml5String aUrl,
|
||||
@ -221,6 +225,13 @@ class nsHtml5SpeculativeLoad {
|
||||
|
||||
private:
|
||||
eHtml5SpeculativeLoad mOpCode;
|
||||
|
||||
/**
|
||||
* Whether the refering element has async and/or defer attributes.
|
||||
*/
|
||||
bool mIsAsync;
|
||||
bool mIsDefer;
|
||||
|
||||
nsString mUrl;
|
||||
nsString mReferrerPolicy;
|
||||
nsString mMetaCSP;
|
||||
|
@ -167,16 +167,20 @@ nsHtml5TreeBuilder::createElement(int32_t aNamespace, nsIAtom* aName,
|
||||
aAttributes->getValue(nsHtml5AttributeName::ATTR_CROSSORIGIN);
|
||||
nsHtml5String integrity =
|
||||
aAttributes->getValue(nsHtml5AttributeName::ATTR_INTEGRITY);
|
||||
bool async =
|
||||
aAttributes->contains(nsHtml5AttributeName::ATTR_ASYNC);
|
||||
bool defer =
|
||||
aAttributes->contains(nsHtml5AttributeName::ATTR_DEFER);
|
||||
mSpeculativeLoadQueue.AppendElement()->InitScript(
|
||||
url,
|
||||
charset,
|
||||
type,
|
||||
crossOrigin,
|
||||
integrity,
|
||||
mode == nsHtml5TreeBuilder::IN_HEAD);
|
||||
mCurrentHtmlScriptIsAsyncOrDefer =
|
||||
aAttributes->contains(nsHtml5AttributeName::ATTR_ASYNC) ||
|
||||
aAttributes->contains(nsHtml5AttributeName::ATTR_DEFER);
|
||||
mode == nsHtml5TreeBuilder::IN_HEAD,
|
||||
async,
|
||||
defer);
|
||||
mCurrentHtmlScriptIsAsyncOrDefer = async || defer;
|
||||
}
|
||||
} else if (nsGkAtoms::link == aName) {
|
||||
nsHtml5String rel =
|
||||
@ -279,7 +283,9 @@ nsHtml5TreeBuilder::createElement(int32_t aNamespace, nsIAtom* aName,
|
||||
type,
|
||||
crossOrigin,
|
||||
integrity,
|
||||
mode == nsHtml5TreeBuilder::IN_HEAD);
|
||||
mode == nsHtml5TreeBuilder::IN_HEAD,
|
||||
false,
|
||||
false);
|
||||
}
|
||||
} else if (nsGkAtoms::style == aName) {
|
||||
nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement();
|
||||
|
@ -947,14 +947,16 @@ nsHtml5TreeOpExecutor::PreloadScript(const nsAString& aURL,
|
||||
const nsAString& aType,
|
||||
const nsAString& aCrossOrigin,
|
||||
const nsAString& aIntegrity,
|
||||
bool aScriptFromHead)
|
||||
bool aScriptFromHead,
|
||||
bool aAsync,
|
||||
bool aDefer)
|
||||
{
|
||||
nsCOMPtr<nsIURI> uri = ConvertIfNotPreloadedYet(aURL);
|
||||
if (!uri) {
|
||||
return;
|
||||
}
|
||||
mDocument->ScriptLoader()->PreloadURI(uri, aCharset, aType, aCrossOrigin,
|
||||
aIntegrity, aScriptFromHead,
|
||||
aIntegrity, aScriptFromHead, aAsync, aDefer,
|
||||
mSpeculationReferrerPolicy);
|
||||
}
|
||||
|
||||
|
@ -252,7 +252,9 @@ class nsHtml5TreeOpExecutor final : public nsHtml5DocumentBuilder,
|
||||
const nsAString& aType,
|
||||
const nsAString& aCrossOrigin,
|
||||
const nsAString& aIntegrity,
|
||||
bool aScriptFromHead);
|
||||
bool aScriptFromHead,
|
||||
bool aAsync,
|
||||
bool aDefer);
|
||||
|
||||
void PreloadStyle(const nsAString& aURL, const nsAString& aCharset,
|
||||
const nsAString& aCrossOrigin,
|
||||
|
Loading…
Reference in New Issue
Block a user