Bug 1945994 - Explicitly block the load event until the cached script is executed. r=nbp,emilio

Differential Revision: https://phabricator.services.mozilla.com/D236980
This commit is contained in:
Tooru Fujisawa
2025-02-06 14:05:03 +00:00
parent c593e037e2
commit af77f5495d
5 changed files with 123 additions and 0 deletions

View File

@@ -1335,6 +1335,45 @@ bool ScriptLoader::ProcessExternalScript(nsIScriptElement* aElement,
} }
return false; return false;
} }
} else {
// https://html.spec.whatwg.org/#prepare-the-script-element
//
// Step 33. If el's type is "classic" and el has a src attribute, or el's
// type is "module":
// ...
// Step 33.2. If el has an async attribute or el's force async is true:
// Step 33.2.1. Let scripts be el's preparation-time document's set of
// scripts that will execute as soon as possible.
// Step 33.2.2. Append el to scripts.
// ...
// Step 33.3. Otherwise, if el is not parser-inserted:
// Step 33.3.1. Let scripts be el's preparation-time document's list of
// scripts that will execute in order as soon as possible.
// Step 33.3.2. Append el to scripts.
// ...
//
// https://html.spec.whatwg.org/#the-end
//
// Step 7. Spin the event loop until the set of scripts that will execute
// as soon as possible and the list of scripts that will execute
// in order as soon as possible are empty.
//
// For scripts that creates the actual necko channel, the request is
// associated with the document's load group, and the load group manages
// the script set and the script list above implicitly, and the above
// "spin the event loop" is handled by IsBusy() check inside
// nsDocLoader::DocLoaderIsEmpty.
//
// https://searchfox.org/mozilla-central/rev/e85232b4b28ecc970240d39203e417d1c320623c/uriloader/base/nsDocLoader.cpp#704
//
// For in-memory-cached scripts, no channel is created, and those scripts
// should explicitly block the step 7 above.
//
// NOTE: IsAsyncScript represents both "async" and "force async".
if (request->GetScriptLoadContext()->IsAsyncScript() ||
parserMetadata == ParserMetadata::NotParserInserted) {
request->GetScriptLoadContext()->BlockOnload(mDocument);
}
} }
} }

View File

@@ -149,6 +149,13 @@ support-files = [
"counter_server.sjs", "counter_server.sjs",
] ]
["browser_scriptCache_load_events.js"]
skip-if = ["!nightly_build"]
support-files = [
"file_scriptCache_load_events.js",
"page_scriptCache_load_events.html",
]
["browser_scriptCache_perf_timeline.js"] ["browser_scriptCache_perf_timeline.js"]
skip-if = ["!nightly_build"] skip-if = ["!nightly_build"]
support-files = [ support-files = [

View File

@@ -0,0 +1,56 @@
const TEST_URL =
"https://example.com/browser/dom/tests/browser/page_scriptCache_load_events.html";
function clearAllCache() {
return new Promise(function (resolve) {
Services.clearData.deleteData(
Ci.nsIClearDataService.CLEAR_ALL_CACHES,
resolve
);
});
}
async function testOrder() {
await clearAllCache();
const tab = await BrowserTestUtils.openNewForegroundTab({
gBrowser,
url: TEST_URL,
});
// The script should be executed in between DOMContentLoaded and load events.
// Uncached cache.
let result = await SpecialPowers.spawn(tab.linkedBrowser, [], () => {
return content.document.getElementById("result").textContent;
});
is(result, "DOMContentLoaded+script+load");
await BrowserTestUtils.reloadTab(tab);
// Cached cache.
result = await SpecialPowers.spawn(tab.linkedBrowser, [], () => {
return content.document.getElementById("result").textContent;
});
is(result, "DOMContentLoaded+script+load");
BrowserTestUtils.removeTab(tab);
}
add_task(async function test_withoutNavigationCache() {
await SpecialPowers.pushPrefEnv({
set: [["dom.script_loader.navigation_cache", false]],
});
registerCleanupFunction(() => SpecialPowers.popPrefEnv());
await testOrder();
});
add_task(async function test_withNavigationCache() {
await SpecialPowers.pushPrefEnv({
set: [["dom.script_loader.navigation_cache", true]],
});
registerCleanupFunction(() => SpecialPowers.popPrefEnv());
await testOrder();
});

View File

@@ -0,0 +1 @@
document.getElementById("result").textContent += "+script";

View File

@@ -0,0 +1,20 @@
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<title>load events order test</title>
<script>
window.addEventListener("DOMContentLoaded", () => {
document.getElementById("result").textContent += "DOMContentLoaded";
const script = document.createElement("script");
script.src = "file_scriptCache_load_events.js";
document.getElementsByTagName("head")[0].appendChild(script);
});
function onLoad() {
document.getElementById("result").textContent += "+load";
}
</script>
</head>
<body onload="onLoad()">
<span id="result"></span>
</body>