summaryrefslogtreecommitdiff
path: root/data/extensions/uBlock0@raymondhill.net/js/filtering-context.js
diff options
context:
space:
mode:
Diffstat (limited to 'data/extensions/uBlock0@raymondhill.net/js/filtering-context.js')
-rw-r--r--data/extensions/uBlock0@raymondhill.net/js/filtering-context.js514
1 files changed, 514 insertions, 0 deletions
diff --git a/data/extensions/uBlock0@raymondhill.net/js/filtering-context.js b/data/extensions/uBlock0@raymondhill.net/js/filtering-context.js
new file mode 100644
index 0000000..a24cdab
--- /dev/null
+++ b/data/extensions/uBlock0@raymondhill.net/js/filtering-context.js
@@ -0,0 +1,514 @@
+/*******************************************************************************
+
+ uBlock Origin - a comprehensive, efficient content blocker
+ Copyright (C) 2018-present Raymond Hill
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see {http://www.gnu.org/licenses/}.
+
+ Home: https://github.com/gorhill/uBlock
+*/
+
+import {
+ domainFromHostname,
+ hostnameFromURI,
+ originFromURI,
+} from './uri-utils.js';
+
+/******************************************************************************/
+
+// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/webRequest/ResourceType
+
+// Long term, convert code wherever possible to work with integer-based type
+// values -- the assumption being that integer operations are faster than
+// string operations.
+
+export const NO_TYPE = 0;
+export const BEACON = 1 << 0;
+export const CSP_REPORT = 1 << 1;
+export const FONT = 1 << 2;
+export const IMAGE = 1 << 4;
+export const IMAGESET = 1 << 4;
+export const MAIN_FRAME = 1 << 5;
+export const MEDIA = 1 << 6;
+export const OBJECT = 1 << 7;
+export const OBJECT_SUBREQUEST = 1 << 7;
+export const PING = 1 << 8;
+export const SCRIPT = 1 << 9;
+export const STYLESHEET = 1 << 10;
+export const SUB_FRAME = 1 << 11;
+export const WEBSOCKET = 1 << 12;
+export const XMLHTTPREQUEST = 1 << 13;
+export const INLINE_FONT = 1 << 14;
+export const INLINE_SCRIPT = 1 << 15;
+export const OTHER = 1 << 16;
+export const FRAME_ANY = MAIN_FRAME | SUB_FRAME | OBJECT;
+export const FONT_ANY = FONT | INLINE_FONT;
+export const INLINE_ANY = INLINE_FONT | INLINE_SCRIPT;
+export const PING_ANY = BEACON | CSP_REPORT | PING;
+export const SCRIPT_ANY = SCRIPT | INLINE_SCRIPT;
+
+const typeStrToIntMap = {
+ 'no_type': NO_TYPE,
+ 'beacon': BEACON,
+ 'csp_report': CSP_REPORT,
+ 'font': FONT,
+ 'image': IMAGE,
+ 'imageset': IMAGESET,
+ 'main_frame': MAIN_FRAME,
+ 'media': MEDIA,
+ 'object': OBJECT,
+ 'object_subrequest': OBJECT_SUBREQUEST,
+ 'ping': PING,
+ 'script': SCRIPT,
+ 'stylesheet': STYLESHEET,
+ 'sub_frame': SUB_FRAME,
+ 'websocket': WEBSOCKET,
+ 'xmlhttprequest': XMLHTTPREQUEST,
+ 'inline-font': INLINE_FONT,
+ 'inline-script': INLINE_SCRIPT,
+ 'other': OTHER,
+};
+
+export const METHOD_NONE = 0;
+export const METHOD_CONNECT = 1 << 1;
+export const METHOD_DELETE = 1 << 2;
+export const METHOD_GET = 1 << 3;
+export const METHOD_HEAD = 1 << 4;
+export const METHOD_OPTIONS = 1 << 5;
+export const METHOD_PATCH = 1 << 6;
+export const METHOD_POST = 1 << 7;
+export const METHOD_PUT = 1 << 8;
+
+const methodStrToBitMap = {
+ '': METHOD_NONE,
+ 'connect': METHOD_CONNECT,
+ 'delete': METHOD_DELETE,
+ 'get': METHOD_GET,
+ 'head': METHOD_HEAD,
+ 'options': METHOD_OPTIONS,
+ 'patch': METHOD_PATCH,
+ 'post': METHOD_POST,
+ 'put': METHOD_PUT,
+ 'CONNECT': METHOD_CONNECT,
+ 'DELETE': METHOD_DELETE,
+ 'GET': METHOD_GET,
+ 'HEAD': METHOD_HEAD,
+ 'OPTIONS': METHOD_OPTIONS,
+ 'PATCH': METHOD_PATCH,
+ 'POST': METHOD_POST,
+ 'PUT': METHOD_PUT,
+};
+
+const methodBitToStrMap = new Map([
+ [ METHOD_NONE, '' ],
+ [ METHOD_CONNECT, 'connect' ],
+ [ METHOD_DELETE, 'delete' ],
+ [ METHOD_GET, 'get' ],
+ [ METHOD_HEAD, 'head' ],
+ [ METHOD_OPTIONS, 'options' ],
+ [ METHOD_PATCH, 'patch' ],
+ [ METHOD_POST, 'post' ],
+ [ METHOD_PUT, 'put' ],
+]);
+
+const reIPv4 = /^\d+\.\d+\.\d+\.\d+$/;
+
+/******************************************************************************/
+
+export const FilteringContext = class {
+ constructor(other) {
+ if ( other instanceof FilteringContext ) {
+ return this.fromFilteringContext(other);
+ }
+ this.tstamp = 0;
+ this.realm = '';
+ this.method = 0;
+ this.itype = NO_TYPE;
+ this.stype = undefined;
+ this.url = undefined;
+ this.aliasURL = undefined;
+ this.hostname = undefined;
+ this.domain = undefined;
+ this.ipaddress = undefined;
+ this.docId = -1;
+ this.frameId = -1;
+ this.docOrigin = undefined;
+ this.docHostname = undefined;
+ this.docDomain = undefined;
+ this.tabId = undefined;
+ this.tabOrigin = undefined;
+ this.tabHostname = undefined;
+ this.tabDomain = undefined;
+ this.redirectURL = undefined;
+ this.filter = undefined;
+ }
+
+ get type() {
+ return this.stype;
+ }
+
+ set type(a) {
+ this.itype = typeStrToIntMap[a] || NO_TYPE;
+ this.stype = a;
+ }
+
+ isRootDocument() {
+ return (this.itype & MAIN_FRAME) !== 0;
+ }
+ isDocument() {
+ return (this.itype & FRAME_ANY) !== 0;
+ }
+
+ isFont() {
+ return (this.itype & FONT_ANY) !== 0;
+ }
+
+ fromFilteringContext(other) {
+ this.realm = other.realm;
+ this.type = other.type;
+ this.method = other.method;
+ this.url = other.url;
+ this.hostname = other.hostname;
+ this.domain = other.domain;
+ this.ipaddress = other.ipaddress;
+ this.docId = other.docId;
+ this.frameId = other.frameId;
+ this.docOrigin = other.docOrigin;
+ this.docHostname = other.docHostname;
+ this.docDomain = other.docDomain;
+ this.tabId = other.tabId;
+ this.tabOrigin = other.tabOrigin;
+ this.tabHostname = other.tabHostname;
+ this.tabDomain = other.tabDomain;
+ this.redirectURL = other.redirectURL;
+ this.filter = undefined;
+ return this;
+ }
+
+ fromDetails({ originURL, url, type }) {
+ this.setDocOriginFromURL(originURL)
+ .setURL(url)
+ .setType(type);
+ return this;
+ }
+
+ duplicate() {
+ return (new FilteringContext(this));
+ }
+
+ setRealm(a) {
+ this.realm = a;
+ return this;
+ }
+
+ setType(a) {
+ this.type = a;
+ return this;
+ }
+
+ setURL(a) {
+ if ( a !== this.url ) {
+ this.hostname = this.domain = this.ipaddress = undefined;
+ this.url = a;
+ }
+ return this;
+ }
+
+ getHostname() {
+ if ( this.hostname === undefined ) {
+ this.hostname = hostnameFromURI(this.url);
+ }
+ return this.hostname;
+ }
+
+ setHostname(a) {
+ if ( a !== this.hostname ) {
+ this.domain = undefined;
+ this.hostname = a;
+ }
+ return this;
+ }
+
+ getDomain() {
+ if ( this.domain === undefined ) {
+ this.domain = domainFromHostname(this.getHostname());
+ }
+ return this.domain;
+ }
+
+ setDomain(a) {
+ this.domain = a;
+ return this;
+ }
+
+ getIPAddress() {
+ if ( this.ipaddress !== undefined ) {
+ return this.ipaddress;
+ }
+ const ipaddr = this.getHostname();
+ const c0 = ipaddr.charCodeAt(0);
+ if ( c0 === 0x5B /* [ */ ) {
+ return (this.ipaddress = ipaddr.slice(1, -1));
+ } else if ( c0 <= 0x39 && c0 >= 0x30 ) {
+ if ( reIPv4.test(ipaddr) ) {
+ return (this.ipaddress = ipaddr);
+ }
+ }
+ return (this.ipaddress = '');
+ }
+
+ // Must always be called *after* setURL()
+ setIPAddress(ipaddr) {
+ this.ipaddress = ipaddr || undefined;
+ return this;
+ }
+
+ getDocOrigin() {
+ if ( this.docOrigin === undefined ) {
+ this.docOrigin = this.tabOrigin;
+ }
+ return this.docOrigin;
+ }
+
+ setDocOrigin(a) {
+ if ( a !== this.docOrigin ) {
+ this.docHostname = this.docDomain = undefined;
+ this.docOrigin = a;
+ }
+ return this;
+ }
+
+ setDocOriginFromURL(a) {
+ return this.setDocOrigin(originFromURI(a));
+ }
+
+ getDocHostname() {
+ if ( this.docHostname === undefined ) {
+ this.docHostname = hostnameFromURI(this.getDocOrigin());
+ }
+ return this.docHostname;
+ }
+
+ setDocHostname(a) {
+ if ( a !== this.docHostname ) {
+ this.docDomain = undefined;
+ this.docHostname = a;
+ }
+ return this;
+ }
+
+ getDocDomain() {
+ if ( this.docDomain === undefined ) {
+ this.docDomain = domainFromHostname(this.getDocHostname());
+ }
+ return this.docDomain;
+ }
+
+ setDocDomain(a) {
+ this.docDomain = a;
+ return this;
+ }
+
+ // The idea is to minimize the amount of work done to figure out whether
+ // the resource is 3rd-party to the document.
+ is3rdPartyToDoc() {
+ let docDomain = this.getDocDomain();
+ if ( docDomain === '' ) { docDomain = this.docHostname; }
+ if ( this.domain !== undefined && this.domain !== '' ) {
+ return this.domain !== docDomain;
+ }
+ const hostname = this.getHostname();
+ if ( hostname.endsWith(docDomain) === false ) { return true; }
+ const i = hostname.length - docDomain.length;
+ if ( i === 0 ) { return false; }
+ return hostname.charCodeAt(i - 1) !== 0x2E /* '.' */;
+ }
+
+ setTabId(a) {
+ this.tabId = a;
+ return this;
+ }
+
+ getTabOrigin() {
+ return this.tabOrigin;
+ }
+
+ setTabOrigin(a) {
+ if ( a !== this.tabOrigin ) {
+ this.tabHostname = this.tabDomain = undefined;
+ this.tabOrigin = a;
+ }
+ return this;
+ }
+
+ setTabOriginFromURL(a) {
+ return this.setTabOrigin(originFromURI(a));
+ }
+
+ getTabHostname() {
+ if ( this.tabHostname === undefined ) {
+ this.tabHostname = hostnameFromURI(this.getTabOrigin());
+ }
+ return this.tabHostname;
+ }
+
+ setTabHostname(a) {
+ if ( a !== this.tabHostname ) {
+ this.tabDomain = undefined;
+ this.tabHostname = a;
+ }
+ return this;
+ }
+
+ getTabDomain() {
+ if ( this.tabDomain === undefined ) {
+ this.tabDomain = domainFromHostname(this.getTabHostname());
+ }
+ return this.tabDomain;
+ }
+
+ setTabDomain(a) {
+ this.docDomain = a;
+ return this;
+ }
+
+ // The idea is to minimize the amount of work done to figure out whether
+ // the resource is 3rd-party to the top document.
+ is3rdPartyToTab() {
+ let tabDomain = this.getTabDomain();
+ if ( tabDomain === '' ) { tabDomain = this.tabHostname; }
+ if ( this.domain !== undefined && this.domain !== '' ) {
+ return this.domain !== tabDomain;
+ }
+ const hostname = this.getHostname();
+ if ( hostname.endsWith(tabDomain) === false ) { return true; }
+ const i = hostname.length - tabDomain.length;
+ if ( i === 0 ) { return false; }
+ return hostname.charCodeAt(i - 1) !== 0x2E /* '.' */;
+ }
+
+ setFilter(a) {
+ this.filter = a;
+ return this;
+ }
+
+ pushFilter(a) {
+ if ( this.filter === undefined ) {
+ return this.setFilter(a);
+ }
+ if ( Array.isArray(this.filter) ) {
+ this.filter.push(a);
+ } else {
+ this.filter = [ this.filter, a ];
+ }
+ return this;
+ }
+
+ pushFilters(a) {
+ if ( this.filter === undefined ) {
+ return this.setFilter(a);
+ }
+ if ( Array.isArray(this.filter) ) {
+ this.filter.push(...a);
+ } else {
+ this.filter = [ this.filter, ...a ];
+ }
+ return this;
+ }
+
+ setMethod(a) {
+ this.method = methodStrToBitMap[a] || 0;
+ return this;
+ }
+
+ getMethodName() {
+ return FilteringContext.getMethodName(this.method);
+ }
+
+ static getMethod(a) {
+ return methodStrToBitMap[a] || 0;
+ }
+
+ static getMethodName(a) {
+ return methodBitToStrMap.get(a) || '';
+ }
+
+ BEACON = BEACON;
+ CSP_REPORT = CSP_REPORT;
+ FONT = FONT;
+ IMAGE = IMAGE;
+ IMAGESET = IMAGESET;
+ MAIN_FRAME = MAIN_FRAME;
+ MEDIA = MEDIA;
+ OBJECT = OBJECT;
+ OBJECT_SUBREQUEST = OBJECT_SUBREQUEST;
+ PING = PING;
+ SCRIPT = SCRIPT;
+ STYLESHEET = STYLESHEET;
+ SUB_FRAME = SUB_FRAME;
+ WEBSOCKET = WEBSOCKET;
+ XMLHTTPREQUEST = XMLHTTPREQUEST;
+ INLINE_FONT = INLINE_FONT;
+ INLINE_SCRIPT = INLINE_SCRIPT;
+ OTHER = OTHER;
+ FRAME_ANY = FRAME_ANY;
+ FONT_ANY = FONT_ANY;
+ INLINE_ANY = INLINE_ANY;
+ PING_ANY = PING_ANY;
+ SCRIPT_ANY = SCRIPT_ANY;
+ METHOD_NONE = METHOD_NONE;
+ METHOD_CONNECT = METHOD_CONNECT;
+ METHOD_DELETE = METHOD_DELETE;
+ METHOD_GET = METHOD_GET;
+ METHOD_HEAD = METHOD_HEAD;
+ METHOD_OPTIONS = METHOD_OPTIONS;
+ METHOD_PATCH = METHOD_PATCH;
+ METHOD_POST = METHOD_POST;
+ METHOD_PUT = METHOD_PUT;
+
+ static BEACON = BEACON;
+ static CSP_REPORT = CSP_REPORT;
+ static FONT = FONT;
+ static IMAGE = IMAGE;
+ static IMAGESET = IMAGESET;
+ static MAIN_FRAME = MAIN_FRAME;
+ static MEDIA = MEDIA;
+ static OBJECT = OBJECT;
+ static OBJECT_SUBREQUEST = OBJECT_SUBREQUEST;
+ static PING = PING;
+ static SCRIPT = SCRIPT;
+ static STYLESHEET = STYLESHEET;
+ static SUB_FRAME = SUB_FRAME;
+ static WEBSOCKET = WEBSOCKET;
+ static XMLHTTPREQUEST = XMLHTTPREQUEST;
+ static INLINE_FONT = INLINE_FONT;
+ static INLINE_SCRIPT = INLINE_SCRIPT;
+ static OTHER = OTHER;
+ static FRAME_ANY = FRAME_ANY;
+ static FONT_ANY = FONT_ANY;
+ static INLINE_ANY = INLINE_ANY;
+ static PING_ANY = PING_ANY;
+ static SCRIPT_ANY = SCRIPT_ANY;
+ static METHOD_NONE = METHOD_NONE;
+ static METHOD_CONNECT = METHOD_CONNECT;
+ static METHOD_DELETE = METHOD_DELETE;
+ static METHOD_GET = METHOD_GET;
+ static METHOD_HEAD = METHOD_HEAD;
+ static METHOD_OPTIONS = METHOD_OPTIONS;
+ static METHOD_PATCH = METHOD_PATCH;
+ static METHOD_POST = METHOD_POST;
+ static METHOD_PUT = METHOD_PUT;
+};
+
+/******************************************************************************/