Bug 1458375 - Make ScriptPreloader wait until browser-idle-startup-tasks-finished before writing cache.
Originally, the ScriptPreloader stopped recording and wrote its cache when the
browser-delayed-startup-finished notification fired for the first window, but there
are other scripts (both in the content and WebExtension processes) that might run
soon after that we also want to cache.
This patch still makes the parent process stop recording scripts after
browser-delayed-startup-finished, but only prepares and writes the cache
once browser-idle-startup-tasks-finished fires, when it is much more likely
that the content and WebExtension caches are ready to go.
MozReview-Commit-ID: KiBEVvuqQkA
--- a/js/xpconnect/loader/ScriptPreloader.cpp
+++ b/js/xpconnect/loader/ScriptPreloader.cpp
@@ -27,18 +27,19 @@
#include "nsIFile.h"
#include "nsIObserverService.h"
#include "nsJSUtils.h"
#include "nsProxyRelease.h"
#include "nsThreadUtils.h"
#include "nsXULAppAPI.h"
#include "xpcpublic.h"
-#define DELAYED_STARTUP_TOPIC "browser-delayed-startup-finished"
+#define STARTUP_COMPLETE_TOPIC "browser-delayed-startup-finished"
#define DOC_ELEM_INSERTED_TOPIC "document-element-inserted"
+#define CACHE_WRITE_TOPIC "browser-idle-startup-tasks-finished"
#define CLEANUP_TOPIC "xpcom-shutdown"
#define SHUTDOWN_TOPIC "quit-application-granted"
#define CACHE_INVALIDATE_TOPIC "startupcache-invalidate"
namespace mozilla {
namespace {
static LazyLogModule gLog("ScriptPreloader");
@@ -226,18 +227,19 @@ ScriptPreloader::ScriptPreloader()
sProcessType = GetChildProcessType(dom::ContentChild::GetSingleton()->GetRemoteType());
}
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
MOZ_RELEASE_ASSERT(obs);
if (XRE_IsParentProcess()) {
// In the parent process, we want to freeze the script cache as soon
- // as delayed startup for the first browser window has completed.
- obs->AddObserver(this, DELAYED_STARTUP_TOPIC, false);
+ // as idle tasks for the first browser window have completed.
+ obs->AddObserver(this, STARTUP_COMPLETE_TOPIC, false);
+ obs->AddObserver(this, CACHE_WRITE_TOPIC, false);
} else {
// In the child process, we need to freeze the script cache before any
// untrusted code has been executed. The insertion of the first DOM
// document element may sometimes be earlier than is ideal, but at
// least it should always be safe.
obs->AddObserver(this, DOC_ELEM_INSERTED_TOPIC, false);
}
obs->AddObserver(this, SHUTDOWN_TOPIC, false);
@@ -333,22 +335,26 @@ ScriptPreloader::InvalidateCache()
getter_AddRefs(mSaveThread), this);
}
}
nsresult
ScriptPreloader::Observe(nsISupports* subject, const char* topic, const char16_t* data)
{
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
- if (!strcmp(topic, DELAYED_STARTUP_TOPIC)) {
- obs->RemoveObserver(this, DELAYED_STARTUP_TOPIC);
+ if (!strcmp(topic, STARTUP_COMPLETE_TOPIC)) {
+ obs->RemoveObserver(this, STARTUP_COMPLETE_TOPIC);
MOZ_ASSERT(XRE_IsParentProcess());
mStartupFinished = true;
+ } else if (!strcmp(topic, CACHE_WRITE_TOPIC)) {
+ obs->RemoveObserver(this, CACHE_WRITE_TOPIC);
+ MOZ_ASSERT(mStartupFinished);
+ MOZ_ASSERT(XRE_IsParentProcess());
if (mChildCache) {
Unused << NS_NewNamedThread("SaveScripts",
getter_AddRefs(mSaveThread), this);
}
} else if (!strcmp(topic, DOC_ELEM_INSERTED_TOPIC)) {
obs->RemoveObserver(this, DOC_ELEM_INSERTED_TOPIC);