Bug 1379148: Part 3 - Inject scripts synchronously if they're already available. r?mixedpuppy
MozReview-Commit-ID: J6vheuxsqJR
--- a/toolkit/components/extensions/ExtensionContent.jsm
+++ b/toolkit/components/extensions/ExtensionContent.jsm
@@ -132,18 +132,26 @@ class CacheMap extends DefaultMap {
this.delete(url);
}
}
}
}
class ScriptCache extends CacheMap {
constructor(options) {
- super(SCRIPT_EXPIRY_TIMEOUT_MS,
- url => ChromeUtils.compileScript(url, options));
+ super(SCRIPT_EXPIRY_TIMEOUT_MS);
+ this.options = options;
+ }
+
+ defaultConstructor(url) {
+ let promise = ChromeUtils.compileScript(url, this.options);
+ promise.then(script => {
+ promise.script = script;
+ });
+ return promise;
}
}
class CSSCache extends CacheMap {
constructor(sheetType) {
super(CSS_EXPIRY_TIMEOUT_MS, url => {
let uri = Services.io.newURI(url);
return styleSheetService.preloadSheetAsync(uri, sheetType).then(sheet => {
@@ -312,27 +320,35 @@ class Script {
this.cssCache.addDocument(url, window.document);
runSafeSyncWithoutClone(winUtils.addSheet, sheet, type);
}
});
}
}
- let scriptsPromise = Promise.all(this.compileScripts());
+ let scriptPromises = this.compileScripts();
+
+ let scripts = scriptPromises.map(promise => promise.script);
+ // If not all scripts are already available in the cache, block
+ // parsing and wait all promises to resolve.
+ if (!scripts.every(script => script)) {
+ let scriptsPromise = Promise.all(scriptPromises);
- // If we're supposed to inject at the start of the document load,
- // and we haven't already missed that point, block further parsing
- // until the scripts have been loaded.
- let {document} = context.contentWindow;
- if (this.runAt === "document_start" && document.readyState !== "complete") {
- document.blockParsing(scriptsPromise, {blockScriptCreated: false});
+ // If we're supposed to inject at the start of the document load,
+ // and we haven't already missed that point, block further parsing
+ // until the scripts have been loaded.
+ let {document} = context.contentWindow;
+ if (this.runAt === "document_start" && document.readyState !== "complete") {
+ document.blockParsing(scriptsPromise, {blockScriptCreated: false});
+ }
+
+ scripts = await scriptsPromise;
}
- let scripts = await scriptsPromise;
let result;
// The evaluations below may throw, in which case the promise will be
// automatically rejected.
TelemetryStopwatch.start(CONTENT_SCRIPT_INJECTION_HISTOGRAM, context);
try {
for (let script of scripts) {
result = script.executeInGlobal(context.cloneScope);
--- a/toolkit/components/extensions/ExtensionUtils.jsm
+++ b/toolkit/components/extensions/ExtensionUtils.jsm
@@ -62,35 +62,39 @@ function instanceOf(value, type) {
ChromeUtils.getClassName(value) === type);
}
/**
* Similar to a WeakMap, but creates a new key with the given
* constructor if one is not present.
*/
class DefaultWeakMap extends WeakMap {
- constructor(defaultConstructor, init) {
+ constructor(defaultConstructor = undefined, init = undefined) {
super(init);
- this.defaultConstructor = defaultConstructor;
+ if (defaultConstructor) {
+ this.defaultConstructor = defaultConstructor;
+ }
}
get(key) {
let value = super.get(key);
if (value === undefined && !this.has(key)) {
value = this.defaultConstructor(key);
this.set(key, value);
}
return value;
}
}
class DefaultMap extends Map {
- constructor(defaultConstructor, init) {
+ constructor(defaultConstructor = undefined, init = undefined) {
super(init);
- this.defaultConstructor = defaultConstructor;
+ if (defaultConstructor) {
+ this.defaultConstructor = defaultConstructor;
+ }
}
get(key) {
let value = super.get(key);
if (value === undefined && !this.has(key)) {
value = this.defaultConstructor(key);
this.set(key, value);
}