mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-10 10:18:38 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
352
toolkit/modules/addons/MatchPattern.jsm
Normal file
352
toolkit/modules/addons/MatchPattern.jsm
Normal file
|
|
@ -0,0 +1,352 @@
|
|||
/* 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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const Cu = Components.utils;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
|
||||
"resource://gre/modules/NetUtil.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Services",
|
||||
"resource://gre/modules/Services.jsm");
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["MatchPattern", "MatchGlobs", "MatchURLFilters"];
|
||||
|
||||
/* globals MatchPattern, MatchGlobs */
|
||||
|
||||
const PERMITTED_SCHEMES = ["http", "https", "file", "ftp", "data"];
|
||||
const PERMITTED_SCHEMES_REGEXP = PERMITTED_SCHEMES.join("|");
|
||||
|
||||
// This function converts a glob pattern (containing * and possibly ?
|
||||
// as wildcards) to a regular expression.
|
||||
function globToRegexp(pat, allowQuestion) {
|
||||
// Escape everything except ? and *.
|
||||
pat = pat.replace(/[.+^${}()|[\]\\]/g, "\\$&");
|
||||
|
||||
if (allowQuestion) {
|
||||
pat = pat.replace(/\?/g, ".");
|
||||
} else {
|
||||
pat = pat.replace(/\?/g, "\\?");
|
||||
}
|
||||
pat = pat.replace(/\*/g, ".*");
|
||||
return new RegExp("^" + pat + "$");
|
||||
}
|
||||
|
||||
// These patterns follow the syntax in
|
||||
// https://developer.chrome.com/extensions/match_patterns
|
||||
function SingleMatchPattern(pat) {
|
||||
if (pat == "<all_urls>") {
|
||||
this.schemes = PERMITTED_SCHEMES;
|
||||
this.hostMatch = () => true;
|
||||
this.pathMatch = () => true;
|
||||
} else if (!pat) {
|
||||
this.schemes = [];
|
||||
} else {
|
||||
let re = new RegExp(`^(${PERMITTED_SCHEMES_REGEXP}|\\*)://(\\*|\\*\\.[^*/]+|[^*/]+|)(/.*)$`);
|
||||
let match = re.exec(pat);
|
||||
if (!match) {
|
||||
Cu.reportError(`Invalid match pattern: '${pat}'`);
|
||||
this.schemes = [];
|
||||
return;
|
||||
}
|
||||
|
||||
if (match[1] == "*") {
|
||||
this.schemes = ["http", "https"];
|
||||
} else {
|
||||
this.schemes = [match[1]];
|
||||
}
|
||||
|
||||
// We allow the host to be empty for file URLs.
|
||||
if (match[2] == "" && this.schemes[0] != "file") {
|
||||
Cu.reportError(`Invalid match pattern: '${pat}'`);
|
||||
this.schemes = [];
|
||||
return;
|
||||
}
|
||||
|
||||
this.host = match[2];
|
||||
this.hostMatch = this.getHostMatcher(match[2]);
|
||||
|
||||
let pathMatch = globToRegexp(match[3], false);
|
||||
this.pathMatch = pathMatch.test.bind(pathMatch);
|
||||
}
|
||||
}
|
||||
|
||||
SingleMatchPattern.prototype = {
|
||||
getHostMatcher(host) {
|
||||
// This code ignores the port, as Chrome does.
|
||||
if (host == "*") {
|
||||
return () => true;
|
||||
}
|
||||
if (host.startsWith("*.")) {
|
||||
let suffix = host.substr(2);
|
||||
let dotSuffix = "." + suffix;
|
||||
|
||||
return ({host}) => host === suffix || host.endsWith(dotSuffix);
|
||||
}
|
||||
return uri => uri.host === host;
|
||||
},
|
||||
|
||||
matches(uri, ignorePath = false) {
|
||||
return (
|
||||
this.schemes.includes(uri.scheme) &&
|
||||
this.hostMatch(uri) &&
|
||||
(ignorePath || (
|
||||
this.pathMatch(uri.cloneIgnoringRef().path)
|
||||
))
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
this.MatchPattern = function(pat) {
|
||||
this.pat = pat;
|
||||
if (!pat) {
|
||||
this.matchers = [];
|
||||
} else if (pat instanceof String || typeof(pat) == "string") {
|
||||
this.matchers = [new SingleMatchPattern(pat)];
|
||||
} else {
|
||||
this.matchers = pat.map(p => new SingleMatchPattern(p));
|
||||
}
|
||||
};
|
||||
|
||||
MatchPattern.prototype = {
|
||||
// |uri| should be an nsIURI.
|
||||
matches(uri) {
|
||||
return this.matchers.some(matcher => matcher.matches(uri));
|
||||
},
|
||||
|
||||
matchesIgnoringPath(uri) {
|
||||
return this.matchers.some(matcher => matcher.matches(uri, true));
|
||||
},
|
||||
|
||||
// Checks that this match pattern grants access to read the given
|
||||
// cookie. |cookie| should be an |nsICookie2| instance.
|
||||
matchesCookie(cookie) {
|
||||
// First check for simple matches.
|
||||
let secureURI = NetUtil.newURI(`https://${cookie.rawHost}/`);
|
||||
if (this.matchesIgnoringPath(secureURI)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
let plainURI = NetUtil.newURI(`http://${cookie.rawHost}/`);
|
||||
if (!cookie.isSecure && this.matchesIgnoringPath(plainURI)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!cookie.isDomain) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Things get tricker for domain cookies. The extension needs to be able
|
||||
// to read any cookies that could be read any host it has permissions
|
||||
// for. This means that our normal host matching checks won't work,
|
||||
// since the pattern "*://*.foo.example.com/" doesn't match ".example.com",
|
||||
// but it does match "bar.foo.example.com", which can read cookies
|
||||
// with the domain ".example.com".
|
||||
//
|
||||
// So, instead, we need to manually check our filters, and accept any
|
||||
// with hosts that end with our cookie's host.
|
||||
|
||||
let {host, isSecure} = cookie;
|
||||
|
||||
for (let matcher of this.matchers) {
|
||||
let schemes = matcher.schemes;
|
||||
if (schemes.includes("https") || (!isSecure && schemes.includes("http"))) {
|
||||
if (matcher.host.endsWith(host)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
serialize() {
|
||||
return this.pat;
|
||||
},
|
||||
};
|
||||
|
||||
// Globs can match everything. Be careful, this DOES NOT filter by allowed schemes!
|
||||
this.MatchGlobs = function(globs) {
|
||||
this.original = globs;
|
||||
if (globs) {
|
||||
this.regexps = Array.from(globs, (glob) => globToRegexp(glob, true));
|
||||
} else {
|
||||
this.regexps = [];
|
||||
}
|
||||
};
|
||||
|
||||
MatchGlobs.prototype = {
|
||||
matches(str) {
|
||||
return this.regexps.some(regexp => regexp.test(str));
|
||||
},
|
||||
serialize() {
|
||||
return this.original;
|
||||
},
|
||||
};
|
||||
|
||||
// Match WebNavigation URL Filters.
|
||||
this.MatchURLFilters = function(filters) {
|
||||
if (!Array.isArray(filters)) {
|
||||
throw new TypeError("filters should be an array");
|
||||
}
|
||||
|
||||
if (filters.length == 0) {
|
||||
throw new Error("filters array should not be empty");
|
||||
}
|
||||
|
||||
this.filters = filters;
|
||||
};
|
||||
|
||||
MatchURLFilters.prototype = {
|
||||
matches(url) {
|
||||
let uri = NetUtil.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.getProtocolHandler(uri.scheme).defaultPort;
|
||||
}
|
||||
}
|
||||
|
||||
// Return false if none of the ports (or port ranges) is verified
|
||||
return filter.ports.some((filterPort) => {
|
||||
if (Array.isArray(filterPort)) {
|
||||
let [lower, upper] = filterPort;
|
||||
return port >= lower && port <= upper;
|
||||
}
|
||||
|
||||
return port === filterPort;
|
||||
});
|
||||
}
|
||||
|
||||
// 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;
|
||||
},
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue