Bug 1368102: Part 4 - Use WebExtensionContentScript to match content scripts. r=mixedpuppy,zombie

MozReview-Commit-ID: 1Ga0259WjC

--HG--
extra : rebase_source : 2768c37e1ed594d293d892c0fb8f9b346508a41d
This commit is contained in:
Kris Maglione 2017-06-03 17:11:08 -07:00
Родитель cc9c553d7c
Коммит 884bb014cb
4 изменённых файлов: 88 добавлений и 118 удалений

Просмотреть файл

@ -196,27 +196,27 @@ defineLazyGetter(BrowserExtensionContent.prototype, "authorCSS", () => {
// Represents a content script.
class Script {
constructor(extension, options) {
constructor(extension, matcher) {
this.extension = extension;
this.options = options;
this.matcher = matcher;
this.runAt = this.options.run_at;
this.js = this.options.js || [];
this.css = this.options.css || [];
this.remove_css = this.options.remove_css;
this.css_origin = this.options.css_origin;
this.runAt = this.matcher.runAt;
this.js = this.matcher.jsPaths;
this.css = this.matcher.cssPaths;
this.removeCSS = this.matcher.removeCSS;
this.cssOrigin = this.matcher.cssOrigin;
this.cssCache = extension[this.css_origin === "user" ? "userCSS"
: "authorCSS"];
this.scriptCache = extension[options.wantReturnValue ? "dynamicScripts"
this.cssCache = extension[this.cssOrigin === "user" ? "userCSS"
: "authorCSS"];
this.scriptCache = extension[matcher.wantReturnValue ? "dynamicScripts"
: "staticScripts"];
if (options.wantReturnValue) {
if (matcher.wantReturnValue) {
this.compileScripts();
this.loadCSS();
}
this.requiresCleanup = !this.remove_css && (this.css.length > 0 || options.cssCode);
this.requiresCleanup = !this.removeCss && (this.css.length > 0 || matcher.cssCode);
}
compileScripts() {
@ -228,10 +228,10 @@ class Script {
}
cleanup(window) {
if (!this.remove_css && this.cssURLs.length) {
if (!this.removeCss && this.cssURLs.length) {
let winUtils = getWinUtils(window);
let type = this.css_origin === "user" ? winUtils.USER_SHEET : winUtils.AUTHOR_SHEET;
let type = this.cssOrigin === "user" ? winUtils.USER_SHEET : winUtils.AUTHOR_SHEET;
for (let url of this.cssURLs) {
this.cssCache.deleteDocument(url, window.document);
runSafeSyncWithoutClone(winUtils.removeSheetUsingURIString, url, type);
@ -276,9 +276,9 @@ class Script {
let window = context.contentWindow;
let winUtils = getWinUtils(window);
let type = this.css_origin === "user" ? winUtils.USER_SHEET : winUtils.AUTHOR_SHEET;
let type = this.cssOrigin === "user" ? winUtils.USER_SHEET : winUtils.AUTHOR_SHEET;
if (this.remove_css) {
if (this.removeCSS) {
for (let url of this.cssURLs) {
this.cssCache.deleteDocument(url, window.document);
@ -325,8 +325,8 @@ class Script {
result = script.executeInGlobal(context.cloneScope);
}
if (this.options.jsCode) {
result = Cu.evalInSandbox(this.options.jsCode, context.cloneScope, "latest");
if (this.matcher.jsCode) {
result = Cu.evalInSandbox(this.matcher.jsCode, context.cloneScope, "latest");
}
await cssPromise;
@ -338,8 +338,8 @@ defineLazyGetter(Script.prototype, "cssURLs", function() {
// We can handle CSS urls (css) and CSS code (cssCode).
let urls = this.css.slice();
if (this.options.cssCode) {
urls.push("data:text/css;charset=utf-8," + encodeURIComponent(this.options.cssCode));
if (this.matcher.cssCode) {
urls.push("data:text/css;charset=utf-8," + encodeURIComponent(this.matcher.cssCode));
}
return urls;

Просмотреть файл

@ -30,6 +30,23 @@ const isParentProcess = appinfo.processType === appinfo.PROCESS_TYPE_DEFAULT;
* when no extensions are running.
*/
function parseScriptOptions(options) {
return {
allFrames: options.all_frames,
matchAboutBlank: options.match_about_blank,
frameID: options.frame_id,
runAt: options.run_at,
matches: new MatchPatternSet(options.matches),
excludeMatches: new MatchPatternSet(options.exclude_matches || []),
includeGlobs: options.include_globs && options.include_globs.map(glob => new MatchGlob(glob)),
excludeGlobs: options.include_globs && options.exclude_globs.map(glob => new MatchGlob(glob)),
jsPaths: options.js || [],
cssPaths: options.css || [],
};
}
var APIs = {
apis: new Map(),
@ -94,6 +111,8 @@ var ExtensionManagement = {
backgroundScripts: (extension.manifest.background &&
extension.manifest.background.scripts),
contentScripts: (extension.manifest.content_scripts || []).map(parseScriptOptions),
});
extension.policy = policy;

Просмотреть файл

@ -353,11 +353,16 @@ WebExtensionContentScript::Matches(const DocInfo& aDoc) const
return false;
}
if (!MatchesURI(aDoc.PrincipalURL())) {
return false;
// Top-level about:blank is a special case. We treat it as a match if
// matchAboutBlank is true and it has the null principal. In all other
// cases, we test the URL of the principal that it inherits.
if (mMatchAboutBlank && aDoc.IsTopLevel() &&
aDoc.URL().Spec().EqualsLiteral("about:blank") &&
aDoc.Principal()->GetIsNullPrincipal()) {
return true;
}
return true;
return MatchesURI(aDoc.PrincipalURL());
}
bool
@ -445,8 +450,8 @@ DocInfo::FrameID() const
} else {
struct Matcher
{
uint64_t match(Window aWin) { return aWin->GetCurrentInnerWindow()->WindowID(); }
uint64_t match(LoadInfo aLoadInfo) { return aLoadInfo->GetInnerWindowID(); }
uint64_t match(Window aWin) { return aWin->WindowID(); }
uint64_t match(LoadInfo aLoadInfo) { return aLoadInfo->GetOuterWindowID(); }
};
mFrameID.emplace(mObj.match(Matcher()));
}

Просмотреть файл

@ -19,8 +19,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "ExtensionManagement",
"resource://gre/modules/ExtensionManagement.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "MessageChannel",
"resource://gre/modules/MessageChannel.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "WebNavigationFrames",
"resource://gre/modules/WebNavigationFrames.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionChild",
"resource://gre/modules/ExtensionChild.jsm");
@ -39,33 +37,39 @@ XPCOMUtils.defineLazyGetter(this, "getInnerWindowID", () => ExtensionUtils.getIn
const appinfo = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime);
const isContentProcess = appinfo.processType == appinfo.PROCESS_TYPE_CONTENT;
function parseScriptOptions(options) {
return {
allFrames: options.all_frames,
matchAboutBlank: options.match_about_blank,
frameID: options.frame_id,
runAt: options.run_at,
matches: new MatchPatternSet(options.matches),
excludeMatches: new MatchPatternSet(options.exclude_matches || []),
includeGlobs: options.include_globs && options.include_globs.map(glob => new MatchGlob(glob)),
excludeGlobs: options.exclude_globs && options.exclude_globs.map(glob => new MatchGlob(glob)),
jsPaths: options.js || [],
cssPaths: options.css || [],
};
}
class ScriptMatcher {
constructor(extension, options) {
constructor(extension, matcher) {
this.extension = extension;
this.options = options;
this.matcher = matcher;
this._script = null;
this.allFrames = options.all_frames;
this.matchAboutBlank = options.match_about_blank;
this.frameId = options.frame_id;
this.runAt = options.run_at;
this.matches = new MatchPatternSet(options.matches);
this.excludeMatches = new MatchPatternSet(options.exclude_matches || []);
this.includeGlobs = options.include_globs && options.include_globs.map(glob => new MatchGlob(glob));
this.excludeGlobs = options.include_globs && options.exclude_globs.map(glob => new MatchGlob(glob));
}
toString() {
return `[Script {js: [${this.options.js}], matchAboutBlank: ${this.matchAboutBlank}, runAt: ${this.runAt}, matches: ${this.options.matches}}]`;
get matchAboutBlank() {
return this.matcher.matchAboutBlank;
}
get script() {
if (!this._script) {
this._script = new ExtensionContent.Script(this.extension.realExtension,
this.options);
this.matcher);
}
return this._script;
}
@ -78,82 +82,11 @@ class ScriptMatcher {
}
matchesLoadInfo(uri, loadInfo) {
if (!this.matchesURI(uri)) {
return false;
}
if (!this.allFrames && !loadInfo.isTopLevelLoad) {
return false;
}
return true;
}
matchesURI(uri) {
if (!(this.matches.matches(uri))) {
return false;
}
if (this.excludeMatches.matches(uri)) {
return false;
}
if (this.includeGlobs && !this.includeGlobs.some(glob => glob.matches(uri.spec))) {
return false;
}
if (this.excludeGlobs && this.excludeGlobs.some(glob => glob.matches(uri.spec))) {
return false;
}
return true;
return this.matcher.matchesLoadInfo(uri, loadInfo);
}
matchesWindow(window) {
if (!this.allFrames && this.frameId == null && window.parent !== window) {
return false;
}
let uri = window.document.documentURIObject;
let principal = window.document.nodePrincipal;
if (this.matchAboutBlank) {
// When matching top-level about:blank documents,
// allow loading into any with a NullPrincipal.
if (uri.spec === "about:blank" && window === window.parent && principal.isNullPrincipal) {
return true;
}
// When matching about:blank/srcdoc iframes, the checks below
// need to be performed against the "owner" document's URI.
if (["about:blank", "about:srcdoc"].includes(uri.spec)) {
uri = principal.URI;
}
}
// Documents from data: URIs also inherit the principal.
if (Services.netUtils.URIChainHasFlags(uri, Ci.nsIProtocolHandler.URI_INHERITS_SECURITY_CONTEXT)) {
if (!this.matchAboutBlank) {
return false;
}
uri = principal.URI;
}
if (!this.matchesURI(uri)) {
return false;
}
if (this.frameId != null && WebNavigationFrames.getFrameId(window) !== this.frameId) {
return false;
}
// If mozAddonManager is present on this page, don't allow
// content scripts.
if (window.navigator.mozAddonManager !== undefined) {
return false;
}
return true;
return this.matcher.matchesWindow(window);
}
injectInto(window) {
@ -202,7 +135,18 @@ class ExtensionGlobal {
return ExtensionContent.handleDetectLanguage(this.global, target);
case "Extension:Execute":
let extension = ExtensionManager.get(recipient.extensionId);
let script = new ScriptMatcher(extension, data.options);
let matcher = new WebExtensionContentScript(extension.policy, parseScriptOptions(data.options));
let options = Object.assign(matcher, {
wantReturnValue: data.options.wantReturnValue,
removeCSS: data.options.remove_css,
cssOrigin: data.options.css_origin,
cssCode: data.options.cssCode,
jsCode: data.options.jsCode,
});
let script = new ScriptMatcher(extension, options);
return ExtensionContent.handleExtensionExecute(this.global, target, data.options, script);
case "WebNavigation:GetFrame":
@ -536,11 +480,11 @@ class StubExtension {
this.whiteListedHosts = new MatchPatternSet(data.whiteListedHosts);
this.webAccessibleResources = data.webAccessibleResources.map(path => new MatchGlob(path));
this.scripts = data.content_scripts.map(scriptData => new ScriptMatcher(this, scriptData));
this._realExtension = null;
this.startup();
this.scripts = this.policy.contentScripts.map(matcher => new ScriptMatcher(this, matcher));
}
startup() {
@ -548,6 +492,8 @@ class StubExtension {
if (isContentProcess) {
let uri = Services.io.newURI(this.data.resourceURL);
ExtensionManagement.startupExtension(this.uuid, uri, this);
} else {
this.policy = WebExtensionPolicy.getByID(this.id);
}
}