зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1333990: Part 3a - Use async loading and in-memory caching for WebExtension content scripts. r=aswan
MozReview-Commit-ID: GcdKDbWcUtu --HG-- extra : rebase_source : 830dfbe0fdd380229340ec8008217e3bd75ec87c
This commit is contained in:
Родитель
a27b823442
Коммит
53e241ffbc
|
@ -49,6 +49,7 @@ Cu.import("resource://gre/modules/ExtensionCommon.jsm");
|
|||
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
|
||||
|
||||
const {
|
||||
DefaultMap,
|
||||
EventEmitter,
|
||||
LocaleData,
|
||||
defineLazyGetter,
|
||||
|
@ -102,6 +103,12 @@ var apiManager = new class extends SchemaAPIManager {
|
|||
}
|
||||
}();
|
||||
|
||||
class ScriptCache extends DefaultMap {
|
||||
constructor(options) {
|
||||
super(url => ChromeUtils.compileScript(url, options));
|
||||
}
|
||||
}
|
||||
|
||||
// Represents a content script.
|
||||
function Script(extension, options, deferred = PromiseUtils.defer()) {
|
||||
this.extension = extension;
|
||||
|
@ -115,6 +122,12 @@ function Script(extension, options, deferred = PromiseUtils.defer()) {
|
|||
|
||||
this.deferred = deferred;
|
||||
|
||||
this.scriptCache = extension[options.wantReturnValue ? "dynamicScripts"
|
||||
: "staticScripts"];
|
||||
if (options.wantReturnValue) {
|
||||
this.compileScripts();
|
||||
}
|
||||
|
||||
this.matches_ = new MatchPattern(this.options.matches);
|
||||
this.exclude_matches_ = new MatchPattern(this.options.exclude_matches || null);
|
||||
// TODO: MatchPattern should pre-mangle host-only patterns so that we
|
||||
|
@ -127,6 +140,10 @@ function Script(extension, options, deferred = PromiseUtils.defer()) {
|
|||
}
|
||||
|
||||
Script.prototype = {
|
||||
compileScripts() {
|
||||
return this.js.map(url => this.scriptCache.get(url));
|
||||
},
|
||||
|
||||
get cssURLs() {
|
||||
// We can handle CSS urls (css) and CSS code (cssCode).
|
||||
let urls = [];
|
||||
|
@ -257,37 +274,32 @@ Script.prototype = {
|
|||
}
|
||||
}
|
||||
|
||||
let result;
|
||||
let scheduled = this.run_at || "document_idle";
|
||||
if (shouldRun(scheduled)) {
|
||||
for (let url of this.js) {
|
||||
url = this.extension.baseURI.resolve(url);
|
||||
let scriptsPromise = Promise.all(this.compileScripts());
|
||||
|
||||
let options = {
|
||||
target: sandbox,
|
||||
charset: "UTF-8",
|
||||
// Inject asynchronously unless we're expected to inject before any
|
||||
// page scripts have run, and we haven't already missed that boat.
|
||||
async: this.run_at !== "document_start" || when !== "document_start",
|
||||
};
|
||||
try {
|
||||
result = Services.scriptloader.loadSubScriptWithOptions(url, options);
|
||||
} catch (e) {
|
||||
Cu.reportError(e);
|
||||
this.deferred.reject(e);
|
||||
}
|
||||
// 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.
|
||||
if (this.run_at === "document_start" && when === "document_start") {
|
||||
window.document.blockParsing(scriptsPromise);
|
||||
}
|
||||
|
||||
if (this.options.jsCode) {
|
||||
try {
|
||||
this.deferred.resolve(scriptsPromise.then(scripts => {
|
||||
let result;
|
||||
|
||||
// The evaluations below may throw, in which case the promise will be
|
||||
// automatically rejected.
|
||||
for (let script of scripts) {
|
||||
result = script.executeInGlobal(sandbox);
|
||||
}
|
||||
|
||||
if (this.options.jsCode) {
|
||||
result = Cu.evalInSandbox(this.options.jsCode, sandbox, "latest");
|
||||
} catch (e) {
|
||||
Cu.reportError(e);
|
||||
this.deferred.reject(e);
|
||||
}
|
||||
}
|
||||
|
||||
this.deferred.resolve(result);
|
||||
return result;
|
||||
}));
|
||||
}
|
||||
},
|
||||
};
|
||||
|
@ -795,7 +807,10 @@ class BrowserExtensionContent extends EventEmitter {
|
|||
this.MESSAGE_EMIT_EVENT = `Extension:EmitEvent:${this.instanceId}`;
|
||||
Services.cpmm.addMessageListener(this.MESSAGE_EMIT_EVENT, this);
|
||||
|
||||
this.scripts = data.content_scripts.map(scriptData => new Script(this, scriptData));
|
||||
defineLazyGetter(this, "scripts", () => {
|
||||
return data.content_scripts.map(scriptData => new Script(this, scriptData));
|
||||
});
|
||||
|
||||
this.webAccessibleResources = new MatchGlobs(data.webAccessibleResources);
|
||||
this.whiteListedHosts = new MatchPattern(data.whiteListedHosts);
|
||||
this.permissions = data.permissions;
|
||||
|
@ -857,6 +872,14 @@ class BrowserExtensionContent extends EventEmitter {
|
|||
}
|
||||
}
|
||||
|
||||
defineLazyGetter(BrowserExtensionContent.prototype, "staticScripts", () => {
|
||||
return new ScriptCache({hasReturnValue: false});
|
||||
});
|
||||
|
||||
defineLazyGetter(BrowserExtensionContent.prototype, "dynamicScripts", () => {
|
||||
return new ScriptCache({hasReturnValue: true});
|
||||
});
|
||||
|
||||
ExtensionManager = {
|
||||
// Map[extensionId, BrowserExtensionContent]
|
||||
extensions: new Map(),
|
||||
|
|
|
@ -563,6 +563,8 @@ class TabBase {
|
|||
options.css_origin = "author";
|
||||
}
|
||||
|
||||
options.wantReturnValue = true;
|
||||
|
||||
return this.sendMessage(context, "Extension:Execute", {options});
|
||||
}
|
||||
|
||||
|
|
|
@ -218,7 +218,7 @@ add_task(function* test_web_accessible_resources_csp() {
|
|||
manifest: {
|
||||
"content_scripts": [{
|
||||
"matches": ["http://example.com/*/file_csp.html"],
|
||||
"run_at": "document_start",
|
||||
"run_at": "document_end",
|
||||
"js": ["content_script_helper.js", "content_script.js"],
|
||||
}],
|
||||
"web_accessible_resources": [
|
||||
|
@ -313,7 +313,7 @@ add_task(function* test_web_accessible_resources_mixed_content() {
|
|||
manifest: {
|
||||
"content_scripts": [{
|
||||
"matches": ["https://example.com/*/file_mixed.html"],
|
||||
"run_at": "document_start",
|
||||
"run_at": "document_end",
|
||||
"js": ["content_script_helper.js", "content_script.js"],
|
||||
}],
|
||||
"web_accessible_resources": [
|
||||
|
|
Загрузка…
Ссылка в новой задаче