summaryrefslogtreecommitdiffstats
path: root/toolkit/components/extensions/MatchURLFilters.sys.mjs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /toolkit/components/extensions/MatchURLFilters.sys.mjs
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/components/extensions/MatchURLFilters.sys.mjs')
-rw-r--r--toolkit/components/extensions/MatchURLFilters.sys.mjs174
1 files changed, 174 insertions, 0 deletions
diff --git a/toolkit/components/extensions/MatchURLFilters.sys.mjs b/toolkit/components/extensions/MatchURLFilters.sys.mjs
new file mode 100644
index 0000000000..22060151e7
--- /dev/null
+++ b/toolkit/components/extensions/MatchURLFilters.sys.mjs
@@ -0,0 +1,174 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Match WebNavigation URL Filters.
+export class MatchURLFilters {
+ constructor(filters) {
+ if (!Array.isArray(filters)) {
+ throw new TypeError("filters should be an array");
+ }
+
+ if (!filters.length) {
+ throw new Error("filters array should not be empty");
+ }
+
+ this.filters = filters;
+ }
+
+ matches(url) {
+ let uri = Services.io.newURI(url);
+ // Set uriURL to an empty object (needed because some schemes, e.g. about doesn't support nsIURL).
+ let uriURL = {};
+ if (uri instanceof Ci.nsIURL) {
+ uriURL = uri;
+ }
+
+ // Set host to a empty string by default (needed so that schemes without an host,
+ // e.g. about, can pass an empty string for host based event filtering as expected).
+ let host = "";
+ try {
+ host = uri.host;
+ } catch (e) {
+ // 'uri.host' throws an exception with some uri schemes (e.g. about).
+ }
+
+ let port;
+ try {
+ port = uri.port;
+ } catch (e) {
+ // 'uri.port' throws an exception with some uri schemes (e.g. about),
+ // in which case it will be |undefined|.
+ }
+
+ let data = {
+ // NOTE: This properties are named after the name of their related
+ // filters (e.g. `pathContains/pathEquals/...` will be tested against the
+ // `data.path` property, and the same is done for the `host`, `query` and `url`
+ // components as well).
+ path: uriURL.filePath,
+ query: uriURL.query,
+ host,
+ port,
+ url,
+ };
+
+ // If any of the filters matches, matches returns true.
+ return this.filters.some(filter =>
+ this.matchURLFilter({ filter, data, uri, uriURL })
+ );
+ }
+
+ matchURLFilter({ filter, data, uri, uriURL }) {
+ // Test for scheme based filtering.
+ if (filter.schemes) {
+ // Return false if none of the schemes matches.
+ if (!filter.schemes.some(scheme => uri.schemeIs(scheme))) {
+ return false;
+ }
+ }
+
+ // Test for exact port matching or included in a range of ports.
+ if (filter.ports) {
+ let port = data.port;
+ if (port === -1) {
+ // NOTE: currently defaultPort for "resource" and "chrome" schemes defaults to -1,
+ // for "about", "data" and "javascript" schemes defaults to undefined.
+ if (["resource", "chrome"].includes(uri.scheme)) {
+ port = undefined;
+ } else {
+ port = Services.io.getDefaultPort(uri.scheme);
+ }
+ }
+
+ // Return false if none of the ports (or port ranges) is verified
+ const portMatch = filter.ports.some(filterPort => {
+ if (Array.isArray(filterPort)) {
+ let [lower, upper] = filterPort;
+ return port >= lower && port <= upper;
+ }
+
+ return port === filterPort;
+ });
+
+ if (!portMatch) {
+ return false;
+ }
+ }
+
+ // Filters on host, url, path, query:
+ // hostContains, hostEquals, hostSuffix, hostPrefix,
+ // urlContains, urlEquals, ...
+ for (let urlComponent of ["host", "path", "query", "url"]) {
+ if (!this.testMatchOnURLComponent({ urlComponent, data, filter })) {
+ return false;
+ }
+ }
+
+ // urlMatches is a regular expression string and it is tested for matches
+ // on the "url without the ref".
+ if (filter.urlMatches) {
+ let urlWithoutRef = uri.specIgnoringRef;
+ if (!urlWithoutRef.match(filter.urlMatches)) {
+ return false;
+ }
+ }
+
+ // originAndPathMatches is a regular expression string and it is tested for matches
+ // on the "url without the query and the ref".
+ if (filter.originAndPathMatches) {
+ let urlWithoutQueryAndRef = uri.resolve(uriURL.filePath);
+ // The above 'uri.resolve(...)' will be null for some URI schemes
+ // (e.g. about).
+ // TODO: handle schemes which will not be able to resolve the filePath
+ // (e.g. for "about:blank", 'urlWithoutQueryAndRef' should be "about:blank" instead
+ // of null)
+ if (
+ !urlWithoutQueryAndRef ||
+ !urlWithoutQueryAndRef.match(filter.originAndPathMatches)
+ ) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ testMatchOnURLComponent({ urlComponent: key, data, filter }) {
+ // Test for equals.
+ // NOTE: an empty string should not be considered a filter to skip.
+ if (filter[`${key}Equals`] != null) {
+ if (data[key] !== filter[`${key}Equals`]) {
+ return false;
+ }
+ }
+
+ // Test for contains.
+ if (filter[`${key}Contains`]) {
+ let value = (key == "host" ? "." : "") + data[key];
+ if (!data[key] || !value.includes(filter[`${key}Contains`])) {
+ return false;
+ }
+ }
+
+ // Test for prefix.
+ if (filter[`${key}Prefix`]) {
+ if (!data[key] || !data[key].startsWith(filter[`${key}Prefix`])) {
+ return false;
+ }
+ }
+
+ // Test for suffix.
+ if (filter[`${key}Suffix`]) {
+ if (!data[key] || !data[key].endsWith(filter[`${key}Suffix`])) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ serialize() {
+ return this.filters;
+ }
+}