commit f70e74238bc9d8ab7b816f2b66b85cc9d9c45d02
parent f211bee4de135ca2126ff3d277c19e42b6975376
Author: Tooru Fujisawa <arai_a@mac.com>
Date: Fri, 31 Oct 2025 05:22:08 +0000
Bug 1980154 - Part 4: Use SharedScriptCache for disk cache handling when navigation cache is enabled. r=nbp
Differential Revision: https://phabricator.services.mozilla.com/D270524
Diffstat:
5 files changed, 113 insertions(+), 36 deletions(-)
diff --git a/dom/base/test/browser_script_loader_js_cache.js b/dom/base/test/browser_script_loader_js_cache.js
@@ -1,9 +1,9 @@
const BASE_URL = "http://mochi.test:8888/browser/dom/base/test/";
-function ev(event, file, hasElement = true) {
+function ev(event, file, hasElement = !!file) {
return {
event,
- url: BASE_URL + file,
+ url: file ? BASE_URL + file : undefined,
hasElement,
};
}
@@ -23,7 +23,7 @@ async function contentTask(item) {
const event = item.events[0];
if (
event.event === param.event &&
- event.url === param.url &&
+ (!param.url || event.url === param.url) &&
(event.hasElement ? param.id === "watchme" : !param.id)
) {
dump("@@@ Got expected event: " + data + "\n");
@@ -232,6 +232,13 @@ add_task(async function testMemoryCache() {
],
});
+ // If in-memory cache is enabled, the disk cache is handled by the
+ // SharedScriptCache, and following differences happen:
+ // * diskcache:disabled and diskcache:register are not notified for
+ // each script
+ // * diskcache:noschedule is notified without associated script
+ // if there's no script to be saved
+
await runTests([
// A small file should be saved to the memory on the 1st load, and used on
// the 2nd load. But it shouldn't be saved to the disk cache.
@@ -244,7 +251,7 @@ add_task(async function testMemoryCache() {
ev("load:source", "file_js_cache_small.js"),
ev("memorycache:saved", "file_js_cache_small.js"),
ev("evaluate:classic", "file_js_cache_small.js"),
- ev("diskcache:disabled", "file_js_cache_small.js"),
+ ev("diskcache:noschedule"),
],
},
{
@@ -252,7 +259,7 @@ add_task(async function testMemoryCache() {
events: [
ev("load:memorycache", "file_js_cache_small.js"),
ev("evaluate:classic", "file_js_cache_small.js"),
- ev("diskcache:disabled", "file_js_cache_small.js"),
+ ev("diskcache:noschedule"),
],
},
{
@@ -260,7 +267,7 @@ add_task(async function testMemoryCache() {
events: [
ev("load:memorycache", "file_js_cache_small.js"),
ev("evaluate:classic", "file_js_cache_small.js"),
- ev("diskcache:disabled", "file_js_cache_small.js"),
+ ev("diskcache:noschedule"),
],
},
{
@@ -268,7 +275,7 @@ add_task(async function testMemoryCache() {
events: [
ev("load:memorycache", "file_js_cache_small.js"),
ev("evaluate:classic", "file_js_cache_small.js"),
- ev("diskcache:disabled", "file_js_cache_small.js"),
+ ev("diskcache:noschedule"),
],
},
],
@@ -288,7 +295,7 @@ add_task(async function testMemoryCache() {
ev("load:source", "file_js_cache_large.js"),
ev("memorycache:saved", "file_js_cache_large.js"),
ev("evaluate:classic", "file_js_cache_large.js"),
- ev("diskcache:disabled", "file_js_cache_large.js"),
+ ev("diskcache:noschedule"),
],
},
{
@@ -296,7 +303,7 @@ add_task(async function testMemoryCache() {
events: [
ev("load:memorycache", "file_js_cache_large.js"),
ev("evaluate:classic", "file_js_cache_large.js"),
- ev("diskcache:disabled", "file_js_cache_large.js"),
+ ev("diskcache:noschedule"),
],
},
{
@@ -304,7 +311,7 @@ add_task(async function testMemoryCache() {
events: [
ev("load:memorycache", "file_js_cache_large.js"),
ev("evaluate:classic", "file_js_cache_large.js"),
- ev("diskcache:disabled", "file_js_cache_large.js"),
+ ev("diskcache:noschedule"),
],
},
{
@@ -312,7 +319,6 @@ add_task(async function testMemoryCache() {
events: [
ev("load:memorycache", "file_js_cache_large.js"),
ev("evaluate:classic", "file_js_cache_large.js"),
- ev("diskcache:register", "file_js_cache_large.js"),
ev("diskcache:saved", "file_js_cache_large.js", false),
],
},
@@ -321,7 +327,7 @@ add_task(async function testMemoryCache() {
events: [
ev("load:memorycache", "file_js_cache_large.js"),
ev("evaluate:classic", "file_js_cache_large.js"),
- ev("diskcache:disabled", "file_js_cache_large.js"),
+ ev("diskcache:noschedule"),
],
},
{
@@ -329,7 +335,7 @@ add_task(async function testMemoryCache() {
events: [
ev("load:memorycache", "file_js_cache_large.js"),
ev("evaluate:classic", "file_js_cache_large.js"),
- ev("diskcache:disabled", "file_js_cache_large.js"),
+ ev("diskcache:noschedule"),
],
},
@@ -340,7 +346,7 @@ add_task(async function testMemoryCache() {
ev("load:diskcache", "file_js_cache_large.js"),
ev("memorycache:saved", "file_js_cache_large.js"),
ev("evaluate:classic", "file_js_cache_large.js"),
- ev("diskcache:disabled", "file_js_cache_large.js"),
+ ev("diskcache:noschedule"),
],
},
{
@@ -348,7 +354,7 @@ add_task(async function testMemoryCache() {
events: [
ev("load:memorycache", "file_js_cache_large.js"),
ev("evaluate:classic", "file_js_cache_large.js"),
- ev("diskcache:disabled", "file_js_cache_large.js"),
+ ev("diskcache:noschedule"),
],
},
],
@@ -362,28 +368,28 @@ add_task(async function testMemoryCache() {
file: "file_js_cache_large_syntax_error.js",
events: [
ev("load:source", "file_js_cache_large_syntax_error.js"),
- ev("diskcache:disabled", "file_js_cache_large_syntax_error.js"),
+ ev("diskcache:noschedule"),
],
},
{
file: "file_js_cache_large_syntax_error.js",
events: [
ev("load:source", "file_js_cache_large_syntax_error.js"),
- ev("diskcache:disabled", "file_js_cache_large_syntax_error.js"),
+ ev("diskcache:noschedule"),
],
},
{
file: "file_js_cache_large_syntax_error.js",
events: [
ev("load:source", "file_js_cache_large_syntax_error.js"),
- ev("diskcache:disabled", "file_js_cache_large_syntax_error.js"),
+ ev("diskcache:noschedule"),
],
},
{
file: "file_js_cache_large_syntax_error.js",
events: [
ev("load:source", "file_js_cache_large_syntax_error.js"),
- ev("diskcache:disabled", "file_js_cache_large_syntax_error.js"),
+ ev("diskcache:noschedule"),
],
},
],
diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp
@@ -2778,10 +2778,13 @@ void ScriptLoader::CalculateCacheFlag(ScriptLoadRequest* aRequest) {
LOG(("ScriptLoadRequest (%p): Bytecode-cache: Mark in-memory: Stencil",
aRequest));
aRequest->MarkPassedConditionForMemoryCache();
- } else {
- aRequest->MarkSkippedMemoryCaching();
+
+ // Disk cache is handled by SharedScriptCache.
+ return;
}
+ aRequest->MarkSkippedMemoryCaching();
+
// The following conditions apply only to the disk cache.
if (aRequest->IsBytecode()) {
@@ -3360,6 +3363,11 @@ nsCString& ScriptLoader::BytecodeMimeTypeFor(
nsresult ScriptLoader::MaybePrepareForDiskCacheAfterExecute(
ScriptLoadRequest* aRequest, nsresult aRv) {
+ if (mCache) {
+ // Disk cache is handled by SharedScriptCache.
+ return NS_OK;
+ }
+
if (!aRequest->PassedConditionForDiskCache() || !aRequest->HasStencil()) {
LOG(("ScriptLoadRequest (%p): Bytecode-cache: disabled (rv = %X)", aRequest,
unsigned(aRv)));
@@ -3400,6 +3408,11 @@ nsresult ScriptLoader::MaybePrepareModuleForDiskCacheAfterExecute(
ModuleLoadRequest* aRequest, nsresult aRv) {
MOZ_ASSERT(aRequest->IsTopLevel());
+ if (mCache) {
+ // Disk cache is handled by SharedScriptCache.
+ return NS_OK;
+ }
+
// NOTE: If a module is passed to this multiple times, it can be
// enqueued multiple times.
// This is okay because ScriptLoader::UpdateDiskCache filters out
@@ -3520,6 +3533,7 @@ LoadedScript* ScriptLoader::GetActiveScript(JSContext* aCx) {
}
void ScriptLoader::RegisterForDiskCache(ScriptLoadRequest* aRequest) {
+ MOZ_ASSERT(!mCache);
MOZ_ASSERT(aRequest->PassedConditionForDiskCache());
MOZ_ASSERT(aRequest->HasStencil());
MOZ_ASSERT(aRequest->getLoadedScript()->HasDiskCacheReference());
@@ -3545,14 +3559,6 @@ void ScriptLoader::Destroy() {
}
void ScriptLoader::MaybeUpdateDiskCache() {
- // If we already gave up, ensure that we are not going to enqueue any script,
- // and that we finalize them properly.
- if (mGiveUpDiskCaching) {
- LOG(("ScriptLoader (%p): Keep giving-up bytecode encoding.", this));
- GiveUpDiskCaching();
- return;
- }
-
// We wait for the load event to be fired before saving the bytecode of
// any script to the cache. It is quite common to have load event
// listeners trigger more JavaScript execution, that we want to save as
@@ -3562,13 +3568,6 @@ void ScriptLoader::MaybeUpdateDiskCache() {
return;
}
- // No need to fire any event if there is no bytecode to be saved.
- if (mDiskCacheQueue.IsEmpty()) {
- LOG(("ScriptLoader (%p): No script in queue to be saved to the disk.",
- this));
- return;
- }
-
// Wait until all scripts are loaded before saving the bytecode, such that
// we capture most of the intialization of the page.
if (HasPendingRequests()) {
@@ -3576,6 +3575,28 @@ void ScriptLoader::MaybeUpdateDiskCache() {
return;
}
+ if (mCache) {
+ if (!mCache->MaybeScheduleUpdateDiskCache()) {
+ TRACE_FOR_TEST_0("diskcache:noschedule");
+ }
+ return;
+ }
+
+ // If we already gave up, ensure that we are not going to enqueue any script,
+ // and that we finalize them properly.
+ if (mGiveUpDiskCaching) {
+ LOG(("ScriptLoader (%p): Keep giving-up bytecode encoding.", this));
+ GiveUpDiskCaching();
+ return;
+ }
+
+ // No need to fire any event if there is no bytecode to be saved.
+ if (mDiskCacheQueue.IsEmpty()) {
+ LOG(("ScriptLoader (%p): No script in queue to be saved to the disk.",
+ this));
+ return;
+ }
+
// Create a new runnable dedicated to encoding the content of the bytecode of
// all enqueued scripts when the document is idle. In case of failure, we
// give-up on encoding the bytecode.
@@ -3717,6 +3738,13 @@ void ScriptLoader::EncodeBytecodeAndSave(
}
void ScriptLoader::GiveUpDiskCaching() {
+ if (mCache) {
+ // Disk cache is handled by SharedScriptCache.
+ MOZ_ASSERT(mDiskCacheQueue.IsEmpty());
+ MOZ_ASSERT(mDiskCacheableDependencyModules.isEmpty());
+ return;
+ }
+
// If the document went away prematurely, we still want to set this, in order
// to avoid queuing more scripts.
mGiveUpDiskCaching = true;
diff --git a/dom/script/ScriptTrace.h b/dom/script/ScriptTrace.h
@@ -19,6 +19,11 @@
mozilla::dom::script::TestingNotifyObserver(requestOrScript, str); \
PR_END_MACRO
+#define TRACE_FOR_TEST_0(str) \
+ PR_BEGIN_MACRO \
+ mozilla::dom::script::TestingNotifyObserver(str); \
+ PR_END_MACRO
+
namespace mozilla::dom::script {
void TestingNotifyObserver(JS::loader::ScriptLoadRequest* aRequest,
@@ -43,6 +48,14 @@ inline void TestingNotifyObserver(JS::loader::ScriptLoadRequest* aRequest,
TestingNotifyObserver(aRequest, aRequest->getLoadedScript(), aEvent);
}
+inline void TestingNotifyObserver(const char* aEvent) {
+ if (!StaticPrefs::dom_expose_test_interfaces()) {
+ return;
+ }
+
+ TestingNotifyObserver(nullptr, nullptr, aEvent);
+}
+
} // namespace mozilla::dom::script
#endif // mozilla_dom_ScriptTrace_h
diff --git a/dom/script/SharedScriptCache.cpp b/dom/script/SharedScriptCache.cpp
@@ -192,6 +192,35 @@ static bool ShouldSave(JS::loader::LoadedScript* aLoadedScript,
return true;
}
+bool SharedScriptCache::MaybeScheduleUpdateDiskCache() {
+ auto strategy = ScriptLoader::GetDiskCacheStrategy();
+ if (strategy.mIsDisabled) {
+ return false;
+ }
+
+ bool hasSaveable = false;
+ for (auto iter = mComplete.Iter(); !iter.Done(); iter.Next()) {
+ JS::loader::LoadedScript* loadedScript = iter.Data().mResource;
+ if (ShouldSave(loadedScript, strategy)) {
+ hasSaveable = true;
+ break;
+ }
+ }
+
+ if (!hasSaveable) {
+ return false;
+ }
+
+ // TODO: Apply more flexible scheduling (bug 1902951)
+
+ nsCOMPtr<nsIRunnable> updater =
+ NewRunnableMethod("SharedScriptCache::UpdateDiskCache", this,
+ &SharedScriptCache::UpdateDiskCache);
+ (void)NS_DispatchToCurrentThreadQueue(updater.forget(),
+ EventQueuePriority::Idle);
+ return true;
+}
+
void SharedScriptCache::UpdateDiskCache() {
auto strategy = ScriptLoader::GetDiskCacheStrategy();
if (strategy.mIsDisabled) {
diff --git a/dom/script/SharedScriptCache.h b/dom/script/SharedScriptCache.h
@@ -193,6 +193,7 @@ class SharedScriptCache final
SharedScriptCache();
void Init();
+ bool MaybeScheduleUpdateDiskCache();
void UpdateDiskCache();
// This has to be static because it's also called for loaders that don't have