Bug 1223634 - Support exclude_globs/include_globs in webext manifests. r=kmag

--HG--
extra : rebase_source : e6a951c77581b08fa8613852f54665506e9fb0ba
This commit is contained in:
Tom Schuster 2016-03-16 15:11:57 +01:00
Родитель 1f813aa1a3
Коммит 3f7b262a15
7 изменённых файлов: 200 добавлений и 4 удалений

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

@ -29,6 +29,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "LanguageDetector",
"resource:///modules/translation/LanguageDetector.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "MatchPattern",
"resource://gre/modules/MatchPattern.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "MatchGlobs",
"resource://gre/modules/MatchPattern.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PromiseUtils",
@ -152,8 +154,8 @@ function Script(options, deferred = PromiseUtils.defer()) {
// TODO: MatchPattern should pre-mangle host-only patterns so that we
// don't need to call a separate match function.
this.matches_host_ = new MatchPattern(this.options.matchesHost || null);
// TODO: Support glob patterns.
this.include_globs_ = new MatchGlobs(this.options.include_globs);
this.exclude_globs_ = new MatchGlobs(this.options.exclude_globs);
}
Script.prototype = {
@ -167,6 +169,16 @@ Script.prototype = {
return false;
}
if (this.options.include_globs != null) {
if (!this.include_globs_.matches(uri)) {
return false;
}
}
if (this.exclude_globs_.matches(uri)) {
return false;
}
if (this.options.frame_id != null) {
if (WebNavigationFrames.getFrameId(window) != this.options.frame_id) {
return false;

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

@ -232,6 +232,16 @@
"minItems": 1,
"items": { "$ref": "MatchPattern" }
},
"include_globs": {
"type": "array",
"optional": true,
"items": { "type": "string" }
},
"exclude_globs": {
"type": "array",
"optional": true,
"items": { "type": "string" }
},
"css": {
"type": "array",
"optional": true,

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

@ -33,6 +33,7 @@ skip-if = buildapp == 'b2g' # runat != document_idle is not supported.
[test_ext_contentscript_create_iframe.html]
[test_ext_contentscript_api_injection.html]
[test_ext_downloads.html]
[test_ext_exclude_include_globs.html]
[test_ext_i18n_css.html]
[test_ext_generate.html]
[test_ext_idle.html]

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

@ -0,0 +1,92 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for content script</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/ExtensionTestUtils.js"></script>
<script type="text/javascript" src="head.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<script type="text/javascript">
"use strict";
add_task(function* test_contentscript() {
function backgroundScript() {
browser.runtime.onMessage.addListener(([script], sender) => {
browser.test.sendMessage("run", {script});
browser.test.sendMessage("run-" + script);
});
browser.test.sendMessage("running");
}
function contentScriptAll() {
browser.runtime.sendMessage(["all"]);
}
function contentScriptIncludesTest1() {
browser.runtime.sendMessage(["includes-test1"]);
}
function contentScriptExcludesTest1() {
browser.runtime.sendMessage(["excludes-test1"]);
}
let extensionData = {
manifest: {
content_scripts: [
{
"matches": ["http://example.org/", "http://*.example.org/"],
"exclude_globs": [],
"include_globs": ["*"],
"js": ["content_script_all.js"],
},
{
"matches": ["http://example.org/", "http://*.example.org/"],
"include_globs": ["*test1*"],
"js": ["content_script_includes_test1.js"],
},
{
"matches": ["http://example.org/", "http://*.example.org/"],
"exclude_globs": ["*test1*"],
"js": ["content_script_excludes_test1.js"],
},
],
},
background: "(" + backgroundScript.toString() + ")()",
files: {
"content_script_all.js": "(" + contentScriptAll.toString() + ")()",
"content_script_includes_test1.js": "(" + contentScriptIncludesTest1.toString() + ")()",
"content_script_excludes_test1.js": "(" + contentScriptExcludesTest1.toString() + ")()",
},
};
let extension = ExtensionTestUtils.loadExtension(extensionData);
let ran = 0;
extension.onMessage("run", ({script}) => {
ran++;
});
yield Promise.all([extension.startup(), extension.awaitMessage("running")]);
info("extension loaded");
let win = window.open("http://example.org/");
yield Promise.all([extension.awaitMessage("run-all"), extension.awaitMessage("run-excludes-test1")]);
win.close();
is(ran, 2);
win = window.open("http://test1.example.org/");
yield Promise.all([extension.awaitMessage("run-all"), extension.awaitMessage("run-includes-test1")]);
win.close();
is(ran, 4);
yield extension.unload();
info("extension unloaded");
});
</script>
</body>
</html>

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

@ -11,9 +11,9 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
"resource://gre/modules/NetUtil.jsm");
this.EXPORTED_SYMBOLS = ["MatchPattern"];
this.EXPORTED_SYMBOLS = ["MatchPattern", "MatchGlobs"];
/* globals MatchPattern */
/* globals MatchPattern, MatchGlobs */
const PERMITTED_SCHEMES = ["http", "https", "file", "ftp", "app", "data"];
const PERMITTED_SCHEMES_REGEXP = PERMITTED_SCHEMES.join("|");
@ -172,3 +172,24 @@ MatchPattern.prototype = {
return this.pat;
},
};
// Globs can match everything. Be careful, this DOES NOT filter by allowed schemes!
this.MatchGlobs = function(globs) {
if (globs) {
this.regexps = Array.from(globs, (glob) => globToRegexp(glob, true));
} else {
this.regexps = [];
}
};
MatchGlobs.prototype = {
matches(uri) {
let spec = uri.spec;
for (let regexp of this.regexps) {
if (regexp.test(spec)) {
return true;
}
}
return false;
}
}

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

@ -0,0 +1,58 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
Components.utils.import("resource://gre/modules/MatchPattern.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
function test(url, pattern) {
let uri = Services.io.newURI(url, null, null);
let m = new MatchGlobs(pattern);
return m.matches(uri);
}
function pass({url, pattern}) {
ok(test(url, pattern), `Expected match: ${JSON.stringify(pattern)}, ${url}`);
}
function fail({url, pattern}) {
ok(!test(url, pattern), `Expected no match: ${JSON.stringify(pattern)}, ${url}`);
}
function run_test() {
let moz = "http://mozilla.org";
pass({url: moz, pattern: ["*"]});
pass({url: moz, pattern: ["http://*"]}),
pass({url: moz, pattern: ["*mozilla*"]});
pass({url: moz, pattern: ["*example*", "*mozilla*"]});
pass({url: moz, pattern: ["*://*"]});
pass({url: "https://mozilla.org", pattern: ["*://*"]});
// Documentation example
pass({url: "http://www.example.com/foo/bar", pattern: ["http://???.example.com/foo/*"]});
pass({url: "http://the.example.com/foo/", pattern: ["http://???.example.com/foo/*"]});
fail({url: "http://my.example.com/foo/bar", pattern: ["http://???.example.com/foo/*"]});
fail({url: "http://example.com/foo/", pattern: ["http://???.example.com/foo/*"]});
fail({url: "http://www.example.com/foo", pattern: ["http://???.example.com/foo/*"]});
// Matches path
let path = moz + "/abc/def";
pass({url: path, pattern: ["*def"]});
pass({url: path, pattern: ["*c/d*"]});
pass({url: path, pattern: ["*org/abc*"]});
fail({url: path + "/", pattern: ["*def"]});
// Trailing slash
pass({url: moz, pattern: ["*.org/"]});
fail({url: moz, pattern: ["*.org"]});
// Wrong TLD
fail({url: moz, pattern: ["www*.m*.com/"]});
// Case sensitive
fail({url: moz, pattern: ["*.ORG/"]});
fail({url: moz, pattern: []});
}

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

@ -25,6 +25,8 @@ skip-if = toolkit == 'android'
skip-if = toolkit == 'android'
[test_MatchPattern.js]
skip-if = toolkit == 'android'
[test_MatchGlobs.js]
skip-if = toolkit == 'android'
[test_NewTabUtils.js]
skip-if = toolkit == 'android'
[test_ObjectUtils.js]