mirror of
https://github.com/hensm/fx_cast.git
synced 2026-06-10 01:29:58 +00:00
Implement receiver selector whitelist suggestion banner
This commit is contained in:
136
ext/src/lib/matchPattern.ts
Normal file
136
ext/src/lib/matchPattern.ts
Normal file
@@ -0,0 +1,136 @@
|
||||
"use strict";
|
||||
|
||||
const WILDCARD_SCHEMES = ["http", "https", "ws", "wss"];
|
||||
|
||||
export const REMOTE_MATCH_PATTERN_REGEX =
|
||||
/^(?:(?:(\*|https?|wss?|ftp):\/\/(\*|(?:\*\.(?:[^\/\*:]\.?)+(?:[^\.])|[^\/\*:]*))?)(\/.*)|<all_urls>)$/;
|
||||
|
||||
/**
|
||||
* Partial implementation of WebExtension match patterns. Only handles
|
||||
* remote patterns, as we don't need local matching and it's more
|
||||
* complex to implement.
|
||||
*/
|
||||
export class RemoteMatchPattern {
|
||||
private partScheme: string;
|
||||
private partHost: string;
|
||||
private partPath: string;
|
||||
|
||||
/** Matching schemes */
|
||||
private schemes: string[] = [];
|
||||
|
||||
/** Base domain for subdomain matching */
|
||||
private domain?: string;
|
||||
/** Host part includes subdomain wildcard */
|
||||
private matchSubdomains = false;
|
||||
|
||||
constructor(public pattern: string) {
|
||||
// Parse match pattern parts
|
||||
const matches = pattern.match(REMOTE_MATCH_PATTERN_REGEX);
|
||||
if (!matches) {
|
||||
throw new Error("Invalid match pattern");
|
||||
}
|
||||
|
||||
[, this.partScheme, this.partHost, this.partPath] = matches;
|
||||
|
||||
if (pattern === "<all_urls>") {
|
||||
this.schemes = WILDCARD_SCHEMES;
|
||||
return;
|
||||
}
|
||||
|
||||
// Scheme
|
||||
this.schemes =
|
||||
this.partScheme === "*" ? WILDCARD_SCHEMES : [this.partScheme];
|
||||
|
||||
// Host
|
||||
if (this.partHost.startsWith("*.")) {
|
||||
this.domain = this.partHost.slice(2);
|
||||
this.matchSubdomains = true;
|
||||
} else if (this.partHost !== "*") {
|
||||
this.domain = this.partHost;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test domain string against match pattern.
|
||||
*/
|
||||
private matchesDomain(domain: string) {
|
||||
// If wildcard or exact match
|
||||
if (this.partHost === "*" || this.domain === domain) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.matchSubdomains) {
|
||||
// Should exist here
|
||||
if (!this.domain) return false;
|
||||
|
||||
// Starting offset of pattern in url host string
|
||||
const offset = domain.length - this.domain.length;
|
||||
if (
|
||||
offset > 0 &&
|
||||
domain[offset - 1] === "." &&
|
||||
domain.slice(offset) === this.domain
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests URL string against match pattern and returns boolean
|
||||
* result.
|
||||
*/
|
||||
matches(urlString: string) {
|
||||
let url: URL;
|
||||
try {
|
||||
url = new URL(urlString);
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If URL does not have a matching scheme
|
||||
if (!this.schemes.includes(url.protocol.slice(0, -1))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If pattern host is not a wildcard
|
||||
if (!this.matchesDomain(url.host)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const urlPath = `${url.pathname}${url.search}`;
|
||||
|
||||
// If pattern path is not a wildcard
|
||||
if (this.partPath !== "/*") {
|
||||
// And if paths don't match
|
||||
if (this.partPath !== urlPath) {
|
||||
const specialChars = ".+*?^${}()|[]\\";
|
||||
|
||||
/**
|
||||
* Create regular expression from pattern path, escaping
|
||||
* any special characters.
|
||||
*/
|
||||
let pathRegexString = "";
|
||||
for (const c of this.partPath) {
|
||||
if (c === "*") {
|
||||
pathRegexString += ".*";
|
||||
} else {
|
||||
if (specialChars.includes(c)) {
|
||||
pathRegexString += "\\";
|
||||
}
|
||||
|
||||
pathRegexString += c;
|
||||
}
|
||||
}
|
||||
|
||||
// Test compiled expression against path
|
||||
if (!new RegExp(`^${pathRegexString}$`).test(urlPath)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user