diff options
Diffstat (limited to 'toolkit/components/extensions/test/xpcshell/test_ext_dnr_urlFilter.js')
-rw-r--r-- | toolkit/components/extensions/test/xpcshell/test_ext_dnr_urlFilter.js | 1159 |
1 files changed, 1159 insertions, 0 deletions
diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_dnr_urlFilter.js b/toolkit/components/extensions/test/xpcshell/test_ext_dnr_urlFilter.js new file mode 100644 index 0000000000..dd12184cbe --- /dev/null +++ b/toolkit/components/extensions/test/xpcshell/test_ext_dnr_urlFilter.js @@ -0,0 +1,1159 @@ +"use strict"; + +add_setup(() => { + Services.prefs.setBoolPref("extensions.manifestV3.enabled", true); + Services.prefs.setBoolPref("extensions.dnr.enabled", true); + Services.prefs.setBoolPref("extensions.dnr.feedback", true); +}); + +// This function is serialized and called in the context of the test extension's +// background page. dnrTestUtils is passed to the background function. +function makeDnrTestUtils() { + const dnrTestUtils = {}; + const dnr = browser.declarativeNetRequest; + + const DUMMY_ACTION = { + // "modifyHeaders" is the only action that allows multiple rule matches. + type: "modifyHeaders", + responseHeaders: [{ operation: "append", header: "x", value: "y" }], + }; + async function testMatchesRequest(request, ruleIds, description) { + browser.test.assertDeepEq( + ruleIds, + (await dnr.testMatchOutcome(request)).matchedRules.map(mr => mr.ruleId), + description + ); + } + async function testMatchesUrlFilter({ + urlFilter, + isUrlFilterCaseSensitive, + urls = [], + urlsNonMatching = [], + }) { + // Sanity check: verify that there are no unexpected escaped characters, + // because that can surprise. + function sanityCheckUrl(url) { + const normalizedUrl = new URL(url).href; + if (normalizedUrl.split("%").length !== url.split("*").length) { + // ^ we only check for %-escapes and not exact URL equality because the + // tests imported from Chrome often omit the "/" (path separator). + browser.test.assertEq(normalizedUrl, url, "url should be canonical"); + } + } + + await dnr.updateSessionRules({ + addRules: [ + { + id: 12345, + condition: { urlFilter, isUrlFilterCaseSensitive }, + action: DUMMY_ACTION, + }, + ], + }); + for (let url of urls) { + sanityCheckUrl(url); + const request = { url, type: "other" }; + const description = `urlFilter ${urlFilter} should match: ${url}`; + await testMatchesRequest(request, [12345], description); + } + for (let url of urlsNonMatching) { + sanityCheckUrl(url); + const request = { url, type: "other" }; + const description = `urlFilter ${urlFilter} should not match: ${url}`; + await testMatchesRequest(request, [], description); + } + await dnr.updateSessionRules({ removeRuleIds: [12345] }); + } + Object.assign(dnrTestUtils, { + DUMMY_ACTION, + testMatchesRequest, + testMatchesUrlFilter, + }); + return dnrTestUtils; +} + +async function runAsDNRExtension({ background, manifest }) { + let extension = ExtensionTestUtils.loadExtension({ + background: `(${background})((${makeDnrTestUtils})())`, + manifest: { + manifest_version: 3, + permissions: ["declarativeNetRequest", "declarativeNetRequestFeedback"], + // While testing urlFilter itself does not require any host permissions, + // we are asking for host permissions anyway because the "modifyHeaders" + // action requires host permissions, and we use the "modifyHeaders" action + // to ensure that we can detect when multiple rules match. + host_permissions: ["<all_urls>"], + granted_host_permissions: true, + ...manifest, + }, + temporarilyInstalled: true, // <-- for granted_host_permissions + }); + await extension.startup(); + await extension.awaitFinish(); + await extension.unload(); +} + +// This test checks various urlFilters with a possibly ambiguous interpretation. +// In some cases the semantic difference in interpretation can have different +// outcomes; in these cases we have chosen the behavior as observed in Chrome. +add_task(async function ambiguous_urlFilter_patterns() { + await runAsDNRExtension({ + background: async dnrTestUtils => { + const { testMatchesUrlFilter } = dnrTestUtils; + + // Left anchor, empty pattern: always matches + // Ambiguous with Right anchor, but same result. + await testMatchesUrlFilter({ + urlFilter: "|", + urls: ["http://a/"], + urlsNonMatching: [], + }); + + // Domain anchor, empty pattern: always matches. + // Ambiguous with Left anchor + Right anchor, the latter would not match + // anything (only an empty string, but URLs cannot be empty). + await testMatchesUrlFilter({ + urlFilter: "||", + urls: ["http://a/"], + urlsNonMatching: [], + }); + + // Domain anchor plus Right separator: never matches. + // Ambiguous with Left anchor + | + Right anchor, that is no match either. + await testMatchesUrlFilter({ + urlFilter: "|||", + urls: [], + urlsNonMatching: ["http://a./|||"], + }); + + // Repeated separator: ^^^^ matches separator chars (=everything except + // alphanumeric, "_", "-", ".", "%"), but when at the end of a string, + // the last "^" can also be interpreted as a right anchor (like ^^^|). + // Ambiguous: while "^" is defined to match the end of URL, it could also + // be interpreted as "^^^^" matching the end of URL 4x, i.e. always. + await testMatchesUrlFilter({ + urlFilter: "^^^^", + urls: [ + // Note: "^" is escaped "%5E" when part of the URL, except after "#". + "http://a/#frag^^^^", // four ^ characters ("^^^^"). + "http://a/#frag^^^", // three ^ characters ("^^^") + end of URL. + "http://a/?&#", // four separator characters ("/?&#"); + "http://a/#^", // three separator characters ("/??") + end of URL. + // ^ Note that "^" is after "#" and therefore not %5E. If "^" were to + // somehow be %-encoded to "%5E", then the end would become "/#%5E" + // and the "/#%" would only be 3 separators followed by alphanum. The + // test matching shows that the canonical representation of "^" after + // a "#" is "^" and can be matched. + ], + urlsNonMatching: [ + "http://a/?", // Just two separator + end of URL, not matching 4x "^". + "http://a/____", // _ is specified to not match ^. + "http://a/----", // - is specified to not match ^. + "http://a/....", // . is specified to not match ^. + ], + }); + // Not ambiguous, but for comparison with "^^^^": all http(s) match. + await testMatchesUrlFilter({ + urlFilter: "^^^", + urls: ["https://a/"], // "://" always matches "^^^". + // Not seen by DNR in practice, but could be passed to testMatchOutcome: + urlsNonMatching: ["file:hello/no/three/consecutive/special/characters"], + }); + + // Separator plus Right anchor: always matches. + // Ambiguous: "^" is defined to match the end of URL once, but a right + // domain anchor already matches that. A potential interpretation is for + // "^" to be required to match a non-alphanumeric (etc.), but in practice + // "^" is allowed to match the end of the URL. Effectively "^|" = "|". + await testMatchesUrlFilter({ + urlFilter: "^|", + urls: [ + "http://a/", // "/" matches "^". + "http://a/a", // "a" does not match "^", but "^" matches the end. + ], + urlsNonMatching: [], + }); + + // Domain anchor plus separator: "^" only matches non-alphanum (etc.) + // Ambiguous: "||" is defined to match a domain anchor. There is no + // domain part after the trailing "." of a FQDN. Still, "." matches. + await testMatchesUrlFilter({ + urlFilter: "||^", + urls: ["http://a./"], // FQDN: "/" after "." matches "^". + urlsNonMatching: ["http://a/", "http://a/||"], + }); + + browser.test.notifyPass(); + }, + }); +}); + +add_task(async function urlFilter_domain_anchor() { + await runAsDNRExtension({ + background: async dnrTestUtils => { + const { testMatchesUrlFilter } = dnrTestUtils; + + await testMatchesUrlFilter({ + // Not a domain anchor, but for comparison with "||ps" below: + urlFilter: "ps", + urls: [ + "https://example.com/", // ps in scheme. + "http://ps.example.com/", // ps at start of domain. + "http://sub.ps.example.com/", // ps at superdomain. + "http://ps/", // ps as sole host. + "http://example-ps.com/", // ps in middle of domain. + "http://ps@example.com/", // ps as user without password. + "http://user:ps@example.com/", // ps in password. + "http://ps:pass@example.com/", // ps in user. + "http://example.com/ps", // ps at end. + "http://example.com/#ps", // ps in fragment. + ], + urlsNonMatching: [ + "http://example.com/", // no ps anywhere. + ], + }); + + await testMatchesUrlFilter({ + urlFilter: "||ps", + urls: [ + "http://ps.example.com/", // ps at start of domain. + "http://sub.ps.example.com/", // ps at superdomain. + "http://ps/", // ps as sole host. + ], + urlsNonMatching: [ + "http://example.com/", // no ps anywhere. + "https://example.com/", // ps in scheme. + "http://example-ps.com/", // ps in middle + "http://ps@example.com/", // ps as user without password. + "http://user:ps@example.com/", // ps in password. + "http://ps:pass@example.com/", // ps in user. + "http://example.com/ps", // ps at end. + ], + }); + + await testMatchesUrlFilter({ + urlFilter: "||1", + urls: [ + "http://127.0.0.1/", + "http://2.0.0.1/", + "http://www.1example.com/", + ], + urlsNonMatching: [ + "http://[::1]/", + "http://[1::1]/", + "http://hostwithport:1/", + "http://host/1", + "http://fqdn.:1/", + "http://fqdn./1", + ], + }); + + await testMatchesUrlFilter({ + urlFilter: "||^1", + urls: [ + "http://[1::1]/", // "[1" at start matches "^1". + "http://fqdn.:1/", // ":1" matches "^1" and is after a ".". + "http://fqdn./1", // "/1" matches "^1" and is after a ".". + ], + urlsNonMatching: [ + "http://127.0.0.1/", + "http://2.0.0.1/", + "http://www.1example.com/", + "http://[::1]/", + "http://hostwithport:1/", + "http://host/1", + ], + }); + + browser.test.notifyPass(); + }, + }); +}); + +// Extreme patterns that should not be used in practice, but are not explicitly +// documented to be disallowed. +add_task( + // Stuck in ccov: https://bugzilla.mozilla.org/show_bug.cgi?id=1806494#c4 + { skip_if: () => mozinfo.ccov }, + async function extreme_urlFilter_patterns() { + await runAsDNRExtension({ + background: async dnrTestUtils => { + const { testMatchesRequest, DUMMY_ACTION } = dnrTestUtils; + + await browser.declarativeNetRequest.updateSessionRules({ + addRules: [ + { + id: 1, + condition: { + urlFilter: "*".repeat(1e6), + }, + action: DUMMY_ACTION, + }, + { + id: 2, + condition: { + urlFilter: "^".repeat(1e6), + }, + action: DUMMY_ACTION, + }, + { + id: 3, + condition: { + // Note: 2 chars repeat 5e5 instead of 1e6 because newURI limits + // the length of the URL (to network.standard-url.max-length), + // so we would not be able to verify whether the URL is really + // that long. + urlFilter: "*^".repeat(5e5), + }, + action: DUMMY_ACTION, + }, + { + id: 4, + condition: { + // Note: well beyond the maximum length of a URL. But as "*" can + // match any char (including zero length), this still matches. + urlFilter: "h" + "*".repeat(1e7) + "endofurl", + }, + action: DUMMY_ACTION, + }, + ], + }); + + await testMatchesRequest( + { url: "http://example.com/", type: "other" }, + [1], + "urlFilter with 1M wildcard chars matches any URL" + ); + + await testMatchesRequest( + { url: "http://example.com/" + "x".repeat(1e6), type: "other" }, + [1], + "urlFilter with 1M wildcards matches, other '^' do not match alpha" + ); + + await testMatchesRequest( + { url: "http://example.com/" + "/".repeat(1e6), type: "other" }, + [1, 2, 3], + "urlFilter with 1M wildcards, ^ and *^ all match URL with 1M '/' chars" + ); + + await testMatchesRequest( + { url: "http://example.com/" + "x/".repeat(5e5), type: "other" }, + [1, 3], + "urlFilter with 1M wildcards and *^ match URL with 1M 'x/' chars" + ); + + await testMatchesRequest( + { url: "http://example.com/endofurl", type: "other" }, + [1, 4], + "urlFilter with 1M and 10M wildcards matches URL" + ); + + browser.test.notifyPass(); + }, + }); + } +); + +add_task(async function test_isUrlFilterCaseSensitive() { + await runAsDNRExtension({ + background: async dnrTestUtils => { + const { testMatchesUrlFilter } = dnrTestUtils; + + await testMatchesUrlFilter({ + urlFilter: "AbC", + isUrlFilterCaseSensitive: true, + urls: [ + "http://true.example.com/AbC", // Exact match. + ], + urlsNonMatching: [ + "http://true.example.com/abc", // All lower. + "http://true.example.com/ABC", // All upper. + "http://true.example.com/???", // ABC not present at all. + "http://true.AbC/", // When canonicalized, the host is lower case. + ], + }); + await testMatchesUrlFilter({ + urlFilter: "AbC", + isUrlFilterCaseSensitive: false, + urls: [ + "http://false.example.com/AbC", // Exact match. + "http://false.example.com/abc", // All lower. + "http://false.example.com/ABC", // All upper. + "http://false.AbC/", // When canonicalized, the host is lower case. + ], + urlsNonMatching: [ + "http://false.example.com/???", // ABC not present at all. + ], + }); + + // Chrome's initial DNR API specified isUrlFilterCaseSensitive to be true + // by default. Later, it became false by default. + // https://github.com/w3c/webextensions/issues/269 + await testMatchesUrlFilter({ + urlFilter: "AbC", + // isUrlFilterCaseSensitive: false, // is implied by default. + urls: [ + "http://default.example.com/AbC", // Exact match. + "http://default.example.com/abc", // All lower. + "http://default.example.com/ABC", // All upper. + "http://default.AbC/", // When canonicalized, the host is lower case. + ], + urlsNonMatching: [ + "http://default.example.com/???", // ABC not present at all. + ], + }); + + browser.test.notifyPass(); + }, + }); +}); + +// Imported tests from Chromium from: +// https://chromium.googlesource.com/chromium/src.git/+/refs/tags/110.0.5442.0/components/url_pattern_index/url_pattern_unittest.cc +// kAnchorNone -> "" (anywhere in the string) +// kBoundary -> | (start or end of string) +// kSubdomain -> || (start of (sub)domain) +// kMatchCase -> isUrlFilterCaseSensitive: true +// kDonotMatchCase -> isUrlFilterCaseSensitive: false (this is the default). +// proto::URL_PATTERN_TYPE_WILDCARDED / proto::URL_PATTERN_TYPE_SUBSTRING -> "" +// +// Minus two tests ("", kBoundary, kBoundary) because the resulting pattern is +// "||" and ambiguous with ("", kSubdomain, ""). +add_task(async function test_chrome_parity() { + await runAsDNRExtension({ + background: async dnrTestUtils => { + const { testMatchesUrlFilter } = dnrTestUtils; + const testCases = [ + // {"", proto::URL_PATTERN_TYPE_SUBSTRING} + { + urlFilter: "*", + url: "http://ex.com/", + expectMatch: true, + }, + // // {"", proto::URL_PATTERN_TYPE_WILDCARDED} + // { // Already tested before. + // urlFilter: "*", + // url: "http://ex.com/", + // expectMatch: true, + // }, + // {"", kBoundary, kAnchorNone} + { + urlFilter: "|", + url: "http://ex.com/", + expectMatch: true, + }, + // {"", kSubdomain, kAnchorNone} + { + urlFilter: "||", + url: "http://ex.com/", + expectMatch: true, + }, + // // {"", kSubdomain, kAnchorNone} + // { // Already tested before. + // urlFilter: "||", + // url: "http://ex.com/", + // expectMatch: true, + // }, + // {"^", kSubdomain, kAnchorNone} + { + urlFilter: "||^", + url: "http://ex.com/", + expectMatch: false, + }, + // {".", kSubdomain, kAnchorNone} + { + urlFilter: "||.", + url: "http://ex.com/", + expectMatch: false, + }, + // // {"", kAnchorNone, kBoundary} + // { // Already tested before. + // urlFilter: "|", + // url: "http://ex.com/", + // expectMatch: true, + // }, + // {"^", kAnchorNone, kBoundary} + { + urlFilter: "^|", + url: "http://ex.com/", + expectMatch: true, + }, + // {".", kAnchorNone, kBoundary} + { + urlFilter: ".|", + url: "http://ex.com/", + expectMatch: false, + }, + // // {"", kBoundary, kBoundary} + // { // "||" is ambiguous, cannot mean Left anchor + Right anchor + // urlFilter: "||", + // url: "http://ex.com/", + // expectMatch: false, + // }, + // {"", kSubdomain, kBoundary} + { + urlFilter: "|||", + url: "http://ex.com/", + expectMatch: false, + }, + // {"com/", kSubdomain, kBoundary} + { + urlFilter: "||com/|", + url: "http://ex.com/", + expectMatch: true, + }, + // {"xampl", proto::URL_PATTERN_TYPE_SUBSTRING} + { + urlFilter: "xampl", + url: "http://example.com", + expectMatch: true, + }, + // {"example", proto::URL_PATTERN_TYPE_SUBSTRING} + { + urlFilter: "example", + url: "http://example.com", + expectMatch: true, + }, + // {"/a?a"} + { + urlFilter: "/a?a", + url: "http://ex.com/a?a", + expectMatch: true, + }, + // {"^abc"} + { + urlFilter: "^abc", + url: "http://ex.com/abc?a", + expectMatch: true, + }, + // {"^abc"} + { + urlFilter: "^abc", + url: "http://ex.com/a?abc", + expectMatch: true, + }, + // {"^abc"} + { + urlFilter: "^abc", + url: "http://ex.com/abc?abc", + expectMatch: true, + }, + // {"^abc^abc"} + { + urlFilter: "^abc^abc", + url: "http://ex.com/abc?abc", + expectMatch: true, + }, + // {"^com^abc^abc"} + { + urlFilter: "^com^abc^abc", + url: "http://ex.com/abc?abc", + expectMatch: false, + }, + // {"http://ex", kBoundary, kAnchorNone} + { + urlFilter: "|http://ex", + url: "http://example.com", + expectMatch: true, + }, + // {"http://ex", kAnchorNone, kAnchorNone} + { + urlFilter: "http://ex", + url: "http://example.com", + expectMatch: true, + }, + // {"mple.com/", kAnchorNone, kBoundary} + { + urlFilter: "mple.com/|", + url: "http://example.com", + expectMatch: true, + }, + // {"mple.com/", kAnchorNone, kAnchorNone} + { + urlFilter: "mple.com/", + url: "http://example.com", + expectMatch: true, + }, + // {"mple.com/", kSubdomain, kAnchorNone} + { + urlFilter: "||mple.com/", + url: "http://example.com", + expectMatch: false, + }, + // {"ex.com", kSubdomain, kAnchorNone} + { + urlFilter: "||ex.com", + url: "http://hex.com", + expectMatch: false, + }, + // {"ex.com", kSubdomain, kAnchorNone} + { + urlFilter: "||ex.com", + url: "http://ex.com", + expectMatch: true, + }, + // {"ex.com", kSubdomain, kAnchorNone} + { + urlFilter: "||ex.com", + url: "http://hex.ex.com", + expectMatch: true, + }, + // {"ex.com", kSubdomain, kAnchorNone} + { + urlFilter: "||ex.com", + url: "http://hex.hex.com", + expectMatch: false, + }, + // {"example.com^", kSubdomain, kAnchorNone} + { + urlFilter: "||example.com^", + url: "http://www.example.com", + expectMatch: true, + }, + // {"http://*mpl", kBoundary, kAnchorNone} + { + urlFilter: "|http://*mpl", + url: "http://example.com", + expectMatch: true, + }, + // {"mpl*com/", kAnchorNone, kBoundary} + { + urlFilter: "mpl*com/|", + url: "http://example.com", + expectMatch: true, + }, + // {"example^com"} + { + urlFilter: "example^com", + url: "http://example.com", + expectMatch: false, + }, + // {"example^com"} + { + urlFilter: "example^com", + url: "http://example/com", + expectMatch: true, + }, + // {"example.com^"} + { + urlFilter: "example.com^", + url: "http://example.com:8080", + expectMatch: true, + }, + // {"http*.com/", kBoundary, kBoundary} + { + urlFilter: "|http*.com/|", + url: "http://example.com", + expectMatch: true, + }, + // {"http*.org/", kBoundary, kBoundary} + { + urlFilter: "|http*.org/|", + url: "http://example.com", + expectMatch: false, + }, + // {"/path?*&p1=*&p2="} + { + urlFilter: "/path?*&p1=*&p2=", + url: "http://ex.com/aaa/path/bbb?k=v&p1=0&p2=1", + expectMatch: false, + }, + // {"/path?*&p1=*&p2="} + { + urlFilter: "/path?*&p1=*&p2=", + url: "http://ex.com/aaa/path?k=v&p1=0&p2=1", + expectMatch: true, + }, + // {"/path?*&p1=*&p2="} + { + urlFilter: "/path?*&p1=*&p2=", + url: "http://ex.com/aaa/path?k=v&k=v&p1=0&p2=1", + expectMatch: true, + }, + // {"/path?*&p1=*&p2="} + { + urlFilter: "/path?*&p1=*&p2=", + url: "http://ex.com/aaa/path?k=v&p1=0&p3=10&p2=1", + expectMatch: true, + }, + // {"/path?*&p1=*&p2="} + { + urlFilter: "/path?*&p1=*&p2=", + url: "http://ex.com/aaa/path&p1=0&p2=1", + expectMatch: false, + }, + // {"/path?*&p1=*&p2="} + { + urlFilter: "/path?*&p1=*&p2=", + url: "http://ex.com/aaa/path?k=v&p2=0&p1=1", + expectMatch: false, + }, + // {"abc*def*ghijk*xyz"} + { + urlFilter: "abc*def*ghijk*xyz", + url: "http://example.com/abcdeffffghijkmmmxyzzz", + expectMatch: true, + }, + // {"abc*cdef"} + { + urlFilter: "abc*cdef", + url: "http://example.com/abcdef", + expectMatch: false, + }, + // {"^^a^^"} + { + urlFilter: "^^a^^", + url: "http://ex.com/?a=/", + expectMatch: true, + }, + // {"^^a^^"} + { + urlFilter: "^^a^^", + url: "http://ex.com/?a=/&b=0", + expectMatch: true, + }, + // {"^^a^^"} + { + urlFilter: "^^a^^", + url: "http://ex.com/?a=x", + expectMatch: false, + }, + // {"^^a^^"} + { + urlFilter: "^^a^^", + url: "http://ex.com/?a=", + expectMatch: true, + }, + // {"ex.com^path^*k=v^"} + { + urlFilter: "ex.com^path^*k=v^", + url: "http://ex.com/path/?k1=v1&ak=v&kk=vv", + expectMatch: true, + }, + // {"ex.com^path^*k=v^"} + { + urlFilter: "ex.com^path^*k=v^", + url: "http://ex.com/p/path/?k1=v1&ak=v&kk=vv", + expectMatch: false, + }, + // {"a^a&a^a&"} + { + urlFilter: "a^a&a^a&", + url: "http://ex.com/a/a/a/a/?a&a&a&a&a", + expectMatch: true, + }, + // {"abc*def^"} + { + urlFilter: "abc*def^", + url: "http://ex.com/abc/a/ddef/", + expectMatch: true, + }, + // {"https://example.com/"} + { + urlFilter: "https://example.com/", + url: "http://example.com/", + expectMatch: false, + }, + // {"example.com/", kSubdomain, kAnchorNone} + { + urlFilter: "||example.com/", + url: "http://example.com/", + expectMatch: true, + }, + // {"examp", kSubdomain, kAnchorNone} + { + urlFilter: "||examp", + url: "http://example.com/", + expectMatch: true, + }, + // {"xamp", kSubdomain, kAnchorNone} + { + urlFilter: "||xamp", + url: "http://example.com/", + expectMatch: false, + }, + // {"examp", kSubdomain, kAnchorNone} + { + urlFilter: "||examp", + url: "http://test.example.com/", + expectMatch: true, + }, + // {"t.examp", kSubdomain, kAnchorNone} + { + urlFilter: "||t.examp", + url: "http://test.example.com/", + expectMatch: false, + }, + // {"com^", kSubdomain, kAnchorNone} + { + urlFilter: "||com^", + url: "http://test.example.com/", + expectMatch: true, + }, + // {"com^x", kSubdomain, kBoundary} + { + urlFilter: "||com^x|", + url: "http://a.com/x", + expectMatch: true, + }, + // {"x.com", kSubdomain, kAnchorNone} + { + urlFilter: "||x.com", + url: "http://ex.com/?url=x.com", + expectMatch: false, + }, + // {"ex.com/", kSubdomain, kBoundary} + { + urlFilter: "||ex.com/|", + url: "http://ex.com/", + expectMatch: true, + }, + // {"ex.com^", kSubdomain, kBoundary} + { + urlFilter: "||ex.com^|", + url: "http://ex.com/", + expectMatch: true, + }, + // {"ex.co", kSubdomain, kBoundary} + { + urlFilter: "||ex.co|", + url: "http://ex.com/", + expectMatch: false, + }, + // {"ex.com", kSubdomain, kBoundary} + { + urlFilter: "||ex.com|", + url: "http://rex.com.ex.com/", + expectMatch: false, + }, + // {"ex.com/", kSubdomain, kBoundary} + { + urlFilter: "||ex.com/|", + url: "http://rex.com.ex.com/", + expectMatch: true, + }, + // {"http", kSubdomain, kBoundary} + { + urlFilter: "||http|", + url: "http://http.com/", + expectMatch: false, + }, + // {"http", kSubdomain, kAnchorNone} + { + urlFilter: "||http", + url: "http://http.com/", + expectMatch: true, + }, + // {"/example.com", kSubdomain, kBoundary} + { + urlFilter: "||/example.com|", + url: "http://example.com/", + expectMatch: false, + }, + // {"/example.com/", kSubdomain, kBoundary} + { + urlFilter: "||/example.com/|", + url: "http://example.com/", + expectMatch: false, + }, + // {".", kSubdomain, kAnchorNone} + { + urlFilter: "||.", + url: "http://a..com/", + expectMatch: true, + }, + // {"^", kSubdomain, kAnchorNone} + { + urlFilter: "||^", + url: "http://a..com/", + expectMatch: false, + }, + // {".", kSubdomain, kAnchorNone} + { + urlFilter: "||.", + url: "http://a.com./", + expectMatch: false, + }, + // {"^", kSubdomain, kAnchorNone} + { + urlFilter: "||^", + url: "http://a.com./", + expectMatch: true, + }, + // {".", kSubdomain, kAnchorNone} + { + urlFilter: "||.", + url: "http://a.com../", + expectMatch: true, + }, + // {"^", kSubdomain, kAnchorNone} + { + urlFilter: "||^", + url: "http://a.com../", + expectMatch: true, + }, + // {"/path", kSubdomain, kAnchorNone} + { + urlFilter: "||/path", + url: "http://a.com./path/to/x", + expectMatch: true, + }, + // {"^path", kSubdomain, kAnchorNone} + { + urlFilter: "||^path", + url: "http://a.com./path/to/x", + expectMatch: true, + }, + // {"/path", kSubdomain, kBoundary} + { + urlFilter: "||/path|", + url: "http://a.com./path", + expectMatch: true, + }, + // {"^path", kSubdomain, kBoundary} + { + urlFilter: "||^path|", + url: "http://a.com./path", + expectMatch: true, + }, + // {"path", kSubdomain, kBoundary} + { + urlFilter: "||path|", + url: "http://a.com./path", + expectMatch: false, + }, + // {"path", proto::URL_PATTERN_TYPE_SUBSTRING, kDonotMatchCase} + { + urlFilter: "path", + url: "http://a.com/PaTh", + isUrlFilterCaseSensitive: false, + expectMatch: true, + }, + // {"path", proto::URL_PATTERN_TYPE_SUBSTRING, kMatchCase} + { + urlFilter: "path", + url: "http://a.com/PaTh", + isUrlFilterCaseSensitive: true, + expectMatch: false, + }, + // {"path", proto::URL_PATTERN_TYPE_SUBSTRING, kDonotMatchCase} + { + urlFilter: "path", + url: "http://a.com/path", + isUrlFilterCaseSensitive: false, + expectMatch: true, + }, + // {"path", proto::URL_PATTERN_TYPE_SUBSTRING, kMatchCase} + { + urlFilter: "path", + url: "http://a.com/path", + isUrlFilterCaseSensitive: true, + expectMatch: true, + }, + // {"abc*def^", proto::URL_PATTERN_TYPE_WILDCARDED, kMatchCase} + { + urlFilter: "abc*def^", + url: "http://a.com/abcxAdef/vo", + isUrlFilterCaseSensitive: true, + expectMatch: true, + }, + // {"abc*def^", proto::URL_PATTERN_TYPE_WILDCARDED, kMatchCase} + { + urlFilter: "abc*def^", + url: "http://a.com/aBcxAdeF/vo", + isUrlFilterCaseSensitive: true, + expectMatch: false, + }, + // {"abc*def^", proto::URL_PATTERN_TYPE_WILDCARDED, kDonotMatchCase} + { + urlFilter: "abc*def^", + url: "http://a.com/aBcxAdeF/vo", + isUrlFilterCaseSensitive: false, + expectMatch: true, + }, + // {"abc*def^", proto::URL_PATTERN_TYPE_WILDCARDED, kDonotMatchCase} + { + urlFilter: "abc*def^", + url: "http://a.com/abcxAdef/vo", + isUrlFilterCaseSensitive: false, + expectMatch: true, + }, + // {"abc^", kAnchorNone, kAnchorNone} + { + urlFilter: "abc^", + url: "https://xyz.com/abc/123", + expectMatch: true, + }, + // {"abc^", kAnchorNone, kAnchorNone} + { + urlFilter: "abc^", + url: "https://xyz.com/abc", + expectMatch: true, + }, + // {"abc^", kAnchorNone, kAnchorNone} + { + urlFilter: "abc^", + url: "https://abc.com", + expectMatch: false, + }, + // {"abc^", kAnchorNone, kBoundary} + { + urlFilter: "abc^|", + url: "https://xyz.com/abc/", + expectMatch: true, + }, + // {"abc^", kAnchorNone, kBoundary} + { + urlFilter: "abc^|", + url: "https://xyz.com/abc", + expectMatch: true, + }, + // {"abc^", kAnchorNone, kBoundary} + { + urlFilter: "abc^|", + url: "https://xyz.com/abc/123", + expectMatch: false, + }, + // {"http://abc.com/x^", kBoundary, kAnchorNone} + { + urlFilter: "|http://abc.com/x^", + url: "http://abc.com/x", + expectMatch: true, + }, + // {"http://abc.com/x^", kBoundary, kAnchorNone} + { + urlFilter: "|http://abc.com/x^", + url: "http://abc.com/x/", + expectMatch: true, + }, + // {"http://abc.com/x^", kBoundary, kAnchorNone} + { + urlFilter: "|http://abc.com/x^", + url: "http://abc.com/x/123", + expectMatch: true, + }, + // {"http://abc.com/x^", kBoundary, kBoundary} + { + urlFilter: "|http://abc.com/x^|", + url: "http://abc.com/x", + expectMatch: true, + }, + // {"http://abc.com/x^", kBoundary, kBoundary} + { + urlFilter: "|http://abc.com/x^|", + url: "http://abc.com/x/", + expectMatch: true, + }, + // {"http://abc.com/x^", kBoundary, kBoundary} + { + urlFilter: "|http://abc.com/x^|", + url: "http://abc.com/x/123", + expectMatch: false, + }, + // {"abc.com^", kSubdomain, kAnchorNone} + { + urlFilter: "||abc.com^", + url: "http://xyz.abc.com/123", + expectMatch: true, + }, + // {"abc.com^", kSubdomain, kAnchorNone} + { + urlFilter: "||abc.com^", + url: "http://xyz.abc.com", + expectMatch: true, + }, + // {"abc.com^", kSubdomain, kAnchorNone} + { + urlFilter: "||abc.com^", + url: "http://abc.com.xyz.com?q=abc.com", + expectMatch: false, + }, + // {"abc.com^", kSubdomain, kBoundary} + { + urlFilter: "||abc.com^|", + url: "http://xyz.abc.com/123", + expectMatch: false, + }, + // {"abc.com^", kSubdomain, kBoundary} + { + urlFilter: "||abc.com^|", + url: "http://xyz.abc.com", + expectMatch: true, + }, + // {"abc.com^", kSubdomain, kBoundary} + { + urlFilter: "||abc.com^|", + url: "http://abc.com.xyz.com?q=abc.com/", + expectMatch: false, + }, + // {"abc*^", kAnchorNone, kAnchorNone} + { + urlFilter: "abc*^", + url: "https://abc.com", + expectMatch: true, + }, + // {"abc*^", kAnchorNone, kAnchorNone} + { + urlFilter: "abc*^", + url: "https://abc.com?q=123", + expectMatch: true, + }, + // {"abc*^", kAnchorNone, kBoundary} + { + urlFilter: "abc*^|", + url: "https://abc.com", + expectMatch: true, + }, + // {"abc*^", kAnchorNone, kBoundary} + { + urlFilter: "abc*^|", + url: "https://abc.com?q=123", + expectMatch: true, + }, + // {"abc*", kAnchorNone, kBoundary} + { + urlFilter: "abc*|", + url: "https://a.com/abcxyz", + expectMatch: true, + }, + // {"*google.com", kBoundary, kAnchorNone} + { + urlFilter: "|*google.com", + url: "https://www.google.com", + expectMatch: true, + }, + // {"*", kBoundary, kBoundary} + { + urlFilter: "|*|", + url: "https://example.com", + expectMatch: true, + }, + // // {"", kBoundary, kBoundary} + // { // "||" is ambiguous, cannot mean Left anchor + Right anchor + // urlFilter: "||", + // url: "https://example.com", + // expectMatch: false, + // }, + ]; + for (let test of testCases) { + let { urlFilter, url, expectMatch, isUrlFilterCaseSensitive } = test; + if (expectMatch) { + await testMatchesUrlFilter({ + urlFilter, + isUrlFilterCaseSensitive, + urls: [url], + }); + } else { + await testMatchesUrlFilter({ + urlFilter, + isUrlFilterCaseSensitive, + urlsNonMatching: [url], + }); + } + } + + browser.test.notifyPass(); + }, + }); +}); |