Bug 1354941: Pre-load all cached schema data in one DB operation. r?mixedpuppy
MozReview-Commit-ID: JyBOcR2Ea15
--- a/toolkit/components/extensions/ExtensionUtils.jsm
+++ b/toolkit/components/extensions/ExtensionUtils.jsm
@@ -53,32 +53,32 @@ XPCOMUtils.defineLazyGetter(this, "uniqu
function getUniqueId() {
return `${nextId++}-${uniqueProcessID}`;
}
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),
@@ -129,36 +129,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);
@@ -2549,22 +2551,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 {
@@ -2579,29 +2587,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 => {
+ for (let [url, json] of results) {
+ this.addSchema(url, json);
+ }
});
}
+
+ 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;
+ }
+
+ await this.loadCachedSchemas();
+
+ if (!this.schemaJSON.has(url)) {
+ let json = await StartupCache.schemas.get(url, readJSON);
+ this.addSchema(url, json);
+ }
},
unload(url) {
this.schemaJSON.delete(url);
let data = Services.ppmm.initialProcessData;
data["Extension:Schemas"] = this.schemaJSON;