Bug 1354941: Pre-load all cached schema data in one DB operation. r=aswan
authorKris Maglione <maglione.k@gmail.com>
Tue, 11 Apr 2017 16:08:45 -0700
changeset 352756 e37c70b7fd23f4c5f6f41fb6b13feb39de3048c1
parent 352755 ddd77f9cf071dc1d40debb288ff632c0d7c64410
child 352757 5d8f0f741be4d603cc67c2ea3c0ddcb2d16e4216
push id89123
push usermaglione.k@gmail.com
push dateThu, 13 Apr 2017 01:43:24 +0000
treeherdermozilla-inbound@e37c70b7fd23 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersaswan
bugs1354941
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1354941: Pre-load all cached schema data in one DB operation. r=aswan MozReview-Commit-ID: JyBOcR2Ea15
toolkit/components/extensions/ExtensionUtils.jsm
toolkit/components/extensions/Schemas.jsm
--- a/toolkit/components/extensions/ExtensionUtils.jsm
+++ b/toolkit/components/extensions/ExtensionUtils.jsm
@@ -92,32 +92,32 @@ function validateThemeManifest(manifestP
     }
   }
   return invalidProps;
 }
 
 let StartupCache = {
   DB_NAME: "ExtensionStartupCache",
 
-  SCHEMA_VERSION: 1,
+  SCHEMA_VERSION: 2,
 
   STORE_NAMES: Object.freeze(["locales", "manifests", "schemas"]),
 
   dbPromise: null,
 
   cacheInvalidated: 0,
 
   initDB(db) {
     for (let name of StartupCache.STORE_NAMES) {
       try {
         db.deleteObjectStore(name);
       } catch (e) {
         // Don't worry if the store doesn't already exist.
       }
-      db.createObjectStore(name);
+      db.createObjectStore(name, {keyPath: "key"});
     }
   },
 
   clearAddonData(id) {
     let range = IDBKeyRange.bound([id], [id, "\uFFFF"]);
 
     return Promise.all([
       this.locales.delete(range),
@@ -168,36 +168,54 @@ Services.obs.addObserver(StartupCache, "
 
 class CacheStore {
   constructor(storeName) {
     this.storeName = storeName;
   }
 
   async get(key, createFunc) {
     let db;
-    let value;
+    let result;
     try {
       db = await StartupCache.open();
 
-      value = await db.objectStore(this.storeName)
+      result = await db.objectStore(this.storeName)
                       .get(key);
     } catch (e) {
       Cu.reportError(e);
 
       return createFunc(key);
     }
 
-    if (value === undefined) {
-      value = await createFunc(key);
+    if (result === undefined) {
+      let value = await createFunc(key);
+      result = {key, value};
 
       db.objectStore(this.storeName, "readwrite")
-        .put(value, key);
+        .put(result);
     }
 
-    return value;
+    return result && result.value;
+  }
+
+  async getAll() {
+    let result = new Map();
+    try {
+      let db = await StartupCache.open();
+
+      let results = await db.objectStore(this.storeName)
+                            .getAll();
+      for (let {key, value} of results) {
+        result.set(key, value);
+      }
+    } catch (e) {
+      Cu.reportError(e);
+    }
+
+    return result;
   }
 
   async delete(key) {
     let db = await StartupCache.open();
 
     return db.objectStore(this.storeName, "readwrite").delete(key);
   }
 }
--- a/toolkit/components/extensions/Schemas.jsm
+++ b/toolkit/components/extensions/Schemas.jsm
@@ -30,16 +30,18 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyServiceGetter(this, "contentPolicyService",
                                    "@mozilla.org/addons/content-policy;1",
                                    "nsIAddonContentPolicy");
 
 this.EXPORTED_SYMBOLS = ["Schemas"];
 
 const {DEBUG} = AppConstants;
 
+const isParentProcess = Services.appinfo.processType === Services.appinfo.PROCESS_TYPE_DEFAULT;
+
 /* globals Schemas, URL */
 
 function readJSON(url) {
   return new Promise((resolve, reject) => {
     NetUtil.asyncFetch({uri: url, loadUsingSystemPrincipal: true}, (inputStream, status) => {
       if (!Components.isSuccessCode(status)) {
         // Convert status code to a string
         let e = Components.Exception("", status);
@@ -2548,22 +2550,28 @@ this.Schemas = {
 
       case "Schema:Delete":
         this.schemaJSON.delete(msg.data.url);
         this.flushSchemas();
         break;
     }
   },
 
+  _needFlush: true,
   flushSchemas() {
-    XPCOMUtils.defineLazyGetter(this, "rootNamespace",
-                                () => this.parseSchemas());
+    if (this._needFlush) {
+      this._needFlush = false;
+      XPCOMUtils.defineLazyGetter(this, "rootNamespace",
+                                  () => this.parseSchemas());
+    }
   },
 
   parseSchemas() {
+    this._needFlush = true;
+
     Object.defineProperty(this, "rootNamespace", {
       enumerable: true,
       configurable: true,
       value: new Namespace("", []),
     });
 
     for (let json of this.schemaJSON.values()) {
       try {
@@ -2578,29 +2586,51 @@ this.Schemas = {
 
   loadSchema(json) {
     for (let namespace of json) {
       this.getNamespace(namespace.namespace)
           .addSchema(namespace);
     }
   },
 
-  load(url) {
-    if (Services.appinfo.processType != Services.appinfo.PROCESS_TYPE_CONTENT) {
-      return StartupCache.schemas.get(url, readJSON).then(json => {
-        this.schemaJSON.set(url, json);
-
-        let data = Services.ppmm.initialProcessData;
-        data["Extension:Schemas"] = this.schemaJSON;
-
-        Services.ppmm.broadcastAsyncMessage("Schema:Add", {url, schema: json});
-
-        this.flushSchemas();
+  _loadCachedSchemasPromise: null,
+  loadCachedSchemas() {
+    if (!this._loadCachedSchemasPromise) {
+      this._loadCachedSchemasPromise = StartupCache.schemas.getAll().then(results => {
+        return results;
       });
     }
+
+    return this._loadCachedSchemasPromise;
+  },
+
+  addSchema(url, json) {
+    this.schemaJSON.set(url, json);
+
+    let data = Services.ppmm.initialProcessData;
+    data["Extension:Schemas"] = this.schemaJSON;
+
+    Services.ppmm.broadcastAsyncMessage("Schema:Add", {url, schema: json});
+
+    this.flushSchemas();
+  },
+
+  async load(url) {
+    if (!isParentProcess) {
+      return;
+    }
+
+    let schemaCache = await this.loadCachedSchemas();
+
+    let json = (schemaCache.get(url) ||
+                await StartupCache.schemas.get(url, readJSON));
+
+    if (!this.schemaJSON.has(url)) {
+      this.addSchema(url, json);
+    }
   },
 
   unload(url) {
     this.schemaJSON.delete(url);
 
     let data = Services.ppmm.initialProcessData;
     data["Extension:Schemas"] = this.schemaJSON;