Source code
Revision control
Copy as Markdown
Other Tools
Test Info:
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
registerCleanupFunction(async () => {
Services.prefs.clearUserPref("network.url.useDefaultURI");
});
add_setup(async function () {
Services.prefs.setBoolPref("network.url.useDefaultURI", false);
});
add_task(async function test_MatchPattern_matches() {
function test(url, pattern, normalized = pattern, options = {}, explicit) {
let uri = Services.io.newURI(url);
pattern = Array.prototype.concat.call(pattern);
normalized = Array.prototype.concat.call(normalized);
let patterns = pattern.map(pat => new MatchPattern(pat, options));
let set = new MatchPatternSet(pattern, options);
let set2 = new MatchPatternSet(patterns, options);
deepEqual(
set2.patterns,
patterns,
"Patterns in set should equal the input patterns"
);
equal(
set.matches(uri, explicit),
set2.matches(uri, explicit),
"Single pattern and pattern set should return the same match"
);
for (let [i, pat] of patterns.entries()) {
equal(
pat.pattern,
normalized[i],
"Pattern property should contain correct normalized pattern value"
);
}
if (patterns.length == 1) {
equal(
patterns[0].matches(uri, explicit),
set.matches(uri, explicit),
"Single pattern and string set should return the same match"
);
}
return set.matches(uri, explicit);
}
function pass({ url, pattern, normalized, options, explicit }) {
ok(
test(url, pattern, normalized, options, explicit),
`Expected match: ${JSON.stringify(pattern)}, ${url}`
);
}
function fail({ url, pattern, normalized, options, explicit }) {
ok(
!test(url, pattern, normalized, options, explicit),
`Expected no match: ${JSON.stringify(pattern)}, ${url}`
);
}
function invalid({ pattern }) {
Assert.throws(
() => new MatchPattern(pattern),
/.*/,
`Invalid pattern '${pattern}' should throw`
);
Assert.throws(
() => new MatchPatternSet([pattern]),
/.*/,
`Invalid pattern '${pattern}' should throw`
);
}
// Invalid pattern.
invalid({ pattern: "" });
// Pattern must include trailing slash.
// Protocol not allowed.
invalid({ pattern: "http:/mozilla.com/" });
// Now try with * in the path.
// Check path stuff.
pass({
});
// Multiple patterns.
pass({
});
pass({
});
fail({
});
// Match url with fragments.
pass({
});
// Match data:-URLs.
pass({ url: "data:text/plain,foo", pattern: ["data:text/plain,foo"] });
pass({ url: "data:text/plain,foo", pattern: ["data:text/plain,*"] });
pass({
url: "data:text/plain;charset=utf-8,foo",
pattern: ["data:text/plain;charset=utf-8,foo"],
});
fail({
url: "data:text/plain,foo",
pattern: ["data:text/plain;charset=utf-8,foo"],
});
fail({
url: "data:text/plain;charset=utf-8,foo",
pattern: ["data:text/plain,foo"],
});
// Privileged matchers:
invalid({ pattern: "about:foo" });
pass({
url: "about:foo",
pattern: ["about:foo", "about:foo*"],
options: { restrictSchemes: false },
});
pass({
url: "about:foo",
pattern: ["about:foo*"],
options: { restrictSchemes: false },
});
pass({
url: "about:foobar",
pattern: ["about:foo*"],
options: { restrictSchemes: false },
});
pass({
options: { restrictSchemes: false },
});
fail({
options: { restrictSchemes: false },
});
fail({
url: "about:foo",
pattern: ["about:meh"],
options: { restrictSchemes: false },
});
// Matchers for schemes without host should ignore ignorePath.
pass({
pattern: ["about:reader*"],
options: { ignorePath: true, restrictSchemes: false },
});
pass({ url: "data:,", pattern: ["data:,*"], options: { ignorePath: true } });
// Matchers for schems without host should still match even if the explicit (host) flag is set.
pass({
url: "about:reader?explicit",
pattern: ["about:reader*"],
options: { restrictSchemes: false },
explicit: true,
});
pass({
url: "about:reader?explicit",
pattern: ["about:reader?explicit"],
options: { restrictSchemes: false },
explicit: true,
});
pass({ url: "data:,explicit", pattern: ["data:,explicit"], explicit: true });
pass({ url: "data:,explicit", pattern: ["data:,*"], explicit: true });
// Matchers without "//" separator in the pattern.
pass({ url: "data:text/plain;charset=utf-8,foo", pattern: ["data:*"] });
pass({
url: "about:blank",
pattern: ["about:*"],
options: { restrictSchemes: false },
});
pass({
pattern: ["view-source:*"],
options: { restrictSchemes: false },
});
invalid({ pattern: ["chrome:*"], options: { restrictSchemes: false } });
invalid({ pattern: "http:*" });
// Matchers for unrecognized schemes.
invalid({ pattern: "unknown-scheme:*" });
pass({
url: "unknown-scheme:foo",
pattern: ["unknown-scheme:foo"],
options: { restrictSchemes: false },
});
pass({
url: "unknown-scheme:foo",
pattern: ["unknown-scheme:*"],
options: { restrictSchemes: false },
});
pass({
options: { restrictSchemes: false },
});
pass({
options: { restrictSchemes: false },
});
pass({
pattern: ["unknown-scheme:*"],
options: { restrictSchemes: false },
});
pass({
url: "unknown-scheme:/foo",
pattern: ["unknown-scheme:/*"],
options: { restrictSchemes: false },
});
fail({
pattern: ["unknown-scheme:foo"],
options: { restrictSchemes: false },
});
fail({
url: "unknown-scheme:foo",
options: { restrictSchemes: false },
});
fail({
url: "unknown-scheme:foo",
options: { restrictSchemes: false },
});
fail({
url: "unknown-scheme:foo",
pattern: ["unknown-scheme:/*"],
options: { restrictSchemes: false },
});
// Matchers for IPv6
pass({
});
fail({
});
// Before fixing Bug 1529230, the only way to match a specific IPv6 url is by droping the brackets in pattern,
// thus we keep this pattern valid for the sake of backward compatibility
pass({
});
});
add_task(async function test_MatchPattern_overlaps() {
function test(filter, hosts, optional) {
filter = Array.prototype.concat.call(filter);
hosts = Array.prototype.concat.call(hosts);
optional = Array.prototype.concat.call(optional);
const set = new MatchPatternSet([...hosts, ...optional]);
const pat = new MatchPatternSet(filter);
return set.overlapsAll(pat);
}
function pass({ filter = [], hosts = [], optional = [] }) {
ok(
test(filter, hosts, optional),
`Expected overlap: ${filter}, ${hosts} (${optional})`
);
}
function fail({ filter = [], hosts = [], optional = [] }) {
ok(
!test(filter, hosts, optional),
`Expected no overlap: ${filter}, ${hosts} (${optional})`
);
}
// Direct comparison.
// Wildcard protocol.
// Wildcard subdomain.
// Wildcard subsumed.
// Subdomain vs substring.
// Wildcard domain.
// Wildcard wildcards.
fail({ hosts: "<all_urls>" });
// Multiple hosts.
// Multiple Multiples.
pass({
});
pass({
});
fail({
});
// Optional.
pass({
});
fail({
});
});
add_task(async function test_MatchGlob() {
function test(url, pattern) {
let m = new MatchGlob(pattern[0]);
return m.matches(Services.io.newURI(url).spec);
}
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}`
);
}
pass({ url: moz, pattern: ["*"] });
pass({ url: moz, pattern: ["*mozilla*"] });
// pass({url: moz, pattern: ["*example*", "*mozilla*"]});
pass({ url: moz, pattern: ["*://*"] });
// Documentation example
pass({
});
pass({
});
fail({
});
fail({
});
fail({
});
// 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: ["*oz*.com/"] });
// Case sensitive
fail({ url: moz, pattern: ["*.ORG/"] });
});
add_task(async function test_MatchGlob_redundant_wildcards_backtracking() {
const slow_build =
AppConstants.DEBUG || AppConstants.TSAN || AppConstants.ASAN;
const first_limit = slow_build ? 200 : 20;
{
let title = `Monster${"*".repeat(99)}Mash`;
// The first run could take longer than subsequent runs, as the DFA is lazily created.
let first_start = Date.now();
let glob = new MatchGlob(title);
let first_matches = glob.matches(title);
let first_duration = Date.now() - first_start;
ok(first_matches, `Expected match: ${title}, ${title}`);
Assert.less(
first_duration,
first_limit,
`First matching duration: ${first_duration}ms (limit: ${first_limit}ms)`
);
let start = Date.now();
let matches = glob.matches(title);
let duration = Date.now() - start;
ok(matches, `Expected match: ${title}, ${title}`);
Assert.less(duration, 10, `Matching duration: ${duration}ms`);
}
{
// Similarly with any continuous combination of ?**???****? wildcards.
let title = `Monster${"?*".repeat(99)}Mash`;
// The first run could take longer than subsequent runs, as the DFA is lazily created.
let first_start = Date.now();
let glob = new MatchGlob(title);
let first_matches = glob.matches(title);
let first_duration = Date.now() - first_start;
ok(first_matches, `Expected match: ${title}, ${title}`);
Assert.less(
first_duration,
first_limit,
`First matching duration: ${first_duration}ms (limit: ${first_limit}ms)`
);
let start = Date.now();
let matches = glob.matches(title);
let duration = Date.now() - start;
ok(matches, `Expected match: ${title}, ${title}`);
Assert.less(duration, 10, `Matching duration: ${duration}ms`);
}
});
add_task(async function test_MatchPattern_subsumes() {
function test(oldPat, newPat) {
let m = new MatchPatternSet(oldPat);
return m.subsumes(new MatchPattern(newPat));
}
function pass({ oldPat, newPat }) {
ok(test(oldPat, newPat), `${JSON.stringify(oldPat)} subsumes "${newPat}"`);
}
function fail({ oldPat, newPat }) {
ok(
!test(oldPat, newPat),
`${JSON.stringify(oldPat)} doesn't subsume "${newPat}"`
);
}
pass({ oldPat: ["<all_urls>"], newPat: "*://*/*" });
pass({ oldPat: ["*://*.example.com/*"], newPat: "*://sub.example.com/*" });
pass({
});
pass({
});
pass({
});
pass({
});
pass({
});
fail({ oldPat: ["*://*/*"], newPat: "<all_urls>" });
fail({
});
fail({
});
fail({
});
fail({
});
fail({
});
});
add_task(async function test_MatchPattern_matchesAllWebUrls() {
function test(patterns, options) {
let m = new MatchPatternSet(patterns, options);
if (patterns.length === 1) {
// Sanity check: with a single pattern, MatchPatternSet and MatchPattern
// have equivalent outputs.
equal(
new MatchPattern(patterns[0], options).matchesAllWebUrls,
m.matchesAllWebUrls,
"matchesAllWebUrls() is consistent in MatchPattern and MatchPatternSet"
);
}
return m.matchesAllWebUrls;
}
function pass(patterns, options) {
ok(
test(patterns, options),
`${JSON.stringify(patterns)} ${
options ? JSON.stringify(options) : ""
} matches all web URLs`
);
}
function fail(patterns, options) {
ok(
!test(patterns, options),
`${JSON.stringify(patterns)} ${
options ? JSON.stringify(options) : ""
} doesn't match all web URLs`
);
}
pass(["<all_urls>"]);
pass(["*://*/*"]);
pass(["*://*/"], { ignorePath: true });
fail(["*://*/"]); // missing path wildcard.
// Edge case: unusual number of wildcards in path.
pass(["*://*/**"]);
pass(["*://*/***"]);
pass(["*://*/***"], { ignorePath: true });
fail(["*://*//***"]);
// After the singular cases, test non-single cases.
fail([]);
});