Bug 1458375 - Make ScriptPreloader wait until browser-idle-startup-tasks-finished before writing cache. draft
authorMike Conley <mconley@mozilla.com>
Mon, 11 Jun 2018 12:19:02 -0700
changeset 806911 92ec32795505a3aede71010ea7e93b1d49800eb2
parent 806910 ef4d49da4c626f2a27a392e2c76901b38dff564b
child 806912 47139835a9d7e053a5c126a91a856a31c4803ddd
push id112986
push usermconley@mozilla.com
push dateTue, 12 Jun 2018 19:30:14 +0000
bugs1458375
milestone62.0a1
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
js/xpconnect/loader/ScriptPreloader.cpp
--- 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);