mirror of
https://github.com/gorhill/uBlock.git
synced 2024-10-04 16:47:15 +02:00
Add new filter option queryprune=
Related issue: - https://github.com/uBlockOrigin/uBlock-issues/issues/760 The purpose of this new network filter option is to remove query parameters form the URL of network requests. The name `queryprune` has been picked over `querystrip` since the purpose of the option is to remove some parameters from the URL rather than all parameters. `queryprune` is a modifier option (like `csp`) in that it does not cause a network request to be blocked but rather modified before being emitted. `queryprune` must be assigned a value, which value will determine which parameters from a query string will be removed. The syntax for the value is that of regular expression *except* for the following rules: - do not wrap the regex directive between `/` - do not use regex special values `^` and `$` - do not use literal comma character in the value, though you can use hex-encoded version, `\x2c` - to match the start of a query parameter, prepend `|` - to match the end of a query parameter, append `|` `queryprune` regex-like values will be tested against each key-value parameter pair as `[key]=[value]` string. This way you can prune according to either the key, the value, or both. This commit introduces the concept of modifier filter options, which as of now are: - `csp=` - `queryprune=` They both work in similar way when used with `important` option or when used in exception filters. Modifier options can apply to any network requests, hence the logger reports the type of the network requests, and no longer use the modifier as the type, i.e. `csp` filters are no longer reported as requests of type `csp`. Though modifier options can apply to any network requests, for the time being the `csp=` modifier option still apply only to top or embedded (frame) documents, just as before. In some future we may want to apply `csp=` directives to network requests of type script, to control the behavior of service workers for example. A new built-in filter expression has been added to the logger: "modified", which allow to see all the network requests which were modified before being emitted. The translation work for this new option will be available in a future commit.
This commit is contained in:
parent
ba2ef925e9
commit
1e2eb037e5
@ -1145,7 +1145,7 @@ vAPI.warSecret = (( ) => {
|
|||||||
url.lastIndexOf(`?secret=${secret}`) !== -1
|
url.lastIndexOf(`?secret=${secret}`) !== -1
|
||||||
);
|
);
|
||||||
if ( pos === -1 ) {
|
if ( pos === -1 ) {
|
||||||
return { redirectUrl: root };
|
return { cancel: true };
|
||||||
}
|
}
|
||||||
secrets.splice(pos, 1);
|
secrets.splice(pos, 1);
|
||||||
};
|
};
|
||||||
|
@ -49,7 +49,9 @@
|
|||||||
vAPI.messaging.send('dashboard', {
|
vAPI.messaging.send('dashboard', {
|
||||||
what: 'sfneBenchmark',
|
what: 'sfneBenchmark',
|
||||||
}).then(result => {
|
}).then(result => {
|
||||||
document.getElementById('sfneBenchmarkResult').textContent = result;
|
document.getElementById('sfneBenchmarkResult').prepend(
|
||||||
|
document.createTextNode(result.trim() + '\n')
|
||||||
|
);
|
||||||
button.removeAttribute('disabled');
|
button.removeAttribute('disabled');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -139,8 +139,8 @@ const µBlock = (( ) => { // jshint ignore:line
|
|||||||
|
|
||||||
// Read-only
|
// Read-only
|
||||||
systemSettings: {
|
systemSettings: {
|
||||||
compiledMagic: 29, // Increase when compiled format changes
|
compiledMagic: 30, // Increase when compiled format changes
|
||||||
selfieMagic: 29, // Increase when selfie format changes
|
selfieMagic: 30, // Increase when selfie format changes
|
||||||
},
|
},
|
||||||
|
|
||||||
// https://github.com/uBlockOrigin/uBlock-issues/issues/759#issuecomment-546654501
|
// https://github.com/uBlockOrigin/uBlock-issues/issues/759#issuecomment-546654501
|
||||||
|
@ -420,7 +420,7 @@ const initHints = function() {
|
|||||||
if ( assignPos !== -1 ) { seedRight = seedRight.slice(0, assignPos); }
|
if ( assignPos !== -1 ) { seedRight = seedRight.slice(0, assignPos); }
|
||||||
const isException = parser.isException();
|
const isException = parser.isException();
|
||||||
const hints = [];
|
const hints = [];
|
||||||
for ( let [ text, bits ] of parser.netOptionTokens ) {
|
for ( let [ text, bits ] of parser.netOptionTokenDescriptors ) {
|
||||||
if ( isNegated && (bits & parser.OPTCanNegate) === 0 ) { continue; }
|
if ( isNegated && (bits & parser.OPTCanNegate) === 0 ) { continue; }
|
||||||
if ( isException ) {
|
if ( isException ) {
|
||||||
if ( (bits & parser.OPTBlockOnly) !== 0 ) { continue; }
|
if ( (bits & parser.OPTBlockOnly) !== 0 ) { continue; }
|
||||||
|
@ -23,55 +23,140 @@
|
|||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
µBlock.FilteringContext = function(other) {
|
{
|
||||||
if ( other instanceof µBlock.FilteringContext ) {
|
// >>>>> start of local scope
|
||||||
return this.fromFilteringContext(other);
|
|
||||||
}
|
/******************************************************************************/
|
||||||
this.tstamp = 0;
|
|
||||||
this.realm = '';
|
const originFromURI = µBlock.URI.originFromURI;
|
||||||
this.id = undefined;
|
const hostnameFromURI = vAPI.hostnameFromURI;
|
||||||
this.type = undefined;
|
const domainFromHostname = vAPI.domainFromHostname;
|
||||||
this.url = undefined;
|
|
||||||
this.aliasURL = undefined;
|
/******************************************************************************/
|
||||||
this.hostname = undefined;
|
|
||||||
this.domain = undefined;
|
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/webRequest/ResourceType
|
||||||
this.docId = -1;
|
|
||||||
this.frameId = -1;
|
// Long term, convert code wherever possible to work with integer-based type
|
||||||
this.docOrigin = undefined;
|
// values -- the assumption being that integer operations are faster than
|
||||||
this.docHostname = undefined;
|
// string operations.
|
||||||
this.docDomain = undefined;
|
|
||||||
this.tabId = undefined;
|
const NO_TYPE = 0;
|
||||||
this.tabOrigin = undefined;
|
const BEACON = 1 << 0;
|
||||||
this.tabHostname = undefined;
|
const CSP_REPORT = 1 << 1;
|
||||||
this.tabDomain = undefined;
|
const FONT = 1 << 2;
|
||||||
this.redirectURL = undefined;
|
const IMAGE = 1 << 4;
|
||||||
this.filter = undefined;
|
const IMAGESET = 1 << 4;
|
||||||
|
const MAIN_FRAME = 1 << 5;
|
||||||
|
const MEDIA = 1 << 6;
|
||||||
|
const OBJECT = 1 << 7;
|
||||||
|
const OBJECT_SUBREQUEST = 1 << 7;
|
||||||
|
const PING = 1 << 8;
|
||||||
|
const SCRIPT = 1 << 9;
|
||||||
|
const STYLESHEET = 1 << 10;
|
||||||
|
const SUB_FRAME = 1 << 11;
|
||||||
|
const WEBSOCKET = 1 << 12;
|
||||||
|
const XMLHTTPREQUEST = 1 << 13;
|
||||||
|
const INLINE_FONT = 1 << 14;
|
||||||
|
const INLINE_SCRIPT = 1 << 15;
|
||||||
|
const OTHER = 1 << 16;
|
||||||
|
const FRAME_ANY = MAIN_FRAME | SUB_FRAME;
|
||||||
|
const FONT_ANY = FONT | INLINE_FONT;
|
||||||
|
const INLINE_ANY = INLINE_FONT | INLINE_SCRIPT;
|
||||||
|
const PING_ANY = BEACON | CSP_REPORT | PING;
|
||||||
|
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,
|
||||||
};
|
};
|
||||||
|
|
||||||
µBlock.FilteringContext.prototype = {
|
/******************************************************************************/
|
||||||
fromTabId: function(tabId) {
|
|
||||||
|
const FilteringContext = class {
|
||||||
|
constructor(other) {
|
||||||
|
if ( other instanceof FilteringContext ) {
|
||||||
|
return this.fromFilteringContext(other);
|
||||||
|
}
|
||||||
|
this.tstamp = 0;
|
||||||
|
this.realm = '';
|
||||||
|
this.id = undefined;
|
||||||
|
this.itype = 0;
|
||||||
|
this.stype = undefined;
|
||||||
|
this.url = undefined;
|
||||||
|
this.aliasURL = undefined;
|
||||||
|
this.hostname = undefined;
|
||||||
|
this.domain = 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
isDocument() {
|
||||||
|
return (this.itype & FRAME_ANY) !== 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
isFont() {
|
||||||
|
return (this.itype & FONT_ANY) !== 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fromTabId(tabId) {
|
||||||
const tabContext = µBlock.tabContextManager.mustLookup(tabId);
|
const tabContext = µBlock.tabContextManager.mustLookup(tabId);
|
||||||
this.tabOrigin = tabContext.origin;
|
this.tabOrigin = tabContext.origin;
|
||||||
this.tabHostname = tabContext.rootHostname;
|
this.tabHostname = tabContext.rootHostname;
|
||||||
this.tabDomain = tabContext.rootDomain;
|
this.tabDomain = tabContext.rootDomain;
|
||||||
this.tabId = tabContext.tabId;
|
this.tabId = tabContext.tabId;
|
||||||
return this;
|
return this;
|
||||||
},
|
}
|
||||||
|
|
||||||
// https://github.com/uBlockOrigin/uBlock-issues/issues/459
|
// https://github.com/uBlockOrigin/uBlock-issues/issues/459
|
||||||
// In case of a request for frame and if ever no context is specified,
|
// In case of a request for frame and if ever no context is specified,
|
||||||
// assume the origin of the context is the same as the request itself.
|
// assume the origin of the context is the same as the request itself.
|
||||||
fromWebrequestDetails: function(details) {
|
fromWebrequestDetails(details) {
|
||||||
const tabId = details.tabId;
|
const tabId = details.tabId;
|
||||||
if ( tabId > 0 && details.type === 'main_frame' ) {
|
this.fromTabId(tabId);
|
||||||
|
this.type = details.type;
|
||||||
|
if ( this.itype === MAIN_FRAME && tabId > 0 ) {
|
||||||
µBlock.tabContextManager.push(tabId, details.url);
|
µBlock.tabContextManager.push(tabId, details.url);
|
||||||
}
|
}
|
||||||
this.fromTabId(tabId);
|
|
||||||
this.realm = '';
|
this.realm = '';
|
||||||
this.id = details.requestId;
|
this.id = details.requestId;
|
||||||
this.type = details.type;
|
|
||||||
this.setURL(details.url);
|
this.setURL(details.url);
|
||||||
this.aliasURL = details.aliasURL || undefined;
|
this.aliasURL = details.aliasURL || undefined;
|
||||||
if ( details.type !== 'sub_frame' ) {
|
if ( this.itype !== SUB_FRAME ) {
|
||||||
this.docId = details.frameId;
|
this.docId = details.frameId;
|
||||||
this.frameId = -1;
|
this.frameId = -1;
|
||||||
} else {
|
} else {
|
||||||
@ -95,12 +180,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if ( details.documentUrl !== undefined ) {
|
} else if ( details.documentUrl !== undefined ) {
|
||||||
const origin = this.originFromURI(
|
const origin = originFromURI(
|
||||||
µBlock.normalizePageURL(0, details.documentUrl)
|
µBlock.normalizePageURL(0, details.documentUrl)
|
||||||
);
|
);
|
||||||
this.setDocOrigin(origin).setTabOrigin(origin);
|
this.setDocOrigin(origin).setTabOrigin(origin);
|
||||||
} else if ( this.docId === -1 || this.type.endsWith('_frame') ) {
|
} else if ( this.docId === -1 || (this.itype & FRAME_ANY) !== 0 ) {
|
||||||
const origin = this.originFromURI(this.url);
|
const origin = originFromURI(this.url);
|
||||||
this.setDocOrigin(origin).setTabOrigin(origin);
|
this.setDocOrigin(origin).setTabOrigin(origin);
|
||||||
} else {
|
} else {
|
||||||
this.setDocOrigin(this.tabOrigin);
|
this.setDocOrigin(this.tabOrigin);
|
||||||
@ -108,8 +193,9 @@
|
|||||||
this.redirectURL = undefined;
|
this.redirectURL = undefined;
|
||||||
this.filter = undefined;
|
this.filter = undefined;
|
||||||
return this;
|
return this;
|
||||||
},
|
}
|
||||||
fromFilteringContext: function(other) {
|
|
||||||
|
fromFilteringContext(other) {
|
||||||
this.realm = other.realm;
|
this.realm = other.realm;
|
||||||
this.type = other.type;
|
this.type = other.type;
|
||||||
this.url = other.url;
|
this.url = other.url;
|
||||||
@ -127,90 +213,106 @@
|
|||||||
this.redirectURL = other.redirectURL;
|
this.redirectURL = other.redirectURL;
|
||||||
this.filter = undefined;
|
this.filter = undefined;
|
||||||
return this;
|
return this;
|
||||||
},
|
}
|
||||||
duplicate: function() {
|
|
||||||
return (new µBlock.FilteringContext(this));
|
duplicate() {
|
||||||
},
|
return (new FilteringContext(this));
|
||||||
setRealm: function(a) {
|
}
|
||||||
|
|
||||||
|
setRealm(a) {
|
||||||
this.realm = a;
|
this.realm = a;
|
||||||
return this;
|
return this;
|
||||||
},
|
}
|
||||||
setType: function(a) {
|
|
||||||
|
setType(a) {
|
||||||
this.type = a;
|
this.type = a;
|
||||||
return this;
|
return this;
|
||||||
},
|
}
|
||||||
setURL: function(a) {
|
|
||||||
|
setURL(a) {
|
||||||
if ( a !== this.url ) {
|
if ( a !== this.url ) {
|
||||||
this.hostname = this.domain = undefined;
|
this.hostname = this.domain = undefined;
|
||||||
this.url = a;
|
this.url = a;
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
},
|
}
|
||||||
getHostname: function() {
|
|
||||||
|
getHostname() {
|
||||||
if ( this.hostname === undefined ) {
|
if ( this.hostname === undefined ) {
|
||||||
this.hostname = this.hostnameFromURI(this.url);
|
this.hostname = hostnameFromURI(this.url);
|
||||||
}
|
}
|
||||||
return this.hostname;
|
return this.hostname;
|
||||||
},
|
}
|
||||||
setHostname: function(a) {
|
|
||||||
|
setHostname(a) {
|
||||||
if ( a !== this.hostname ) {
|
if ( a !== this.hostname ) {
|
||||||
this.domain = undefined;
|
this.domain = undefined;
|
||||||
this.hostname = a;
|
this.hostname = a;
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
},
|
}
|
||||||
getDomain: function() {
|
|
||||||
|
getDomain() {
|
||||||
if ( this.domain === undefined ) {
|
if ( this.domain === undefined ) {
|
||||||
this.domain = this.domainFromHostname(this.getHostname());
|
this.domain = domainFromHostname(this.getHostname());
|
||||||
}
|
}
|
||||||
return this.domain;
|
return this.domain;
|
||||||
},
|
}
|
||||||
setDomain: function(a) {
|
|
||||||
|
setDomain(a) {
|
||||||
this.domain = a;
|
this.domain = a;
|
||||||
return this;
|
return this;
|
||||||
},
|
}
|
||||||
getDocOrigin: function() {
|
|
||||||
|
getDocOrigin() {
|
||||||
if ( this.docOrigin === undefined ) {
|
if ( this.docOrigin === undefined ) {
|
||||||
this.docOrigin = this.tabOrigin;
|
this.docOrigin = this.tabOrigin;
|
||||||
}
|
}
|
||||||
return this.docOrigin;
|
return this.docOrigin;
|
||||||
},
|
}
|
||||||
setDocOrigin: function(a) {
|
|
||||||
|
setDocOrigin(a) {
|
||||||
if ( a !== this.docOrigin ) {
|
if ( a !== this.docOrigin ) {
|
||||||
this.docHostname = this.docDomain = undefined;
|
this.docHostname = this.docDomain = undefined;
|
||||||
this.docOrigin = a;
|
this.docOrigin = a;
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
},
|
}
|
||||||
setDocOriginFromURL: function(a) {
|
|
||||||
return this.setDocOrigin(this.originFromURI(a));
|
setDocOriginFromURL(a) {
|
||||||
},
|
return this.setDocOrigin(originFromURI(a));
|
||||||
getDocHostname: function() {
|
}
|
||||||
|
|
||||||
|
getDocHostname() {
|
||||||
if ( this.docHostname === undefined ) {
|
if ( this.docHostname === undefined ) {
|
||||||
this.docHostname = this.hostnameFromURI(this.getDocOrigin());
|
this.docHostname = hostnameFromURI(this.getDocOrigin());
|
||||||
}
|
}
|
||||||
return this.docHostname;
|
return this.docHostname;
|
||||||
},
|
}
|
||||||
setDocHostname: function(a) {
|
|
||||||
|
setDocHostname(a) {
|
||||||
if ( a !== this.docHostname ) {
|
if ( a !== this.docHostname ) {
|
||||||
this.docDomain = undefined;
|
this.docDomain = undefined;
|
||||||
this.docHostname = a;
|
this.docHostname = a;
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
},
|
}
|
||||||
getDocDomain: function() {
|
|
||||||
|
getDocDomain() {
|
||||||
if ( this.docDomain === undefined ) {
|
if ( this.docDomain === undefined ) {
|
||||||
this.docDomain = this.domainFromHostname(this.getDocHostname());
|
this.docDomain = domainFromHostname(this.getDocHostname());
|
||||||
}
|
}
|
||||||
return this.docDomain;
|
return this.docDomain;
|
||||||
},
|
}
|
||||||
setDocDomain: function(a) {
|
|
||||||
|
setDocDomain(a) {
|
||||||
this.docDomain = a;
|
this.docDomain = a;
|
||||||
return this;
|
return this;
|
||||||
},
|
}
|
||||||
|
|
||||||
// The idea is to minimize the amout of work done to figure out whether
|
// The idea is to minimize the amout of work done to figure out whether
|
||||||
// the resource is 3rd-party to the document.
|
// the resource is 3rd-party to the document.
|
||||||
is3rdPartyToDoc: function() {
|
is3rdPartyToDoc() {
|
||||||
let docDomain = this.getDocDomain();
|
let docDomain = this.getDocDomain();
|
||||||
if ( docDomain === '' ) { docDomain = this.docHostname; }
|
if ( docDomain === '' ) { docDomain = this.docHostname; }
|
||||||
if ( this.domain !== undefined && this.domain !== '' ) {
|
if ( this.domain !== undefined && this.domain !== '' ) {
|
||||||
@ -221,12 +323,14 @@
|
|||||||
const i = hostname.length - docDomain.length;
|
const i = hostname.length - docDomain.length;
|
||||||
if ( i === 0 ) { return false; }
|
if ( i === 0 ) { return false; }
|
||||||
return hostname.charCodeAt(i - 1) !== 0x2E /* '.' */;
|
return hostname.charCodeAt(i - 1) !== 0x2E /* '.' */;
|
||||||
},
|
}
|
||||||
setTabId: function(a) {
|
|
||||||
|
setTabId(a) {
|
||||||
this.tabId = a;
|
this.tabId = a;
|
||||||
return this;
|
return this;
|
||||||
},
|
}
|
||||||
getTabOrigin: function() {
|
|
||||||
|
getTabOrigin() {
|
||||||
if ( this.tabOrigin === undefined ) {
|
if ( this.tabOrigin === undefined ) {
|
||||||
const tabContext = µBlock.tabContextManager.mustLookup(this.tabId);
|
const tabContext = µBlock.tabContextManager.mustLookup(this.tabId);
|
||||||
this.tabOrigin = tabContext.origin;
|
this.tabOrigin = tabContext.origin;
|
||||||
@ -234,43 +338,50 @@
|
|||||||
this.tabDomain = tabContext.rootDomain;
|
this.tabDomain = tabContext.rootDomain;
|
||||||
}
|
}
|
||||||
return this.tabOrigin;
|
return this.tabOrigin;
|
||||||
},
|
}
|
||||||
setTabOrigin: function(a) {
|
|
||||||
|
setTabOrigin(a) {
|
||||||
if ( a !== this.tabOrigin ) {
|
if ( a !== this.tabOrigin ) {
|
||||||
this.tabHostname = this.tabDomain = undefined;
|
this.tabHostname = this.tabDomain = undefined;
|
||||||
this.tabOrigin = a;
|
this.tabOrigin = a;
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
},
|
}
|
||||||
setTabOriginFromURL: function(a) {
|
|
||||||
return this.setTabOrigin(this.originFromURI(a));
|
setTabOriginFromURL(a) {
|
||||||
},
|
return this.setTabOrigin(originFromURI(a));
|
||||||
getTabHostname: function() {
|
}
|
||||||
|
|
||||||
|
getTabHostname() {
|
||||||
if ( this.tabHostname === undefined ) {
|
if ( this.tabHostname === undefined ) {
|
||||||
this.tabHostname = this.hostnameFromURI(this.getTabOrigin());
|
this.tabHostname = hostnameFromURI(this.getTabOrigin());
|
||||||
}
|
}
|
||||||
return this.tabHostname;
|
return this.tabHostname;
|
||||||
},
|
}
|
||||||
setTabHostname: function(a) {
|
|
||||||
|
setTabHostname(a) {
|
||||||
if ( a !== this.tabHostname ) {
|
if ( a !== this.tabHostname ) {
|
||||||
this.tabDomain = undefined;
|
this.tabDomain = undefined;
|
||||||
this.tabHostname = a;
|
this.tabHostname = a;
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
},
|
}
|
||||||
getTabDomain: function() {
|
|
||||||
|
getTabDomain() {
|
||||||
if ( this.tabDomain === undefined ) {
|
if ( this.tabDomain === undefined ) {
|
||||||
this.tabDomain = this.domainFromHostname(this.getTabHostname());
|
this.tabDomain = domainFromHostname(this.getTabHostname());
|
||||||
}
|
}
|
||||||
return this.tabDomain;
|
return this.tabDomain;
|
||||||
},
|
}
|
||||||
setTabDomain: function(a) {
|
|
||||||
|
setTabDomain(a) {
|
||||||
this.docDomain = a;
|
this.docDomain = a;
|
||||||
return this;
|
return this;
|
||||||
},
|
}
|
||||||
|
|
||||||
// The idea is to minimize the amout of work done to figure out whether
|
// The idea is to minimize the amout of work done to figure out whether
|
||||||
// the resource is 3rd-party to the top document.
|
// the resource is 3rd-party to the top document.
|
||||||
is3rdPartyToTab: function() {
|
is3rdPartyToTab() {
|
||||||
let tabDomain = this.getTabDomain();
|
let tabDomain = this.getTabDomain();
|
||||||
if ( tabDomain === '' ) { tabDomain = this.tabHostname; }
|
if ( tabDomain === '' ) { tabDomain = this.tabHostname; }
|
||||||
if ( this.domain !== undefined && this.domain !== '' ) {
|
if ( this.domain !== undefined && this.domain !== '' ) {
|
||||||
@ -281,12 +392,38 @@
|
|||||||
const i = hostname.length - tabDomain.length;
|
const i = hostname.length - tabDomain.length;
|
||||||
if ( i === 0 ) { return false; }
|
if ( i === 0 ) { return false; }
|
||||||
return hostname.charCodeAt(i - 1) !== 0x2E /* '.' */;
|
return hostname.charCodeAt(i - 1) !== 0x2E /* '.' */;
|
||||||
},
|
}
|
||||||
setFilter: function(a) {
|
|
||||||
|
setFilter(a) {
|
||||||
this.filter = a;
|
this.filter = a;
|
||||||
return this;
|
return this;
|
||||||
},
|
}
|
||||||
toLogger: function() {
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
toLogger() {
|
||||||
this.tstamp = Date.now();
|
this.tstamp = Date.now();
|
||||||
if ( this.domain === undefined ) {
|
if ( this.domain === undefined ) {
|
||||||
void this.getDomain();
|
void this.getDomain();
|
||||||
@ -297,11 +434,49 @@
|
|||||||
if ( this.tabDomain === undefined ) {
|
if ( this.tabDomain === undefined ) {
|
||||||
void this.getTabDomain();
|
void this.getTabDomain();
|
||||||
}
|
}
|
||||||
µBlock.logger.writeOne(this);
|
const logger = µBlock.logger;
|
||||||
},
|
const filters = this.filter;
|
||||||
originFromURI: µBlock.URI.originFromURI,
|
// Many filters may have been applied to the current context
|
||||||
hostnameFromURI: µBlock.URI.hostnameFromURI,
|
if ( Array.isArray(filters) === false ) {
|
||||||
domainFromHostname: µBlock.URI.domainFromHostname,
|
return logger.writeOne(this);
|
||||||
|
}
|
||||||
|
for ( const filter of filters ) {
|
||||||
|
this.filter = filter;
|
||||||
|
logger.writeOne(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
µBlock.filteringContext = new µBlock.FilteringContext();
|
/******************************************************************************/
|
||||||
|
|
||||||
|
FilteringContext.prototype.BEACON = FilteringContext.BEACON = BEACON;
|
||||||
|
FilteringContext.prototype.CSP_REPORT = FilteringContext.CSP_REPORT = CSP_REPORT;
|
||||||
|
FilteringContext.prototype.FONT = FilteringContext.FONT = FONT;
|
||||||
|
FilteringContext.prototype.IMAGE = FilteringContext.IMAGE = IMAGE;
|
||||||
|
FilteringContext.prototype.IMAGESET = FilteringContext.IMAGESET = IMAGESET;
|
||||||
|
FilteringContext.prototype.MAIN_FRAME = FilteringContext.MAIN_FRAME = MAIN_FRAME;
|
||||||
|
FilteringContext.prototype.MEDIA = FilteringContext.MEDIA = MEDIA;
|
||||||
|
FilteringContext.prototype.OBJECT = FilteringContext.OBJECT = OBJECT;
|
||||||
|
FilteringContext.prototype.OBJECT_SUBREQUEST = FilteringContext.OBJECT_SUBREQUEST = OBJECT_SUBREQUEST;
|
||||||
|
FilteringContext.prototype.PING = FilteringContext.PING = PING;
|
||||||
|
FilteringContext.prototype.SCRIPT = FilteringContext.SCRIPT = SCRIPT;
|
||||||
|
FilteringContext.prototype.STYLESHEET = FilteringContext.STYLESHEET = STYLESHEET;
|
||||||
|
FilteringContext.prototype.SUB_FRAME = FilteringContext.SUB_FRAME = SUB_FRAME;
|
||||||
|
FilteringContext.prototype.WEBSOCKET = FilteringContext.WEBSOCKET = WEBSOCKET;
|
||||||
|
FilteringContext.prototype.XMLHTTPREQUEST = FilteringContext.XMLHTTPREQUEST = XMLHTTPREQUEST;
|
||||||
|
FilteringContext.prototype.INLINE_FONT = FilteringContext.INLINE_FONT = INLINE_FONT;
|
||||||
|
FilteringContext.prototype.INLINE_SCRIPT = FilteringContext.INLINE_SCRIPT = INLINE_SCRIPT;
|
||||||
|
FilteringContext.prototype.OTHER = FilteringContext.OTHER = OTHER;
|
||||||
|
FilteringContext.prototype.FRAME_ANY = FilteringContext.FRAME_ANY = FRAME_ANY;
|
||||||
|
FilteringContext.prototype.FONT_ANY = FilteringContext.FONT_ANY = FONT_ANY;
|
||||||
|
FilteringContext.prototype.INLINE_ANY = FilteringContext.INLINE_ANY = INLINE_ANY;
|
||||||
|
FilteringContext.prototype.PING_ANY = FilteringContext.PING_ANY = PING_ANY;
|
||||||
|
FilteringContext.prototype.SCRIPT_ANY = FilteringContext.SCRIPT_ANY = SCRIPT_ANY;
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
µBlock.FilteringContext = FilteringContext;
|
||||||
|
µBlock.filteringContext = new FilteringContext();
|
||||||
|
|
||||||
|
// <<<<< end of local scope
|
||||||
|
}
|
||||||
|
@ -214,6 +214,7 @@ const LogEntry = function(details) {
|
|||||||
this[prop] = details[prop];
|
this[prop] = details[prop];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.type = details.stype;
|
||||||
if ( details.aliasURL !== undefined ) {
|
if ( details.aliasURL !== undefined ) {
|
||||||
this.aliased = true;
|
this.aliased = true;
|
||||||
}
|
}
|
||||||
|
@ -62,10 +62,11 @@
|
|||||||
ownerId: undefined,
|
ownerId: undefined,
|
||||||
writeOne: function(details) {
|
writeOne: function(details) {
|
||||||
if ( buffer === null ) { return; }
|
if ( buffer === null ) { return; }
|
||||||
|
const box = boxEntry(details);
|
||||||
if ( writePtr === buffer.length ) {
|
if ( writePtr === buffer.length ) {
|
||||||
buffer.push(boxEntry(details));
|
buffer.push(box);
|
||||||
} else {
|
} else {
|
||||||
buffer[writePtr] = boxEntry(details);
|
buffer[writePtr] = box;
|
||||||
}
|
}
|
||||||
writePtr += 1;
|
writePtr += 1;
|
||||||
},
|
},
|
||||||
|
@ -1546,10 +1546,12 @@ const logCSPViolations = function(pageStore, request) {
|
|||||||
cspData = new Map();
|
cspData = new Map();
|
||||||
|
|
||||||
const staticDirectives =
|
const staticDirectives =
|
||||||
µb.staticNetFilteringEngine.matchAndFetchData(fctxt, 'csp');
|
µb.staticNetFilteringEngine.matchAndFetchModifiers(fctxt, 'csp');
|
||||||
for ( const directive of staticDirectives ) {
|
if ( staticDirectives !== undefined ) {
|
||||||
if ( directive.result !== 1 ) { continue; }
|
for ( const directive of staticDirectives ) {
|
||||||
cspData.set(directive.getData('csp'), directive.logData());
|
if ( directive.result !== 1 ) { continue; }
|
||||||
|
cspData.set(directive.value, directive.logData());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fctxt.type = 'inline-script';
|
fctxt.type = 'inline-script';
|
||||||
|
@ -545,27 +545,28 @@ const PageStore = class {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const requestType = fctxt.type;
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
requestType === 'csp_report' &&
|
fctxt.itype === fctxt.CSP_REPORT &&
|
||||||
this.filterCSPReport(fctxt) === 1
|
this.filterCSPReport(fctxt) === 1
|
||||||
) {
|
) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( requestType.endsWith('font') && this.filterFont(fctxt) === 1 ) {
|
if (
|
||||||
|
(fctxt.itype & fctxt.FONT_ANY) !== 0 &&
|
||||||
|
this.filterFont(fctxt) === 1 )
|
||||||
|
{
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
requestType === 'script' &&
|
fctxt.itype === fctxt.SCRIPT &&
|
||||||
this.filterScripting(fctxt, true) === 1
|
this.filterScripting(fctxt, true) === 1
|
||||||
) {
|
) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const cacheableResult = this.cacheableResults.has(requestType);
|
const cacheableResult = this.cacheableResults.has(fctxt.itype);
|
||||||
|
|
||||||
if ( cacheableResult ) {
|
if ( cacheableResult ) {
|
||||||
const entry = this.netFilteringCache.lookupResult(fctxt);
|
const entry = this.netFilteringCache.lookupResult(fctxt);
|
||||||
@ -576,13 +577,16 @@ const PageStore = class {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const requestType = fctxt.type;
|
||||||
|
const loggerEnabled = µb.logger.enabled;
|
||||||
|
|
||||||
// Dynamic URL filtering.
|
// Dynamic URL filtering.
|
||||||
let result = µb.sessionURLFiltering.evaluateZ(
|
let result = µb.sessionURLFiltering.evaluateZ(
|
||||||
fctxt.getTabHostname(),
|
fctxt.getTabHostname(),
|
||||||
fctxt.url,
|
fctxt.url,
|
||||||
requestType
|
requestType
|
||||||
);
|
);
|
||||||
if ( result !== 0 && µb.logger.enabled ) {
|
if ( result !== 0 && loggerEnabled ) {
|
||||||
fctxt.filter = µb.sessionURLFiltering.toLogData();
|
fctxt.filter = µb.sessionURLFiltering.toLogData();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -593,17 +597,17 @@ const PageStore = class {
|
|||||||
fctxt.getHostname(),
|
fctxt.getHostname(),
|
||||||
requestType
|
requestType
|
||||||
);
|
);
|
||||||
if ( result !== 0 && result !== 3 && µb.logger.enabled ) {
|
if ( result !== 0 && result !== 3 && loggerEnabled ) {
|
||||||
fctxt.filter = µb.sessionFirewall.toLogData();
|
fctxt.filter = µb.sessionFirewall.toLogData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Static filtering has lowest precedence.
|
// Static filtering has lowest precedence.
|
||||||
|
const snfe = µb.staticNetFilteringEngine;
|
||||||
if ( result === 0 || result === 3 ) {
|
if ( result === 0 || result === 3 ) {
|
||||||
const snfe = µb.staticNetFilteringEngine;
|
|
||||||
result = snfe.matchString(fctxt);
|
result = snfe.matchString(fctxt);
|
||||||
if ( result !== 0 ) {
|
if ( result !== 0 ) {
|
||||||
if ( µb.logger.enabled ) {
|
if ( loggerEnabled ) {
|
||||||
fctxt.filter = snfe.toLogData();
|
fctxt.filter = snfe.toLogData();
|
||||||
}
|
}
|
||||||
// https://github.com/uBlockOrigin/uBlock-issues/issues/943
|
// https://github.com/uBlockOrigin/uBlock-issues/issues/943
|
||||||
@ -625,8 +629,8 @@ const PageStore = class {
|
|||||||
const frameStore = this.getFrameStore(fctxt.frameId);
|
const frameStore = this.getFrameStore(fctxt.frameId);
|
||||||
if ( frameStore !== null && frameStore.clickToLoad ) {
|
if ( frameStore !== null && frameStore.clickToLoad ) {
|
||||||
result = 2;
|
result = 2;
|
||||||
if ( µb.logger.enabled ) {
|
if ( loggerEnabled ) {
|
||||||
fctxt.setFilter({
|
fctxt.pushFilter({
|
||||||
result,
|
result,
|
||||||
source: 'network',
|
source: 'network',
|
||||||
raw: 'click-to-load',
|
raw: 'click-to-load',
|
||||||
@ -635,19 +639,36 @@ const PageStore = class {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Modifier(s)?
|
||||||
|
// A modifier is an action which transform the original network request.
|
||||||
// https://github.com/gorhill/uBlock/issues/949
|
// https://github.com/gorhill/uBlock/issues/949
|
||||||
// Redirect blocked request?
|
// Redirect blocked request?
|
||||||
if ( result === 1 && µb.hiddenSettings.ignoreRedirectFilters !== true ) {
|
// https://github.com/uBlockOrigin/uBlock-issues/issues/760
|
||||||
const redirectURL = µb.redirectEngine.toURL(fctxt);
|
// Redirect non-blocked request?
|
||||||
if ( redirectURL !== undefined ) {
|
if ( (fctxt.itype & fctxt.INLINE_ANY) === 0 ) {
|
||||||
fctxt.redirectURL = redirectURL;
|
if ( result === 1 ) {
|
||||||
this.internalRedirectionCount += 1;
|
if ( µb.hiddenSettings.ignoreRedirectFilters !== true ) {
|
||||||
|
const redirectURL = µb.redirectEngine.toURL(fctxt);
|
||||||
|
if ( redirectURL !== undefined ) {
|
||||||
|
fctxt.redirectURL = redirectURL;
|
||||||
|
this.internalRedirectionCount += 1;
|
||||||
|
fctxt.pushFilter({
|
||||||
|
source: 'redirect',
|
||||||
|
raw: µb.redirectEngine.resourceNameRegister
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if ( snfe.hasQuery(fctxt) ) {
|
||||||
|
const directives = snfe.filterQuery(fctxt);
|
||||||
|
if ( directives !== undefined && loggerEnabled ) {
|
||||||
|
fctxt.pushFilters(directives.map(a => a.logData()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( cacheableResult ) {
|
if ( cacheableResult ) {
|
||||||
this.netFilteringCache.rememberResult(fctxt, result);
|
this.netFilteringCache.rememberResult(fctxt, result);
|
||||||
} else if ( result === 1 && this.collapsibleResources.has(requestType) ) {
|
} else if ( result === 1 && this.collapsibleResources.has(fctxt.itype) ) {
|
||||||
this.netFilteringCache.rememberBlock(fctxt);
|
this.netFilteringCache.rememberBlock(fctxt);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -670,7 +691,7 @@ const PageStore = class {
|
|||||||
}
|
}
|
||||||
|
|
||||||
filterFont(fctxt) {
|
filterFont(fctxt) {
|
||||||
if ( fctxt.type === 'font' ) {
|
if ( fctxt.itype === fctxt.FONT ) {
|
||||||
this.remoteFontCount += 1;
|
this.remoteFontCount += 1;
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
@ -800,7 +821,7 @@ const PageStore = class {
|
|||||||
}
|
}
|
||||||
if ( exceptCname === false ) { return false; }
|
if ( exceptCname === false ) { return false; }
|
||||||
if ( exceptCname instanceof Object ) {
|
if ( exceptCname instanceof Object ) {
|
||||||
fctxt.setFilter(exceptCname);
|
fctxt.pushFilter(exceptCname);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -830,14 +851,14 @@ const PageStore = class {
|
|||||||
};
|
};
|
||||||
|
|
||||||
PageStore.prototype.cacheableResults = new Set([
|
PageStore.prototype.cacheableResults = new Set([
|
||||||
'sub_frame',
|
µb.FilteringContext.SUB_FRAME,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
PageStore.prototype.collapsibleResources = new Set([
|
PageStore.prototype.collapsibleResources = new Set([
|
||||||
'image',
|
µb.FilteringContext.IMAGE,
|
||||||
'media',
|
µb.FilteringContext.MEDIA,
|
||||||
'object',
|
µb.FilteringContext.OBJECT,
|
||||||
'sub_frame',
|
µb.FilteringContext.SUB_FRAME,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
µb.PageStore = PageStore;
|
µb.PageStore = PageStore;
|
||||||
|
@ -1922,11 +1922,12 @@ const OPTTokenPopunder = 26;
|
|||||||
const OPTTokenPopup = 27;
|
const OPTTokenPopup = 27;
|
||||||
const OPTTokenRedirect = 28;
|
const OPTTokenRedirect = 28;
|
||||||
const OPTTokenRedirectRule = 29;
|
const OPTTokenRedirectRule = 29;
|
||||||
const OPTTokenScript = 30;
|
const OPTTokenQueryprune = 30;
|
||||||
const OPTTokenShide = 31;
|
const OPTTokenScript = 31;
|
||||||
const OPTTokenXhr = 32;
|
const OPTTokenShide = 32;
|
||||||
const OPTTokenWebrtc = 33;
|
const OPTTokenXhr = 33;
|
||||||
const OPTTokenWebsocket = 34;
|
const OPTTokenWebrtc = 34;
|
||||||
|
const OPTTokenWebsocket = 35;
|
||||||
|
|
||||||
const OPTCanNegate = 1 << 8;
|
const OPTCanNegate = 1 << 8;
|
||||||
const OPTBlockOnly = 1 << 9;
|
const OPTBlockOnly = 1 << 9;
|
||||||
@ -1937,8 +1938,9 @@ const OPTDomainList = 1 << 13;
|
|||||||
const OPTType = 1 << 14;
|
const OPTType = 1 << 14;
|
||||||
const OPTNetworkType = 1 << 15;
|
const OPTNetworkType = 1 << 15;
|
||||||
const OPTRedirectType = 1 << 16;
|
const OPTRedirectType = 1 << 16;
|
||||||
const OPTRedirectableType = 1 << 17;
|
const OPTModifiableType = 1 << 17;
|
||||||
const OPTNotSupported = 1 << 18;
|
const OPTModifierType = 1 << 18;
|
||||||
|
const OPTNotSupported = 1 << 19;
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
@ -2003,6 +2005,7 @@ Parser.prototype.OPTTokenOther = OPTTokenOther;
|
|||||||
Parser.prototype.OPTTokenPing = OPTTokenPing;
|
Parser.prototype.OPTTokenPing = OPTTokenPing;
|
||||||
Parser.prototype.OPTTokenPopunder = OPTTokenPopunder;
|
Parser.prototype.OPTTokenPopunder = OPTTokenPopunder;
|
||||||
Parser.prototype.OPTTokenPopup = OPTTokenPopup;
|
Parser.prototype.OPTTokenPopup = OPTTokenPopup;
|
||||||
|
Parser.prototype.OPTTokenQueryprune = OPTTokenQueryprune;
|
||||||
Parser.prototype.OPTTokenRedirect = OPTTokenRedirect;
|
Parser.prototype.OPTTokenRedirect = OPTTokenRedirect;
|
||||||
Parser.prototype.OPTTokenRedirectRule = OPTTokenRedirectRule;
|
Parser.prototype.OPTTokenRedirectRule = OPTTokenRedirectRule;
|
||||||
Parser.prototype.OPTTokenScript = OPTTokenScript;
|
Parser.prototype.OPTTokenScript = OPTTokenScript;
|
||||||
@ -2023,12 +2026,12 @@ Parser.prototype.OPTDomainList = OPTDomainList;
|
|||||||
Parser.prototype.OPTType = OPTType;
|
Parser.prototype.OPTType = OPTType;
|
||||||
Parser.prototype.OPTNetworkType = OPTNetworkType;
|
Parser.prototype.OPTNetworkType = OPTNetworkType;
|
||||||
Parser.prototype.OPTRedirectType = OPTRedirectType;
|
Parser.prototype.OPTRedirectType = OPTRedirectType;
|
||||||
Parser.prototype.OPTRedirectableType = OPTRedirectableType;
|
Parser.prototype.OPTModifiableType = OPTModifiableType;
|
||||||
Parser.prototype.OPTNotSupported = OPTNotSupported;
|
Parser.prototype.OPTNotSupported = OPTNotSupported;
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
const netOptionTokens = new Map([
|
const netOptionTokenDescriptors = new Map([
|
||||||
[ '1p', OPTToken1p | OPTCanNegate ],
|
[ '1p', OPTToken1p | OPTCanNegate ],
|
||||||
[ 'first-party', OPTToken1p | OPTCanNegate ],
|
[ 'first-party', OPTToken1p | OPTCanNegate ],
|
||||||
[ '3p', OPTToken3p | OPTCanNegate ],
|
[ '3p', OPTToken3p | OPTCanNegate ],
|
||||||
@ -2036,47 +2039,136 @@ const netOptionTokens = new Map([
|
|||||||
[ 'all', OPTTokenAll | OPTType | OPTNetworkType ],
|
[ 'all', OPTTokenAll | OPTType | OPTNetworkType ],
|
||||||
[ 'badfilter', OPTTokenBadfilter ],
|
[ 'badfilter', OPTTokenBadfilter ],
|
||||||
[ 'cname', OPTTokenCname | OPTAllowOnly | OPTType ],
|
[ 'cname', OPTTokenCname | OPTAllowOnly | OPTType ],
|
||||||
[ 'csp', OPTTokenCsp | OPTMustAssign | OPTAllowMayAssign ],
|
[ 'csp', OPTTokenCsp | OPTMustAssign | OPTAllowMayAssign | OPTModifierType ],
|
||||||
[ 'css', OPTTokenCss | OPTCanNegate | OPTType | OPTNetworkType | OPTRedirectableType ],
|
[ 'css', OPTTokenCss | OPTCanNegate | OPTType | OPTNetworkType | OPTModifiableType ],
|
||||||
[ 'stylesheet', OPTTokenCss | OPTCanNegate | OPTType | OPTNetworkType | OPTRedirectableType ],
|
[ 'stylesheet', OPTTokenCss | OPTCanNegate | OPTType | OPTNetworkType | OPTModifiableType ],
|
||||||
[ 'denyallow', OPTTokenDenyAllow | OPTMustAssign | OPTDomainList ],
|
[ 'denyallow', OPTTokenDenyAllow | OPTMustAssign | OPTDomainList ],
|
||||||
[ 'doc', OPTTokenDoc | OPTType | OPTNetworkType | OPTCanNegate ],
|
[ 'doc', OPTTokenDoc | OPTType | OPTNetworkType | OPTCanNegate | OPTModifiableType ],
|
||||||
[ 'document', OPTTokenDoc | OPTType | OPTNetworkType | OPTCanNegate ],
|
[ 'document', OPTTokenDoc | OPTType | OPTNetworkType | OPTCanNegate | OPTModifiableType ],
|
||||||
[ 'domain', OPTTokenDomain | OPTMustAssign | OPTDomainList ],
|
[ 'domain', OPTTokenDomain | OPTMustAssign | OPTDomainList ],
|
||||||
[ 'ehide', OPTTokenEhide | OPTType ],
|
[ 'ehide', OPTTokenEhide | OPTType ],
|
||||||
[ 'elemhide', OPTTokenEhide | OPTType ],
|
[ 'elemhide', OPTTokenEhide | OPTType ],
|
||||||
[ 'empty', OPTTokenEmpty | OPTBlockOnly | OPTRedirectType ],
|
[ 'empty', OPTTokenEmpty | OPTBlockOnly | OPTRedirectType | OPTModifierType ],
|
||||||
[ 'frame', OPTTokenFrame | OPTCanNegate | OPTType | OPTNetworkType | OPTRedirectableType ],
|
[ 'frame', OPTTokenFrame | OPTCanNegate | OPTType | OPTNetworkType | OPTModifiableType ],
|
||||||
[ 'subdocument', OPTTokenFrame | OPTCanNegate | OPTType | OPTNetworkType | OPTRedirectableType ],
|
[ 'subdocument', OPTTokenFrame | OPTCanNegate | OPTType | OPTNetworkType | OPTModifiableType ],
|
||||||
[ 'font', OPTTokenFont | OPTCanNegate | OPTType | OPTNetworkType | OPTRedirectableType ],
|
[ 'font', OPTTokenFont | OPTCanNegate | OPTType | OPTNetworkType | OPTModifiableType ],
|
||||||
[ 'genericblock', OPTTokenGenericblock | OPTNotSupported ],
|
[ 'genericblock', OPTTokenGenericblock | OPTNotSupported ],
|
||||||
[ 'ghide', OPTTokenGhide | OPTType ],
|
[ 'ghide', OPTTokenGhide | OPTType ],
|
||||||
[ 'generichide', OPTTokenGhide | OPTType ],
|
[ 'generichide', OPTTokenGhide | OPTType ],
|
||||||
[ 'image', OPTTokenImage | OPTCanNegate | OPTType | OPTNetworkType | OPTRedirectableType ],
|
[ 'image', OPTTokenImage | OPTCanNegate | OPTType | OPTNetworkType | OPTModifiableType ],
|
||||||
[ 'important', OPTTokenImportant | OPTBlockOnly ],
|
[ 'important', OPTTokenImportant | OPTBlockOnly ],
|
||||||
[ 'inline-font', OPTTokenInlineFont | OPTType | OPTCanNegate ],
|
[ 'inline-font', OPTTokenInlineFont | OPTType | OPTCanNegate ],
|
||||||
[ 'inline-script', OPTTokenInlineScript | OPTType | OPTCanNegate ],
|
[ 'inline-script', OPTTokenInlineScript | OPTType | OPTCanNegate ],
|
||||||
[ 'media', OPTTokenMedia | OPTCanNegate | OPTType | OPTNetworkType | OPTRedirectableType ],
|
[ 'media', OPTTokenMedia | OPTCanNegate | OPTType | OPTNetworkType | OPTModifiableType ],
|
||||||
[ 'mp4', OPTTokenMp4 | OPTType | OPTNetworkType | OPTBlockOnly | OPTRedirectType | OPTRedirectableType ],
|
[ 'mp4', OPTTokenMp4 | OPTType | OPTNetworkType | OPTBlockOnly | OPTRedirectType | OPTModifierType ],
|
||||||
[ 'object', OPTTokenObject | OPTCanNegate | OPTType | OPTNetworkType | OPTRedirectableType ],
|
[ 'object', OPTTokenObject | OPTCanNegate | OPTType | OPTNetworkType | OPTModifiableType ],
|
||||||
[ 'object-subrequest', OPTTokenObject | OPTCanNegate | OPTType | OPTNetworkType ],
|
[ 'object-subrequest', OPTTokenObject | OPTCanNegate | OPTType | OPTNetworkType | OPTModifiableType ],
|
||||||
[ 'other', OPTTokenOther | OPTCanNegate | OPTType | OPTNetworkType | OPTRedirectableType ],
|
[ 'other', OPTTokenOther | OPTCanNegate | OPTType | OPTNetworkType | OPTModifiableType ],
|
||||||
[ 'ping', OPTTokenPing | OPTCanNegate | OPTType | OPTNetworkType ],
|
[ 'ping', OPTTokenPing | OPTCanNegate | OPTType | OPTNetworkType | OPTModifiableType ],
|
||||||
[ 'beacon', OPTTokenPing | OPTCanNegate | OPTType | OPTNetworkType ],
|
[ 'beacon', OPTTokenPing | OPTCanNegate | OPTType | OPTNetworkType | OPTModifiableType ],
|
||||||
[ 'popunder', OPTTokenPopunder | OPTType ],
|
[ 'popunder', OPTTokenPopunder | OPTType ],
|
||||||
[ 'popup', OPTTokenPopup | OPTType | OPTCanNegate ],
|
[ 'popup', OPTTokenPopup | OPTType | OPTCanNegate ],
|
||||||
[ 'redirect', OPTTokenRedirect | OPTMustAssign | OPTBlockOnly | OPTRedirectType ],
|
[ 'queryprune', OPTTokenQueryprune | OPTMustAssign | OPTAllowMayAssign | OPTModifierType ],
|
||||||
[ 'redirect-rule', OPTTokenRedirectRule | OPTMustAssign | OPTBlockOnly | OPTRedirectType ],
|
[ 'redirect', OPTTokenRedirect | OPTMustAssign | OPTBlockOnly | OPTRedirectType | OPTModifierType ],
|
||||||
[ 'script', OPTTokenScript | OPTCanNegate | OPTType | OPTNetworkType | OPTRedirectableType ],
|
[ 'redirect-rule', OPTTokenRedirectRule | OPTMustAssign | OPTBlockOnly | OPTRedirectType | OPTModifierType ],
|
||||||
|
[ 'script', OPTTokenScript | OPTCanNegate | OPTType | OPTNetworkType | OPTModifiableType ],
|
||||||
[ 'shide', OPTTokenShide | OPTType ],
|
[ 'shide', OPTTokenShide | OPTType ],
|
||||||
[ 'specifichide', OPTTokenShide | OPTType ],
|
[ 'specifichide', OPTTokenShide | OPTType ],
|
||||||
[ 'xhr', OPTTokenXhr | OPTCanNegate| OPTType | OPTNetworkType | OPTRedirectableType ],
|
[ 'xhr', OPTTokenXhr | OPTCanNegate| OPTType | OPTNetworkType | OPTModifiableType ],
|
||||||
[ 'xmlhttprequest', OPTTokenXhr | OPTCanNegate | OPTType | OPTNetworkType | OPTRedirectableType ],
|
[ 'xmlhttprequest', OPTTokenXhr | OPTCanNegate | OPTType | OPTNetworkType | OPTModifiableType ],
|
||||||
[ 'webrtc', OPTTokenWebrtc | OPTNotSupported ],
|
[ 'webrtc', OPTTokenWebrtc | OPTNotSupported ],
|
||||||
[ 'websocket', OPTTokenWebsocket | OPTCanNegate | OPTType | OPTNetworkType ],
|
[ 'websocket', OPTTokenWebsocket | OPTCanNegate | OPTType | OPTNetworkType | OPTModifiableType ],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
Parser.prototype.netOptionTokens = netOptionTokens;
|
Parser.prototype.netOptionTokenDescriptors =
|
||||||
|
Parser.netOptionTokenDescriptors = netOptionTokenDescriptors;
|
||||||
|
|
||||||
|
Parser.netOptionTokenIds = new Map([
|
||||||
|
[ '1p', OPTToken1p ],
|
||||||
|
[ 'first-party', OPTToken1p ],
|
||||||
|
[ '3p', OPTToken3p ],
|
||||||
|
[ 'third-party', OPTToken3p ],
|
||||||
|
[ 'all', OPTTokenAll ],
|
||||||
|
[ 'badfilter', OPTTokenBadfilter ],
|
||||||
|
[ 'cname', OPTTokenCname ],
|
||||||
|
[ 'csp', OPTTokenCsp ],
|
||||||
|
[ 'css', OPTTokenCss ],
|
||||||
|
[ 'stylesheet', OPTTokenCss ],
|
||||||
|
[ 'denyallow', OPTTokenDenyAllow ],
|
||||||
|
[ 'doc', OPTTokenDoc ],
|
||||||
|
[ 'document', OPTTokenDoc ],
|
||||||
|
[ 'domain', OPTTokenDomain ],
|
||||||
|
[ 'ehide', OPTTokenEhide ],
|
||||||
|
[ 'elemhide', OPTTokenEhide ],
|
||||||
|
[ 'empty', OPTTokenEmpty ],
|
||||||
|
[ 'frame', OPTTokenFrame ],
|
||||||
|
[ 'subdocument', OPTTokenFrame ],
|
||||||
|
[ 'font', OPTTokenFont ],
|
||||||
|
[ 'genericblock', OPTTokenGenericblock ],
|
||||||
|
[ 'ghide', OPTTokenGhide ],
|
||||||
|
[ 'generichide', OPTTokenGhide ],
|
||||||
|
[ 'image', OPTTokenImage ],
|
||||||
|
[ 'important', OPTTokenImportant ],
|
||||||
|
[ 'inline-font', OPTTokenInlineFont ],
|
||||||
|
[ 'inline-script', OPTTokenInlineScript ],
|
||||||
|
[ 'media', OPTTokenMedia ],
|
||||||
|
[ 'mp4', OPTTokenMp4 ],
|
||||||
|
[ 'object', OPTTokenObject ],
|
||||||
|
[ 'object-subrequest', OPTTokenObject ],
|
||||||
|
[ 'other', OPTTokenOther ],
|
||||||
|
[ 'ping', OPTTokenPing ],
|
||||||
|
[ 'beacon', OPTTokenPing ],
|
||||||
|
[ 'popunder', OPTTokenPopunder ],
|
||||||
|
[ 'popup', OPTTokenPopup ],
|
||||||
|
[ 'queryprune', OPTTokenQueryprune ],
|
||||||
|
[ 'redirect', OPTTokenRedirect ],
|
||||||
|
[ 'redirect-rule', OPTTokenRedirectRule ],
|
||||||
|
[ 'script', OPTTokenScript ],
|
||||||
|
[ 'shide', OPTTokenShide ],
|
||||||
|
[ 'specifichide', OPTTokenShide ],
|
||||||
|
[ 'xhr', OPTTokenXhr ],
|
||||||
|
[ 'xmlhttprequest', OPTTokenXhr ],
|
||||||
|
[ 'webrtc', OPTTokenWebrtc ],
|
||||||
|
[ 'websocket', OPTTokenWebsocket ],
|
||||||
|
]);
|
||||||
|
|
||||||
|
Parser.netOptionTokenNames = new Map([
|
||||||
|
[ OPTToken1p, '1p' ],
|
||||||
|
[ OPTToken3p, '3p' ],
|
||||||
|
[ OPTTokenAll, 'all' ],
|
||||||
|
[ OPTTokenBadfilter, 'badfilter' ],
|
||||||
|
[ OPTTokenCname, 'cname' ],
|
||||||
|
[ OPTTokenCsp, 'csp' ],
|
||||||
|
[ OPTTokenCss, 'stylesheet' ],
|
||||||
|
[ OPTTokenDenyAllow, 'denyallow' ],
|
||||||
|
[ OPTTokenDoc, 'document' ],
|
||||||
|
[ OPTTokenDomain, 'domain' ],
|
||||||
|
[ OPTTokenEhide, 'elemhide' ],
|
||||||
|
[ OPTTokenEmpty, 'empty' ],
|
||||||
|
[ OPTTokenFrame, 'subdocument' ],
|
||||||
|
[ OPTTokenFont, 'font' ],
|
||||||
|
[ OPTTokenGenericblock, 'genericblock' ],
|
||||||
|
[ OPTTokenGhide, 'generichide' ],
|
||||||
|
[ OPTTokenImage, 'image' ],
|
||||||
|
[ OPTTokenImportant, 'important' ],
|
||||||
|
[ OPTTokenInlineFont, 'inline-font' ],
|
||||||
|
[ OPTTokenInlineScript, 'inline-script' ],
|
||||||
|
[ OPTTokenMedia, 'media' ],
|
||||||
|
[ OPTTokenMp4, 'mp4' ],
|
||||||
|
[ OPTTokenObject, 'object' ],
|
||||||
|
[ OPTTokenOther, 'other' ],
|
||||||
|
[ OPTTokenPing, 'ping' ],
|
||||||
|
[ OPTTokenPopunder, 'popunder' ],
|
||||||
|
[ OPTTokenPopup, 'popup' ],
|
||||||
|
[ OPTTokenQueryprune, 'queryprune' ],
|
||||||
|
[ OPTTokenRedirect, 'redirect' ],
|
||||||
|
[ OPTTokenRedirectRule, 'redirect-rule' ],
|
||||||
|
[ OPTTokenScript, 'script' ],
|
||||||
|
[ OPTTokenShide, 'specifichide' ],
|
||||||
|
[ OPTTokenXhr, 'xmlhttprequest' ],
|
||||||
|
[ OPTTokenWebrtc, 'webrtc' ],
|
||||||
|
[ OPTTokenWebsocket, 'websocket' ],
|
||||||
|
]);
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
@ -2134,9 +2226,8 @@ const NetOptionsIterator = class {
|
|||||||
const slices = this.parser.slices;
|
const slices = this.parser.slices;
|
||||||
const optSlices = this.optSlices;
|
const optSlices = this.optSlices;
|
||||||
let typeCount = 0;
|
let typeCount = 0;
|
||||||
let redirectableTypeCount = 0;
|
let modifiableTypeCount = 0;
|
||||||
let redirectIndex = -1;
|
let modifierIndex = -1;
|
||||||
let cspIndex = -1;
|
|
||||||
let writePtr = 0;
|
let writePtr = 0;
|
||||||
let lopt = lopts;
|
let lopt = lopts;
|
||||||
while ( lopt < ropts ) {
|
while ( lopt < ropts ) {
|
||||||
@ -2170,7 +2261,7 @@ const NetOptionsIterator = class {
|
|||||||
if ( good ) {
|
if ( good ) {
|
||||||
const rtok = lval === 0 ? i : lval;
|
const rtok = lval === 0 ? i : lval;
|
||||||
const token = this.parser.raw.slice(slices[ltok+1], slices[rtok+1]);
|
const token = this.parser.raw.slice(slices[ltok+1], slices[rtok+1]);
|
||||||
descriptor = netOptionTokens.get(token);
|
descriptor = netOptionTokenDescriptors.get(token);
|
||||||
}
|
}
|
||||||
// Validate option according to context
|
// Validate option according to context
|
||||||
if (
|
if (
|
||||||
@ -2189,20 +2280,17 @@ const NetOptionsIterator = class {
|
|||||||
// Keep count of types
|
// Keep count of types
|
||||||
if ( hasBits(descriptor, OPTType) ) {
|
if ( hasBits(descriptor, OPTType) ) {
|
||||||
typeCount += 1;
|
typeCount += 1;
|
||||||
if ( hasBits(descriptor, OPTRedirectableType) ) {
|
const modifiable = hasBits(descriptor, OPTModifiableType);
|
||||||
redirectableTypeCount += 1;
|
if ( modifiable ) {
|
||||||
}
|
modifiableTypeCount += 1;
|
||||||
}
|
} else if ( modifierIndex !== -1 ) {
|
||||||
// Only one `redirect` or `csp` can be present
|
|
||||||
if ( hasBits(descriptor, OPTRedirectType) ) {
|
|
||||||
if ( redirectIndex === -1 ) {
|
|
||||||
redirectIndex = writePtr;
|
|
||||||
} else {
|
|
||||||
descriptor = OPTTokenInvalid;
|
descriptor = OPTTokenInvalid;
|
||||||
}
|
}
|
||||||
} else if ( (descriptor & 0xFF) === OPTTokenCsp ) {
|
}
|
||||||
if ( cspIndex === -1 ) {
|
// Only one modifier can be present
|
||||||
cspIndex = writePtr;
|
if ( hasBits(descriptor, OPTModifierType) ) {
|
||||||
|
if ( modifierIndex === -1 ) {
|
||||||
|
modifierIndex = writePtr;
|
||||||
} else {
|
} else {
|
||||||
descriptor = OPTTokenInvalid;
|
descriptor = OPTTokenInvalid;
|
||||||
}
|
}
|
||||||
@ -2244,38 +2332,26 @@ const NetOptionsIterator = class {
|
|||||||
if ( this.interactive && hasBits(this.parser.slices[ropts-3], BITComma) ) {
|
if ( this.interactive && hasBits(this.parser.slices[ropts-3], BITComma) ) {
|
||||||
this.parser.slices[ropts-3] |= BITError;
|
this.parser.slices[ropts-3] |= BITError;
|
||||||
}
|
}
|
||||||
// Invalid combinations of options
|
|
||||||
//
|
|
||||||
// `csp` can't be used with any other types or redirection
|
|
||||||
if ( cspIndex !== -1 && ( typeCount !== 0 || redirectIndex !== -1 ) ) {
|
|
||||||
optSlices[cspIndex] = OPTTokenInvalid;
|
|
||||||
if ( this.interactive ) {
|
|
||||||
this.parser.markSlices(
|
|
||||||
optSlices[cspIndex+1],
|
|
||||||
optSlices[cspIndex+5],
|
|
||||||
BITError
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// `redirect` requires one single redirectable type, EXCEPT for when we
|
// `redirect` requires one single redirectable type, EXCEPT for when we
|
||||||
// redirect to `empty`, in which case it is allowed to not have any
|
// redirect to `empty`, in which case it is allowed to not have any
|
||||||
// network type specified.
|
// network type specified.
|
||||||
if (
|
if (
|
||||||
redirectIndex !== -1 &&
|
modifierIndex !== -1 &&
|
||||||
redirectableTypeCount !== 1 && (
|
hasBits(optSlices[modifierIndex+0], OPTRedirectType) &&
|
||||||
redirectableTypeCount !== 0 ||
|
modifiableTypeCount !== 1 && (
|
||||||
|
modifiableTypeCount !== 0 ||
|
||||||
typeCount !== 0 ||
|
typeCount !== 0 ||
|
||||||
this.parser.raw.slice(
|
this.parser.raw.slice(
|
||||||
this.parser.slices[optSlices[redirectIndex+0]+1],
|
this.parser.slices[optSlices[modifierIndex+0]+1],
|
||||||
this.parser.slices[optSlices[redirectIndex+5]+1]
|
this.parser.slices[optSlices[modifierIndex+5]+1]
|
||||||
).endsWith('empty') === false
|
).endsWith('empty') === false
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
optSlices[redirectIndex] = OPTTokenInvalid;
|
optSlices[modifierIndex] = OPTTokenInvalid;
|
||||||
if ( this.interactive ) {
|
if ( this.interactive ) {
|
||||||
this.parser.markSlices(
|
this.parser.markSlices(
|
||||||
optSlices[redirectIndex+1],
|
optSlices[modifierIndex+1],
|
||||||
optSlices[redirectIndex+5],
|
optSlices[modifierIndex+5],
|
||||||
BITError
|
BITError
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -33,103 +33,117 @@ const µb = µBlock;
|
|||||||
const urlTokenizer = µb.urlTokenizer;
|
const urlTokenizer = µb.urlTokenizer;
|
||||||
|
|
||||||
// fedcba9876543210
|
// fedcba9876543210
|
||||||
// | | |||
|
// | | || |
|
||||||
// | | |||
|
// | | || |
|
||||||
// | | |||
|
// | | || |
|
||||||
// | | |||
|
// | | || |
|
||||||
// | | ||+---- bit 0: [BlockAction | AllowAction]
|
// | | || +---- bit 0- 1: block=0, allow=1, modify=2
|
||||||
// | | |+----- bit 1: `important`
|
// | | |+------ bit 2: important
|
||||||
// | | +------ bit 2- 3: party [0 - 3]
|
// | | +------- bit 3- 4: party [0-3]
|
||||||
// | +-------- bit 4- 8: type [0 - 31]
|
// | +--------- bit 5- 9: type [0-31]
|
||||||
// +------------- bit 9-15: unused
|
// +-------------- bit 10-15: unused
|
||||||
|
|
||||||
|
const ActionBitsMask = 0b0000000011;
|
||||||
|
const TypeBitsMask = 0b1111100000;
|
||||||
|
const TypeBitsOffset = 5;
|
||||||
|
|
||||||
|
const BlockAction = 0 << 0;
|
||||||
|
const AllowAction = 1 << 0;
|
||||||
|
const ModifyAction = 2 << 0;
|
||||||
|
// Note:
|
||||||
|
// It's possible to increase granularity of ModifyAction realm with
|
||||||
|
// sub-realms if it helps performance, but so far I found it's not
|
||||||
|
// needed, there is no meaningful gains to be had.
|
||||||
|
|
||||||
|
const Important = 1 << 2;
|
||||||
|
|
||||||
const BlockAction = 0 << 0;
|
|
||||||
const AllowAction = 1 << 0;
|
|
||||||
const Important = 1 << 1;
|
|
||||||
const AnyParty = 0 << 2;
|
|
||||||
const FirstParty = 1 << 2;
|
|
||||||
const ThirdParty = 2 << 2;
|
|
||||||
const BlockImportant = BlockAction | Important;
|
const BlockImportant = BlockAction | Important;
|
||||||
|
|
||||||
|
const AnyParty = 0 << 3;
|
||||||
|
const FirstParty = 1 << 3;
|
||||||
|
const ThirdParty = 2 << 3;
|
||||||
|
|
||||||
|
|
||||||
const typeNameToTypeValue = {
|
const typeNameToTypeValue = {
|
||||||
'no_type': 0 << 4,
|
'no_type': 0 << TypeBitsOffset,
|
||||||
'stylesheet': 1 << 4,
|
'stylesheet': 1 << TypeBitsOffset,
|
||||||
'image': 2 << 4,
|
'image': 2 << TypeBitsOffset,
|
||||||
'object': 3 << 4,
|
'object': 3 << TypeBitsOffset,
|
||||||
'object_subrequest': 3 << 4,
|
'object_subrequest': 3 << TypeBitsOffset,
|
||||||
'script': 4 << 4,
|
'script': 4 << TypeBitsOffset,
|
||||||
'fetch': 5 << 4,
|
'fetch': 5 << TypeBitsOffset,
|
||||||
'xmlhttprequest': 5 << 4,
|
'xmlhttprequest': 5 << TypeBitsOffset,
|
||||||
'sub_frame': 6 << 4,
|
'sub_frame': 6 << TypeBitsOffset,
|
||||||
'font': 7 << 4,
|
'font': 7 << TypeBitsOffset,
|
||||||
'media': 8 << 4,
|
'media': 8 << TypeBitsOffset,
|
||||||
'websocket': 9 << 4,
|
'websocket': 9 << TypeBitsOffset,
|
||||||
'beacon': 10 << 4,
|
'beacon': 10 << TypeBitsOffset,
|
||||||
'ping': 10 << 4,
|
'ping': 10 << TypeBitsOffset,
|
||||||
'other': 11 << 4,
|
'other': 11 << TypeBitsOffset,
|
||||||
'popup': 12 << 4, // start of behavorial filtering
|
'popup': 12 << TypeBitsOffset, // start of behavorial filtering
|
||||||
'popunder': 13 << 4,
|
'popunder': 13 << TypeBitsOffset,
|
||||||
'main_frame': 14 << 4, // start of 1st-party-only behavorial filtering
|
'main_frame': 14 << TypeBitsOffset, // start of 1p behavorial filtering
|
||||||
'generichide': 15 << 4,
|
'generichide': 15 << TypeBitsOffset,
|
||||||
'specifichide': 16 << 4,
|
'specifichide': 16 << TypeBitsOffset,
|
||||||
'inline-font': 17 << 4,
|
'inline-font': 17 << TypeBitsOffset,
|
||||||
'inline-script': 18 << 4,
|
'inline-script': 18 << TypeBitsOffset,
|
||||||
'cname': 19 << 4,
|
'cname': 19 << TypeBitsOffset,
|
||||||
'data': 20 << 4, // special: a generic data holder
|
'unused': 20 << TypeBitsOffset,
|
||||||
'redirect': 21 << 4,
|
'redirect': 21 << TypeBitsOffset,
|
||||||
'webrtc': 22 << 4,
|
'webrtc': 22 << TypeBitsOffset,
|
||||||
'unsupported': 23 << 4,
|
'unsupported': 23 << TypeBitsOffset,
|
||||||
};
|
};
|
||||||
|
|
||||||
const otherTypeBitValue = typeNameToTypeValue.other;
|
const otherTypeBitValue = typeNameToTypeValue.other;
|
||||||
|
|
||||||
const bitFromType = type =>
|
const bitFromType = type =>
|
||||||
1 << ((typeNameToTypeValue[type] >>> 4) - 1);
|
1 << ((typeNameToTypeValue[type] >>> TypeBitsOffset) - 1);
|
||||||
|
|
||||||
// All network request types to bitmap
|
// All network request types to bitmap
|
||||||
// bring origin to 0 (from 4 -- see typeNameToTypeValue)
|
// bring origin to 0 (from TypeBitsOffset -- see typeNameToTypeValue)
|
||||||
// left-shift 1 by the above-calculated value
|
// left-shift 1 by the above-calculated value
|
||||||
// subtract 1 to set all type bits
|
// subtract 1 to set all type bits
|
||||||
const allNetworkTypesBits =
|
const allNetworkTypesBits =
|
||||||
(1 << (otherTypeBitValue >>> 4)) - 1;
|
(1 << (otherTypeBitValue >>> TypeBitsOffset)) - 1;
|
||||||
|
|
||||||
const allTypesBits =
|
const allTypesBits =
|
||||||
allNetworkTypesBits |
|
allNetworkTypesBits |
|
||||||
1 << (typeNameToTypeValue['popup'] >>> 4) - 1 |
|
1 << (typeNameToTypeValue['popup'] >>> TypeBitsOffset) - 1 |
|
||||||
1 << (typeNameToTypeValue['main_frame'] >>> 4) - 1 |
|
1 << (typeNameToTypeValue['main_frame'] >>> TypeBitsOffset) - 1 |
|
||||||
1 << (typeNameToTypeValue['inline-font'] >>> 4) - 1 |
|
1 << (typeNameToTypeValue['inline-font'] >>> TypeBitsOffset) - 1 |
|
||||||
1 << (typeNameToTypeValue['inline-script'] >>> 4) - 1;
|
1 << (typeNameToTypeValue['inline-script'] >>> TypeBitsOffset) - 1;
|
||||||
|
|
||||||
const unsupportedTypeBit =
|
const unsupportedTypeBit =
|
||||||
1 << (typeNameToTypeValue['unsupported'] >>> 4) - 1;
|
1 << (typeNameToTypeValue['unsupported'] >>> TypeBitsOffset) - 1;
|
||||||
|
|
||||||
const typeValueToTypeName = {
|
const typeValueToTypeName = [
|
||||||
1: 'stylesheet',
|
'',
|
||||||
2: 'image',
|
'stylesheet',
|
||||||
3: 'object',
|
'image',
|
||||||
4: 'script',
|
'object',
|
||||||
5: 'xmlhttprequest',
|
'script',
|
||||||
6: 'subdocument',
|
'xmlhttprequest',
|
||||||
7: 'font',
|
'subdocument',
|
||||||
8: 'media',
|
'font',
|
||||||
9: 'websocket',
|
'media',
|
||||||
10: 'ping',
|
'websocket',
|
||||||
11: 'other',
|
'ping',
|
||||||
12: 'popup',
|
'other',
|
||||||
13: 'popunder',
|
'popup',
|
||||||
14: 'document',
|
'popunder',
|
||||||
15: 'generichide',
|
'document',
|
||||||
16: 'specifichide',
|
'generichide',
|
||||||
17: 'inline-font',
|
'specifichide',
|
||||||
18: 'inline-script',
|
'inline-font',
|
||||||
19: 'cname',
|
'inline-script',
|
||||||
20: 'data',
|
'cname',
|
||||||
21: 'redirect',
|
'unused',
|
||||||
22: 'webrtc',
|
'redirect',
|
||||||
23: 'unsupported',
|
'webrtc',
|
||||||
};
|
'unsupported',
|
||||||
|
];
|
||||||
|
|
||||||
const typeValueFromCatBits = catBits => (catBits >>> 4) & 0b11111;
|
//const typeValueFromCatBits = catBits => (catBits >>> TypeBitsOffset) & 0b11111;
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
@ -222,9 +236,9 @@ const toLogDataInternal = function(categoryBits, tokenHash, iunit) {
|
|||||||
} else if ( categoryBits & 0x004 ) {
|
} else if ( categoryBits & 0x004 ) {
|
||||||
logData.options.unshift('1p');
|
logData.options.unshift('1p');
|
||||||
}
|
}
|
||||||
const type = categoryBits & 0x1F0;
|
const type = categoryBits & TypeBitsMask;
|
||||||
if ( type !== 0 && type !== typeNameToTypeValue.data ) {
|
if ( type !== 0 ) {
|
||||||
logData.options.unshift(typeValueToTypeName[type >>> 4]);
|
logData.options.unshift(typeValueToTypeName[type >>> TypeBitsOffset]);
|
||||||
}
|
}
|
||||||
let raw = logData.pattern.join('');
|
let raw = logData.pattern.join('');
|
||||||
if (
|
if (
|
||||||
@ -1444,80 +1458,96 @@ registerFilterClass(FilterOriginEntityMiss);
|
|||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
const FilterDataHolder = class {
|
const FilterModifier = class {
|
||||||
constructor(dataType, data) {
|
constructor(actionBits, modifier, value) {
|
||||||
this.dataType = dataType;
|
this.actionBits = actionBits;
|
||||||
this.data = data;
|
this.type = modifier;
|
||||||
|
this.value = value;
|
||||||
|
this.cache = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
match() {
|
match() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
matchAndFetchData(type, callback) {
|
matchAndFetchModifiers(env) {
|
||||||
if ( this.dataType !== type ) { return; }
|
if ( this.type !== env.modifier ) { return; }
|
||||||
callback(this);
|
env.results.push(
|
||||||
|
new FilterModifierResult(env.bits, env.th, env.iunit)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getData(type) {
|
get modifier() {
|
||||||
if ( type === this.dataType ) {
|
return this;
|
||||||
return this.data;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
logData(details) {
|
logData(details) {
|
||||||
let opt = this.dataType;
|
let opt = vAPI.StaticFilteringParser.netOptionTokenNames.get(this.type);
|
||||||
if ( this.data !== '' ) {
|
if ( this.value !== '' ) {
|
||||||
opt += `=${this.data}`;
|
opt += `=${this.value}`;
|
||||||
}
|
}
|
||||||
details.options.push(opt);
|
details.options.push(opt);
|
||||||
}
|
}
|
||||||
|
|
||||||
toSelfie() {
|
toSelfie() {
|
||||||
return [ this.fid, this.dataType, this.data ];
|
return [ this.fid, this.actionBits, this.type, this.value ];
|
||||||
}
|
}
|
||||||
|
|
||||||
static compile(details) {
|
static compile(details) {
|
||||||
return [ FilterDataHolder.fid, details.dataType, details.data ];
|
return [
|
||||||
|
FilterModifier.fid,
|
||||||
|
details.action | details.important,
|
||||||
|
details.modifyType,
|
||||||
|
details.modifyValue
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromCompiled(args) {
|
static fromCompiled(args) {
|
||||||
return new FilterDataHolder(args[1], args[2]);
|
return new FilterModifier(args[1], args[2], args[3]);
|
||||||
}
|
}
|
||||||
|
|
||||||
static fromSelfie(args) {
|
static fromSelfie(args) {
|
||||||
return new FilterDataHolder(args[1], args[2]);
|
return new FilterModifier(args[1], args[2], args[3]);
|
||||||
}
|
}
|
||||||
|
|
||||||
static keyFromArgs(args) {
|
static keyFromArgs(args) {
|
||||||
return `${args[1]}\t${args[2]}`;
|
return `${args[1]}\t${args[2]}\t${args[3]}`;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
registerFilterClass(FilterDataHolder);
|
registerFilterClass(FilterModifier);
|
||||||
|
|
||||||
// Helper class for storing instances of FilterDataHolder which were found to
|
// Helper class for storing instances of FilterModifier which were found to
|
||||||
// be a match.
|
// be a match.
|
||||||
|
|
||||||
const FilterDataHolderResult = class {
|
const FilterModifierResult = class {
|
||||||
constructor(bits, th, iunit) {
|
constructor(bits, th, iunit) {
|
||||||
this.bits = bits;
|
|
||||||
this.th = th;
|
|
||||||
this.iunit = iunit;
|
this.iunit = iunit;
|
||||||
|
this.th = th;
|
||||||
|
this.bits = (bits & ~ActionBitsMask) | this.modifier.actionBits;
|
||||||
}
|
}
|
||||||
|
|
||||||
getData(type) {
|
get filter() {
|
||||||
return filterUnits[this.iunit].getData(type);
|
return filterUnits[this.iunit];
|
||||||
|
}
|
||||||
|
|
||||||
|
get modifier() {
|
||||||
|
return this.filter.modifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
get result() {
|
get result() {
|
||||||
return (this.bits & AllowAction) === 0 ? 1 : 2;
|
return (this.bits & AllowAction) === 0 ? 1 : 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get value() {
|
||||||
|
return this.modifier.value;
|
||||||
|
}
|
||||||
|
|
||||||
logData() {
|
logData() {
|
||||||
const r = toLogDataInternal(this.bits, this.th, this.iunit);
|
const r = toLogDataInternal(this.bits, this.th, this.iunit);
|
||||||
r.source = 'static';
|
r.source = 'static';
|
||||||
r.result = this.result;
|
r.result = this.result;
|
||||||
|
r.modifier = true;
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -1636,20 +1666,21 @@ const FilterCompositeAll = class extends FilterCollection {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
matchAndFetchData(type, callback) {
|
matchAndFetchModifiers(env) {
|
||||||
if ( this.match() !== true ) { return false; }
|
if ( this.match() !== true ) { return false; }
|
||||||
this.forEach(iunit => {
|
this.forEach(iunit => {
|
||||||
const f = filterUnits[iunit];
|
const f = filterUnits[iunit];
|
||||||
if ( f.matchAndFetchData instanceof Function === false ) { return; }
|
if ( f.matchAndFetchModifiers instanceof Function ) {
|
||||||
f.matchAndFetchData(type, ( ) => { callback(this); });
|
f.matchAndFetchModifiers(env);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getData(type) {
|
get modifier() {
|
||||||
return this.forEach(iunit => {
|
return this.forEach(iunit => {
|
||||||
const f = filterUnits[iunit];
|
const f = filterUnits[iunit];
|
||||||
if ( f.matchAndFetchData instanceof Function ) {
|
if ( f.matchAndFetchModifiers instanceof Function ) {
|
||||||
return f.getData(type);
|
return f.modifier;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1902,7 +1933,7 @@ const FilterPlainTrie = class {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
matchAndFetchData(/* type, out */) {
|
matchAndFetchModifiers(/* type, callback */) {
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1953,12 +1984,11 @@ const FilterBucket = class extends FilterCollection {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
matchAndFetchData(type, callback) {
|
matchAndFetchModifiers(env) {
|
||||||
const units = filterUnits;
|
const units = filterUnits;
|
||||||
this.forEach(iunit => {
|
this.forEach(iunit => {
|
||||||
units[iunit].matchAndFetchData(type, f => {
|
env.iunit = iunit;
|
||||||
callback(f, iunit);
|
units[iunit].matchAndFetchModifiers(env);
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2226,8 +2256,8 @@ const FilterParser = class {
|
|||||||
// 0101 (0x5): anchored to the hostname and end of the URL.
|
// 0101 (0x5): anchored to the hostname and end of the URL.
|
||||||
this.anchor = 0;
|
this.anchor = 0;
|
||||||
this.badFilter = false;
|
this.badFilter = false;
|
||||||
this.dataType = undefined;
|
this.modifyType = undefined;
|
||||||
this.data = undefined;
|
this.modifyValue = undefined;
|
||||||
this.invalid = false;
|
this.invalid = false;
|
||||||
this.pattern = '';
|
this.pattern = '';
|
||||||
this.firstParty = false;
|
this.firstParty = false;
|
||||||
@ -2325,18 +2355,17 @@ const FilterParser = class {
|
|||||||
this.badFilter = true;
|
this.badFilter = true;
|
||||||
break;
|
break;
|
||||||
case parser.OPTTokenCsp:
|
case parser.OPTTokenCsp:
|
||||||
this.typeBits = bitFromType('data');
|
this.modifyType = parser.OPTTokenCsp;
|
||||||
this.dataType = 'csp';
|
|
||||||
if ( val !== undefined ) {
|
if ( val !== undefined ) {
|
||||||
if ( this.reBadCSP.test(val) ) { return false; }
|
if ( this.reBadCSP.test(val) ) { return false; }
|
||||||
this.data = val;
|
this.modifyValue = val;
|
||||||
} else if ( this.action === AllowAction ) {
|
} else if ( this.action === AllowAction ) {
|
||||||
this.data = '';
|
this.modifyValue = '';
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
// https://github.com/gorhill/uBlock/issues/2294
|
// https://github.com/gorhill/uBlock/issues/2294
|
||||||
// Detect and discard filter if domain option contains nonsensical
|
// Detect and discard filter if domain option contains
|
||||||
// characters.
|
// nonsensical characters.
|
||||||
case parser.OPTTokenDomain:
|
case parser.OPTTokenDomain:
|
||||||
this.domainOpt = this.parseHostnameList(
|
this.domainOpt = this.parseHostnameList(
|
||||||
parser,
|
parser,
|
||||||
@ -2368,6 +2397,14 @@ const FilterParser = class {
|
|||||||
if ( this.redirect !== 0 ) { return false; }
|
if ( this.redirect !== 0 ) { return false; }
|
||||||
this.redirect = id === parser.OPTTokenRedirectRule ? 2 : 1;
|
this.redirect = id === parser.OPTTokenRedirectRule ? 2 : 1;
|
||||||
break;
|
break;
|
||||||
|
case parser.OPTTokenQueryprune:
|
||||||
|
this.modifyType = parser.OPTTokenQueryprune;
|
||||||
|
if ( val !== undefined ) {
|
||||||
|
this.modifyValue = val;
|
||||||
|
} else if ( this.action === AllowAction ) {
|
||||||
|
this.modifyValue = '';
|
||||||
|
}
|
||||||
|
break;
|
||||||
case parser.OPTTokenInvalid:
|
case parser.OPTTokenInvalid:
|
||||||
return false;
|
return false;
|
||||||
default:
|
default:
|
||||||
@ -2576,7 +2613,7 @@ const FilterParser = class {
|
|||||||
|
|
||||||
isJustOrigin() {
|
isJustOrigin() {
|
||||||
return this.isRegex === false &&
|
return this.isRegex === false &&
|
||||||
this.dataType === undefined &&
|
this.modifyType === undefined &&
|
||||||
this.denyallowOpt === '' &&
|
this.denyallowOpt === '' &&
|
||||||
this.domainOpt !== '' && (
|
this.domainOpt !== '' && (
|
||||||
this.pattern === '*' || (
|
this.pattern === '*' || (
|
||||||
@ -2728,7 +2765,7 @@ FilterContainer.prototype.freeze = function() {
|
|||||||
|
|
||||||
// Special cases: delegate to more specialized engines.
|
// Special cases: delegate to more specialized engines.
|
||||||
// Redirect engine.
|
// Redirect engine.
|
||||||
if ( (bits & 0x1F0) === redirectTypeValue ) {
|
if ( (bits & TypeBitsMask) === redirectTypeValue ) {
|
||||||
µb.redirectEngine.fromCompiledRule(args[1]);
|
µb.redirectEngine.fromCompiledRule(args[1]);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -2803,10 +2840,9 @@ FilterContainer.prototype.freeze = function() {
|
|||||||
this.badFilters.clear();
|
this.badFilters.clear();
|
||||||
this.goodFilters.clear();
|
this.goodFilters.clear();
|
||||||
|
|
||||||
// Skip 'data' type since bidi-trie does not (yet) support matchAll().
|
// Skip modify realm, since bidi-trie does not (yet) support matchAll().
|
||||||
const dataTypeValue = typeValueFromCatBits(typeNameToTypeValue['data']);
|
|
||||||
for ( const [ catBits, bucket ] of this.categories ) {
|
for ( const [ catBits, bucket ] of this.categories ) {
|
||||||
if ( typeValueFromCatBits(catBits) === dataTypeValue ) { continue; }
|
if ( (catBits & ActionBitsMask) === ModifyAction ) { continue; }
|
||||||
for ( const iunit of bucket.values() ) {
|
for ( const iunit of bucket.values() ) {
|
||||||
const f = units[iunit];
|
const f = units[iunit];
|
||||||
if ( f instanceof FilterBucket === false ) { continue; }
|
if ( f instanceof FilterBucket === false ) { continue; }
|
||||||
@ -2980,7 +3016,7 @@ FilterContainer.prototype.compile = function(parser, writer) {
|
|||||||
parsed.isPureHostname &&
|
parsed.isPureHostname &&
|
||||||
parsed.domainOpt === '' &&
|
parsed.domainOpt === '' &&
|
||||||
parsed.denyallowOpt === '' &&
|
parsed.denyallowOpt === '' &&
|
||||||
parsed.dataType === undefined
|
parsed.modifyType === undefined
|
||||||
) {
|
) {
|
||||||
parsed.tokenHash = this.dotTokenHash;
|
parsed.tokenHash = this.dotTokenHash;
|
||||||
this.compileToAtomicFilter(parsed, parsed.pattern, writer);
|
this.compileToAtomicFilter(parsed, parsed.pattern, writer);
|
||||||
@ -3064,8 +3100,10 @@ FilterContainer.prototype.compile = function(parser, writer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Data
|
// Data
|
||||||
if ( parsed.dataType !== undefined ) {
|
if ( parsed.modifyType !== undefined ) {
|
||||||
units.push(FilterDataHolder.compile(parsed));
|
units.push(FilterModifier.compile(parsed));
|
||||||
|
parsed.action = ModifyAction;
|
||||||
|
parsed.important = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const fdata = units.length === 1
|
const fdata = units.length === 1
|
||||||
@ -3108,7 +3146,7 @@ FilterContainer.prototype.compileToAtomicFilter = function(
|
|||||||
do {
|
do {
|
||||||
if ( typeBits & 1 ) {
|
if ( typeBits & 1 ) {
|
||||||
writer.push([
|
writer.push([
|
||||||
catBits | (bitOffset << 4),
|
catBits | (bitOffset << TypeBitsOffset),
|
||||||
parsed.tokenHash,
|
parsed.tokenHash,
|
||||||
fdata
|
fdata
|
||||||
]);
|
]);
|
||||||
@ -3154,70 +3192,113 @@ FilterContainer.prototype.fromCompiledContent = function(reader) {
|
|||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
FilterContainer.prototype.realmMatchAndFetchData = function(
|
// TODO:
|
||||||
realmBits,
|
// Evaluate converting redirect directives in redirect engine into
|
||||||
partyBits,
|
// modifiers in static network filtering engine.
|
||||||
type,
|
//
|
||||||
out
|
// Advantages: no more syntax quirks, gain all performance benefits, ability
|
||||||
|
// to reverse-lookup list of redirect directive in logger.
|
||||||
|
//
|
||||||
|
// Challenges: need to figure a way to calculate specificity so that the
|
||||||
|
// most specific redirect directive out of many can be
|
||||||
|
// identified (possibly based on total number of hostname labels
|
||||||
|
// seen at compile time).
|
||||||
|
|
||||||
|
FilterContainer.prototype.matchAndFetchModifiers = function(
|
||||||
|
fctxt,
|
||||||
|
modifierType
|
||||||
) {
|
) {
|
||||||
const bits01 = realmBits | typeNameToTypeValue.data;
|
|
||||||
const bits11 = realmBits | typeNameToTypeValue.data | partyBits;
|
|
||||||
|
|
||||||
const bucket01 = this.categories.get(bits01);
|
|
||||||
const bucket11 = partyBits !== 0
|
|
||||||
? this.categories.get(bits11)
|
|
||||||
: undefined;
|
|
||||||
|
|
||||||
if ( bucket01 === undefined && bucket11 === undefined ) { return false; }
|
|
||||||
|
|
||||||
const t = type, o = out; // to avoid jshint warning
|
|
||||||
const fdhr = (a, b, c) => new FilterDataHolderResult(a, b, c);
|
|
||||||
const units = filterUnits;
|
|
||||||
const tokenHashes = urlTokenizer.getTokens(bidiTrie);
|
|
||||||
let i = 0;
|
|
||||||
for (;;) {
|
|
||||||
const th = tokenHashes[i];
|
|
||||||
if ( th === 0 ) { return; }
|
|
||||||
$tokenBeg = tokenHashes[i+1];
|
|
||||||
if ( bucket01 !== undefined ) bucket01: {
|
|
||||||
const iunit = bucket01.get(th);
|
|
||||||
if ( iunit === undefined ) { break bucket01; }
|
|
||||||
units[iunit].matchAndFetchData(type, (f, i) => {
|
|
||||||
o.set(f.getData(t), fdhr(bits01, th, i || iunit));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if ( bucket11 !== undefined ) bucket11: {
|
|
||||||
const iunit = bucket11.get(th);
|
|
||||||
if ( iunit === undefined ) { break bucket11; }
|
|
||||||
units[iunit].matchAndFetchData(t, (f, i) => {
|
|
||||||
o.set(f.getData(t), fdhr(bits11, th, i || iunit));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
i += 2;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
FilterContainer.prototype.matchAndFetchData = function(fctxt, type) {
|
|
||||||
$requestURL = urlTokenizer.setURL(fctxt.url);
|
$requestURL = urlTokenizer.setURL(fctxt.url);
|
||||||
$docHostname = fctxt.getDocHostname();
|
$docHostname = fctxt.getDocHostname();
|
||||||
$docDomain = fctxt.getDocDomain();
|
$docDomain = fctxt.getDocDomain();
|
||||||
$docEntity.reset();
|
$docEntity.reset();
|
||||||
$requestHostname = fctxt.getHostname();
|
$requestHostname = fctxt.getHostname();
|
||||||
|
|
||||||
|
const typeBits = typeNameToTypeValue[fctxt.type] || otherTypeBitValue;
|
||||||
const partyBits = fctxt.is3rdPartyToDoc() ? ThirdParty : FirstParty;
|
const partyBits = fctxt.is3rdPartyToDoc() ? ThirdParty : FirstParty;
|
||||||
|
|
||||||
|
const catBits00 = ModifyAction;
|
||||||
|
const catBits01 = ModifyAction | typeBits;
|
||||||
|
const catBits10 = ModifyAction | partyBits;
|
||||||
|
const catBits11 = ModifyAction | typeBits | partyBits;
|
||||||
|
|
||||||
|
const bucket00 = this.categories.get(catBits00);
|
||||||
|
const bucket01 = typeBits !== 0
|
||||||
|
? this.categories.get(catBits01)
|
||||||
|
: undefined;
|
||||||
|
const bucket10 = partyBits !== 0
|
||||||
|
? this.categories.get(catBits10)
|
||||||
|
: undefined;
|
||||||
|
const bucket11 = typeBits !== 0 && partyBits !== 0
|
||||||
|
? this.categories.get(catBits11)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
|
const modifierResults = [];
|
||||||
|
const env = {
|
||||||
|
modifier: vAPI.StaticFilteringParser.netOptionTokenIds.get(modifierType) || 0,
|
||||||
|
bits: 0,
|
||||||
|
th: 0,
|
||||||
|
iunit: 0,
|
||||||
|
results: modifierResults,
|
||||||
|
};
|
||||||
|
|
||||||
|
const units = filterUnits;
|
||||||
|
const tokenHashes = urlTokenizer.getTokens(bidiTrie);
|
||||||
|
let i = 0;
|
||||||
|
for (;;) {
|
||||||
|
const th = tokenHashes[i];
|
||||||
|
if ( th === 0 ) { break; }
|
||||||
|
env.th = th;
|
||||||
|
$tokenBeg = tokenHashes[i+1];
|
||||||
|
if ( bucket00 !== undefined ) {
|
||||||
|
const iunit = bucket00.get(th);
|
||||||
|
if ( iunit !== undefined ) {
|
||||||
|
env.bits = catBits00; env.iunit = iunit;
|
||||||
|
units[iunit].matchAndFetchModifiers(env);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( bucket01 !== undefined ) {
|
||||||
|
const iunit = bucket01.get(th);
|
||||||
|
if ( iunit !== undefined ) {
|
||||||
|
env.bits = catBits01; env.iunit = iunit;
|
||||||
|
units[iunit].matchAndFetchModifiers(env);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( bucket10 !== undefined ) {
|
||||||
|
const iunit = bucket10.get(th);
|
||||||
|
if ( iunit !== undefined ) {
|
||||||
|
env.bits = catBits10; env.iunit = iunit;
|
||||||
|
units[iunit].matchAndFetchModifiers(env);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( bucket11 !== undefined ) {
|
||||||
|
const iunit = bucket11.get(th);
|
||||||
|
if ( iunit !== undefined ) {
|
||||||
|
env.bits = catBits11; env.iunit = iunit;
|
||||||
|
units[iunit].matchAndFetchModifiers(env);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i += 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( modifierResults.length === 0 ) { return; }
|
||||||
|
|
||||||
const toAddImportant = new Map();
|
const toAddImportant = new Map();
|
||||||
this.realmMatchAndFetchData(BlockImportant, partyBits, type, toAddImportant);
|
|
||||||
|
|
||||||
const toAdd = new Map();
|
const toAdd = new Map();
|
||||||
this.realmMatchAndFetchData(BlockAction, partyBits, type, toAdd);
|
|
||||||
|
|
||||||
if ( toAddImportant.size === 0 && toAdd.size === 0 ) { return []; }
|
|
||||||
|
|
||||||
const toRemove = new Map();
|
const toRemove = new Map();
|
||||||
this.realmMatchAndFetchData(AllowAction, partyBits, type, toRemove);
|
|
||||||
|
for ( const modifierResult of modifierResults ) {
|
||||||
|
const actionBits = modifierResult.bits & ActionBitsMask;
|
||||||
|
const modifyValue = modifierResult.modifier.value;
|
||||||
|
if ( actionBits === BlockImportant ) {
|
||||||
|
toAddImportant.set(modifyValue, modifierResult);
|
||||||
|
} else if ( actionBits === BlockAction ) {
|
||||||
|
toAdd.set(modifyValue, modifierResult);
|
||||||
|
} else {
|
||||||
|
toRemove.set(modifyValue, modifierResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( toAddImportant.size === 0 && toAdd.size === 0 ) { return; }
|
||||||
|
|
||||||
// Remove entries overriden by important block filters.
|
// Remove entries overriden by important block filters.
|
||||||
for ( const key of toAddImportant.keys() ) {
|
for ( const key of toAddImportant.keys() ) {
|
||||||
@ -3231,9 +3312,11 @@ FilterContainer.prototype.matchAndFetchData = function(fctxt, type) {
|
|||||||
if ( toRemove.has('') ) {
|
if ( toRemove.has('') ) {
|
||||||
if ( toAdd.size !== 0 ) {
|
if ( toAdd.size !== 0 ) {
|
||||||
toAdd.clear();
|
toAdd.clear();
|
||||||
toRemove.forEach((v, k, m) => {
|
if ( toRemove.size !== 1 ) {
|
||||||
if ( k !== '' ) { m.delete(k); }
|
const entry = toRemove.get('');
|
||||||
});
|
toRemove.clear();
|
||||||
|
toRemove.set('', entry);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
toRemove.clear();
|
toRemove.clear();
|
||||||
}
|
}
|
||||||
@ -3249,11 +3332,22 @@ FilterContainer.prototype.matchAndFetchData = function(fctxt, type) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Merge important and normal block filters
|
if (
|
||||||
for ( const [ key, entry ] of toAddImportant ) {
|
toAdd.size === 0 &&
|
||||||
toAdd.set(key, entry);
|
toAddImportant.size === 0 &&
|
||||||
|
toRemove.size === 0
|
||||||
|
) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return Array.from(toAdd.values()).concat(Array.from(toRemove.values()));
|
|
||||||
|
const out = Array.from(toAdd.values());
|
||||||
|
if ( toAddImportant.size !== 0 ) {
|
||||||
|
out.push(...toAddImportant.values());
|
||||||
|
}
|
||||||
|
if ( toRemove.size !== 0 ) {
|
||||||
|
out.push(...toRemove.values());
|
||||||
|
}
|
||||||
|
return out;
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
@ -3460,6 +3554,46 @@ FilterContainer.prototype.matchString = function(fctxt, modifiers = 0) {
|
|||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
|
FilterContainer.prototype.filterQuery = function(fctxt) {
|
||||||
|
const directives = this.matchAndFetchModifiers(fctxt, 'queryprune');
|
||||||
|
if ( directives === undefined ) { return; }
|
||||||
|
const params = [];
|
||||||
|
const out = [];
|
||||||
|
const url = new URL(fctxt.url);
|
||||||
|
for ( const directive of directives ) {
|
||||||
|
const modifier = directive.modifier;
|
||||||
|
if ( modifier.cache === undefined ) {
|
||||||
|
let retext = modifier.value;
|
||||||
|
if ( retext.startsWith('|') ) { retext = `^${retext.slice(1)}`; }
|
||||||
|
if ( retext.endsWith('|') ) { retext = `${retext.slice(0,-1)}$`; }
|
||||||
|
modifier.cache = new RegExp(retext);
|
||||||
|
}
|
||||||
|
const re = modifier.cache;
|
||||||
|
let filtered = false;
|
||||||
|
for ( const [ key, value ] of url.searchParams ) {
|
||||||
|
if ( re.test(`${key}=${value}`) ) {
|
||||||
|
filtered = true;
|
||||||
|
} else {
|
||||||
|
params.push(`${key}=${encodeURIComponent(value)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( filtered ) {
|
||||||
|
out.push(directive);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( out.length === 0 ) { return; }
|
||||||
|
url.search = params.length !== 0 ? `?${params.join('&')}` : '';
|
||||||
|
fctxt.redirectURL = url.href;
|
||||||
|
return out;
|
||||||
|
};
|
||||||
|
|
||||||
|
FilterContainer.prototype.hasQuery = function(fctxt) {
|
||||||
|
urlTokenizer.setURL(fctxt.url);
|
||||||
|
return urlTokenizer.hasQuery();
|
||||||
|
};
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
FilterContainer.prototype.toLogData = function() {
|
FilterContainer.prototype.toLogData = function() {
|
||||||
if ( this.$filterUnit === 0 ) { return; }
|
if ( this.$filterUnit === 0 ) { return; }
|
||||||
const logData = toLogDataInternal(
|
const logData = toLogDataInternal(
|
||||||
@ -3471,7 +3605,7 @@ FilterContainer.prototype.toLogData = function() {
|
|||||||
logData.tokenHash = this.$tokenHash;
|
logData.tokenHash = this.$tokenHash;
|
||||||
logData.result = this.$filterUnit === 0
|
logData.result = this.$filterUnit === 0
|
||||||
? 0
|
? 0
|
||||||
: ((this.$catBits & 1) !== 0 ? 2 : 1);
|
: ((this.$catBits & AllowAction) !== 0 ? 2 : 1);
|
||||||
return logData;
|
return logData;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -3505,8 +3639,9 @@ FilterContainer.prototype.benchmark = async function(action, target) {
|
|||||||
const requests = await µb.loadBenchmarkDataset();
|
const requests = await µb.loadBenchmarkDataset();
|
||||||
|
|
||||||
if ( Array.isArray(requests) === false || requests.length === 0 ) {
|
if ( Array.isArray(requests) === false || requests.length === 0 ) {
|
||||||
console.info('No requests found to benchmark');
|
const text = 'No dataset found to benchmark';
|
||||||
return;
|
console.info(text);
|
||||||
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
const print = log.print;
|
const print = log.print;
|
||||||
@ -3560,8 +3695,11 @@ FilterContainer.prototype.benchmark = async function(action, target) {
|
|||||||
print(`\turl=${fctxt.url}`);
|
print(`\turl=${fctxt.url}`);
|
||||||
print(`\tdocOrigin=${fctxt.getDocOrigin()}`);
|
print(`\tdocOrigin=${fctxt.getDocOrigin()}`);
|
||||||
}
|
}
|
||||||
if ( fctxt.type === 'main_frame' || fctxt.type === 'sub_frame' ) {
|
if ( r !== 1 && this.hasQuery(fctxt) ) {
|
||||||
this.matchAndFetchData(fctxt, 'csp');
|
this.filterQuery(fctxt, 'queryprune');
|
||||||
|
}
|
||||||
|
if ( r !== 1 && fctxt.type === 'main_frame' || fctxt.type === 'sub_frame' ) {
|
||||||
|
this.matchAndFetchModifiers(fctxt, 'csp');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const t1 = self.performance.now();
|
const t1 = self.performance.now();
|
||||||
@ -3687,7 +3825,7 @@ FilterContainer.prototype.bucketHistogram = function() {
|
|||||||
"FilterAnchorHn" => 1453}
|
"FilterAnchorHn" => 1453}
|
||||||
"FilterOriginMiss" => 730}
|
"FilterOriginMiss" => 730}
|
||||||
"FilterPatternGeneric" => 601}
|
"FilterPatternGeneric" => 601}
|
||||||
"FilterDataHolder" => 404}
|
"FilterModifier" => 404}
|
||||||
"FilterOriginMissSet" => 316}
|
"FilterOriginMissSet" => 316}
|
||||||
"FilterTrailingSeparator" => 235}
|
"FilterTrailingSeparator" => 235}
|
||||||
"FilterAnchorRight" => 174}
|
"FilterAnchorRight" => 174}
|
||||||
|
@ -64,7 +64,7 @@ const onBeforeRequest = function(details) {
|
|||||||
// https://github.com/chrisaljoudi/uBlock/issues/1001
|
// https://github.com/chrisaljoudi/uBlock/issues/1001
|
||||||
// This must be executed regardless of whether the request is
|
// This must be executed regardless of whether the request is
|
||||||
// behind-the-scene
|
// behind-the-scene
|
||||||
if ( details.type === 'main_frame' ) {
|
if ( fctxt.itype === fctxt.MAIN_FRAME ) {
|
||||||
return onBeforeRootFrameRequest(fctxt);
|
return onBeforeRootFrameRequest(fctxt);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,33 +94,31 @@ const onBeforeRequest = function(details) {
|
|||||||
fctxt.setRealm('network').toLogger();
|
fctxt.setRealm('network').toLogger();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not blocked
|
// Redirected
|
||||||
if ( result !== 1 ) {
|
|
||||||
if (
|
if ( fctxt.redirectURL !== undefined ) {
|
||||||
details.parentFrameId !== -1 &&
|
return { redirectUrl: fctxt.redirectURL };
|
||||||
details.type === 'sub_frame' &&
|
|
||||||
details.aliasURL === undefined
|
|
||||||
) {
|
|
||||||
pageStore.setFrameURL(details.frameId, details.url);
|
|
||||||
}
|
|
||||||
if ( result === 2 ) {
|
|
||||||
return { cancel: false };
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Blocked
|
// Not redirected
|
||||||
|
|
||||||
if ( fctxt.redirectURL === undefined ) {
|
// Blocked
|
||||||
|
if ( result === 1 ) {
|
||||||
return { cancel: true };
|
return { cancel: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( µb.logger.enabled ) {
|
// Not blocked
|
||||||
fctxt.setRealm('redirect')
|
if (
|
||||||
.setFilter({ source: 'redirect', raw: µb.redirectEngine.resourceNameRegister })
|
fctxt.itype === fctxt.SUB_FRAME &&
|
||||||
.toLogger();
|
details.parentFrameId !== -1 &&
|
||||||
|
details.aliasURL === undefined
|
||||||
|
) {
|
||||||
|
pageStore.setFrameURL(details.frameId, details.url);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( result === 2 ) {
|
||||||
|
return { cancel: false };
|
||||||
}
|
}
|
||||||
return { redirectUrl: fctxt.redirectURL };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
@ -134,14 +132,14 @@ const onBeforeRootFrameRequest = function(fctxt) {
|
|||||||
// This must be executed regardless of whether the request is
|
// This must be executed regardless of whether the request is
|
||||||
// behind-the-scene
|
// behind-the-scene
|
||||||
const requestHostname = fctxt.getHostname();
|
const requestHostname = fctxt.getHostname();
|
||||||
const logEnabled = µb.logger.enabled;
|
const loggerEnabled = µb.logger.enabled;
|
||||||
let result = 0,
|
let result = 0;
|
||||||
logData;
|
let logData;
|
||||||
|
|
||||||
// If the site is whitelisted, disregard strict blocking
|
// If the site is whitelisted, disregard strict blocking
|
||||||
if ( µb.getNetFilteringSwitch(requestURL) === false ) {
|
if ( µb.getNetFilteringSwitch(requestURL) === false ) {
|
||||||
result = 2;
|
result = 2;
|
||||||
if ( logEnabled ) {
|
if ( loggerEnabled ) {
|
||||||
logData = { engine: 'u', result: 2, raw: 'whitelisted' };
|
logData = { engine: 'u', result: 2, raw: 'whitelisted' };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -152,16 +150,24 @@ const onBeforeRootFrameRequest = function(fctxt) {
|
|||||||
µb.sessionSwitches.evaluateZ('no-strict-blocking', requestHostname)
|
µb.sessionSwitches.evaluateZ('no-strict-blocking', requestHostname)
|
||||||
) {
|
) {
|
||||||
result = 2;
|
result = 2;
|
||||||
if ( logEnabled ) {
|
if ( loggerEnabled ) {
|
||||||
logData = { engine: 'u', result: 2, raw: 'no-strict-blocking: ' + µb.sessionSwitches.z + ' true' };
|
logData = {
|
||||||
|
engine: 'u',
|
||||||
|
result: 2,
|
||||||
|
raw: `no-strict-blocking: ${µb.sessionSwitches.z} true`
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Temporarily whitelisted?
|
// Temporarily whitelisted?
|
||||||
if ( result === 0 && strictBlockBypasser.isBypassed(requestHostname) ) {
|
if ( result === 0 && strictBlockBypasser.isBypassed(requestHostname) ) {
|
||||||
result = 2;
|
result = 2;
|
||||||
if ( logEnabled ) {
|
if ( loggerEnabled ) {
|
||||||
logData = { engine: 'u', result: 2, raw: 'no-strict-blocking: true (temporary)' };
|
logData = {
|
||||||
|
engine: 'u',
|
||||||
|
result: 2,
|
||||||
|
raw: 'no-strict-blocking: true (temporary)'
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,9 +176,8 @@ const onBeforeRootFrameRequest = function(fctxt) {
|
|||||||
|
|
||||||
// Check for specific block
|
// Check for specific block
|
||||||
if ( result === 0 ) {
|
if ( result === 0 ) {
|
||||||
fctxt.type = 'main_frame';
|
|
||||||
result = snfe.matchString(fctxt, 0b0001);
|
result = snfe.matchString(fctxt, 0b0001);
|
||||||
if ( result !== 0 || logEnabled ) {
|
if ( result !== 0 || loggerEnabled ) {
|
||||||
logData = snfe.toLogData();
|
logData = snfe.toLogData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -181,14 +186,14 @@ const onBeforeRootFrameRequest = function(fctxt) {
|
|||||||
if ( result === 0 ) {
|
if ( result === 0 ) {
|
||||||
fctxt.type = 'no_type';
|
fctxt.type = 'no_type';
|
||||||
result = snfe.matchString(fctxt, 0b0001);
|
result = snfe.matchString(fctxt, 0b0001);
|
||||||
if ( result !== 0 || logEnabled ) {
|
if ( result !== 0 || loggerEnabled ) {
|
||||||
logData = snfe.toLogData();
|
logData = snfe.toLogData();
|
||||||
}
|
}
|
||||||
// https://github.com/chrisaljoudi/uBlock/issues/1128
|
// https://github.com/chrisaljoudi/uBlock/issues/1128
|
||||||
// Do not block if the match begins after the hostname, except when
|
// Do not block if the match begins after the hostname, except when
|
||||||
// the filter is specifically of type `other`.
|
// the filter is specifically of type `other`.
|
||||||
// https://github.com/gorhill/uBlock/issues/490
|
// https://github.com/gorhill/uBlock/issues/490
|
||||||
// Removing this for the time being, will need a new, dedicated type.
|
// Removing this for the time being, will need a new, dedicated type.
|
||||||
if (
|
if (
|
||||||
result === 1 &&
|
result === 1 &&
|
||||||
toBlockDocResult(requestURL, requestHostname, logData) === false
|
toBlockDocResult(requestURL, requestHostname, logData) === false
|
||||||
@ -196,21 +201,43 @@ const onBeforeRootFrameRequest = function(fctxt) {
|
|||||||
result = 0;
|
result = 0;
|
||||||
logData = undefined;
|
logData = undefined;
|
||||||
}
|
}
|
||||||
|
fctxt.type = 'main_frame';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log
|
|
||||||
fctxt.type = 'main_frame';
|
|
||||||
const pageStore = µb.bindTabToPageStats(fctxt.tabId, 'beforeRequest');
|
const pageStore = µb.bindTabToPageStats(fctxt.tabId, 'beforeRequest');
|
||||||
if ( pageStore ) {
|
if ( pageStore ) {
|
||||||
pageStore.journalAddRootFrame('uncommitted', requestURL);
|
pageStore.journalAddRootFrame('uncommitted', requestURL);
|
||||||
pageStore.journalAddRequest(requestHostname, result);
|
pageStore.journalAddRequest(requestHostname, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( logEnabled ) {
|
// Log
|
||||||
fctxt.setRealm('network').setFilter(logData).toLogger();
|
if ( loggerEnabled ) {
|
||||||
|
fctxt.setRealm('network').setFilter(logData);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modifier(s)?
|
||||||
|
// A modifier is an action which transform the original network request.
|
||||||
|
// https://github.com/uBlockOrigin/uBlock-issues/issues/760
|
||||||
|
// Redirect non-blocked request?
|
||||||
|
if ( result === 0 && snfe.hasQuery(fctxt) ) {
|
||||||
|
const directives = snfe.filterQuery(fctxt);
|
||||||
|
if ( directives !== undefined && loggerEnabled ) {
|
||||||
|
fctxt.pushFilters(directives.map(a => a.logData()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( loggerEnabled ) {
|
||||||
|
fctxt.setRealm('network').toLogger();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Redirected
|
||||||
|
|
||||||
|
if ( fctxt.redirectURL !== undefined ) {
|
||||||
|
return { redirectUrl: fctxt.redirectURL };
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not blocked
|
// Not blocked
|
||||||
|
|
||||||
if ( result !== 1 ) { return; }
|
if ( result !== 1 ) { return; }
|
||||||
|
|
||||||
// No log data means no strict blocking (because we need to report why
|
// No log data means no strict blocking (because we need to report why
|
||||||
@ -218,6 +245,7 @@ const onBeforeRootFrameRequest = function(fctxt) {
|
|||||||
if ( logData === undefined ) { return; }
|
if ( logData === undefined ) { return; }
|
||||||
|
|
||||||
// Blocked
|
// Blocked
|
||||||
|
|
||||||
const query = encodeURIComponent(JSON.stringify({
|
const query = encodeURIComponent(JSON.stringify({
|
||||||
url: requestURL,
|
url: requestURL,
|
||||||
hn: requestHostname,
|
hn: requestHostname,
|
||||||
@ -307,7 +335,7 @@ const onBeforeBehindTheSceneRequest = function(fctxt) {
|
|||||||
fctxt.tabOrigin.endsWith('-scheme') === false &&
|
fctxt.tabOrigin.endsWith('-scheme') === false &&
|
||||||
µb.URI.isNetworkURI(fctxt.tabOrigin) ||
|
µb.URI.isNetworkURI(fctxt.tabOrigin) ||
|
||||||
µb.userSettings.advancedUserEnabled ||
|
µb.userSettings.advancedUserEnabled ||
|
||||||
fctxt.type === 'csp_report'
|
fctxt.itype === fctxt.CSP_REPORT
|
||||||
) {
|
) {
|
||||||
result = pageStore.filterRequest(fctxt);
|
result = pageStore.filterRequest(fctxt);
|
||||||
|
|
||||||
@ -440,9 +468,7 @@ const onHeadersReceived = function(details) {
|
|||||||
|
|
||||||
const µb = µBlock;
|
const µb = µBlock;
|
||||||
const fctxt = µb.filteringContext.fromWebrequestDetails(details);
|
const fctxt = µb.filteringContext.fromWebrequestDetails(details);
|
||||||
const requestType = fctxt.type;
|
const isRootDoc = fctxt.itype === fctxt.MAIN_FRAME;
|
||||||
const isRootDoc = requestType === 'main_frame';
|
|
||||||
const isDoc = isRootDoc || requestType === 'sub_frame';
|
|
||||||
|
|
||||||
let pageStore = µb.pageStoreFromTabId(fctxt.tabId);
|
let pageStore = µb.pageStoreFromTabId(fctxt.tabId);
|
||||||
if ( pageStore === null ) {
|
if ( pageStore === null ) {
|
||||||
@ -451,11 +477,11 @@ const onHeadersReceived = function(details) {
|
|||||||
}
|
}
|
||||||
if ( pageStore.getNetFilteringSwitch(fctxt) === false ) { return; }
|
if ( pageStore.getNetFilteringSwitch(fctxt) === false ) { return; }
|
||||||
|
|
||||||
if ( requestType === 'image' || requestType === 'media' ) {
|
if ( fctxt.itype === fctxt.IMAGE || fctxt.itype === fctxt.MEDIA ) {
|
||||||
return foilLargeMediaElement(details, fctxt, pageStore);
|
return foilLargeMediaElement(details, fctxt, pageStore);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( isDoc === false ) { return; }
|
if ( isRootDoc === false && fctxt.itype !== fctxt.SUB_FRAME ) { return; }
|
||||||
|
|
||||||
// Keep in mind response headers will be modified in-place if needed, so
|
// Keep in mind response headers will be modified in-place if needed, so
|
||||||
// `details.responseHeaders` will always point to the modified response
|
// `details.responseHeaders` will always point to the modified response
|
||||||
@ -801,6 +827,7 @@ const injectCSP = function(fctxt, pageStore, responseHeaders) {
|
|||||||
const µb = µBlock;
|
const µb = µBlock;
|
||||||
const loggerEnabled = µb.logger.enabled;
|
const loggerEnabled = µb.logger.enabled;
|
||||||
const cspSubsets = [];
|
const cspSubsets = [];
|
||||||
|
const requestType = fctxt.type;
|
||||||
|
|
||||||
// Start collecting policies >>>>>>>>
|
// Start collecting policies >>>>>>>>
|
||||||
|
|
||||||
@ -849,11 +876,14 @@ const injectCSP = function(fctxt, pageStore, responseHeaders) {
|
|||||||
|
|
||||||
// Static filtering.
|
// Static filtering.
|
||||||
|
|
||||||
|
fctxt.type = requestType;
|
||||||
const staticDirectives =
|
const staticDirectives =
|
||||||
µb.staticNetFilteringEngine.matchAndFetchData(fctxt, 'csp');
|
µb.staticNetFilteringEngine.matchAndFetchModifiers(fctxt, 'csp');
|
||||||
for ( const directive of staticDirectives ) {
|
if ( staticDirectives !== undefined ) {
|
||||||
if ( directive.result !== 1 ) { continue; }
|
for ( const directive of staticDirectives ) {
|
||||||
cspSubsets.push(directive.getData('csp'));
|
if ( directive.result !== 1 ) { continue; }
|
||||||
|
cspSubsets.push(directive.modifier.value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// URL filtering `allow` rules override static filtering.
|
// URL filtering `allow` rules override static filtering.
|
||||||
@ -897,11 +927,10 @@ const injectCSP = function(fctxt, pageStore, responseHeaders) {
|
|||||||
|
|
||||||
// Static CSP policies will be applied.
|
// Static CSP policies will be applied.
|
||||||
|
|
||||||
if ( loggerEnabled && staticDirectives.length !== 0 ) {
|
if ( loggerEnabled && staticDirectives !== undefined ) {
|
||||||
fctxt.setRealm('network').setType('csp');
|
fctxt.setRealm('network')
|
||||||
for ( const directive of staticDirectives ) {
|
.pushFilters(staticDirectives.map(a => a.logData()))
|
||||||
fctxt.setFilter(directive.logData()).toLogger();
|
.toLogger();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( cspSubsets.length === 0 ) { return; }
|
if ( cspSubsets.length === 0 ) { return; }
|
||||||
|
@ -61,6 +61,7 @@
|
|||||||
this._urlIn = '';
|
this._urlIn = '';
|
||||||
this._urlOut = '';
|
this._urlOut = '';
|
||||||
this._tokenized = false;
|
this._tokenized = false;
|
||||||
|
this._hasQuery = 0;
|
||||||
// https://www.reddit.com/r/uBlockOrigin/comments/dzw57l/
|
// https://www.reddit.com/r/uBlockOrigin/comments/dzw57l/
|
||||||
// Remember: 1 token needs two slots
|
// Remember: 1 token needs two slots
|
||||||
this._tokens = new Uint32Array(2064);
|
this._tokens = new Uint32Array(2064);
|
||||||
@ -74,6 +75,7 @@
|
|||||||
if ( url !== this._urlIn ) {
|
if ( url !== this._urlIn ) {
|
||||||
this._urlIn = url;
|
this._urlIn = url;
|
||||||
this._urlOut = url.toLowerCase();
|
this._urlOut = url.toLowerCase();
|
||||||
|
this._hasQuery = 0;
|
||||||
this._tokenized = false;
|
this._tokenized = false;
|
||||||
}
|
}
|
||||||
return this._urlOut;
|
return this._urlOut;
|
||||||
@ -115,6 +117,14 @@
|
|||||||
return this._tokens;
|
return this._tokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hasQuery() {
|
||||||
|
if ( this._hasQuery === 0 ) {
|
||||||
|
const i = this._urlOut.indexOf('?');
|
||||||
|
this._hasQuery = i !== -1 ? i + 1 : -1;
|
||||||
|
}
|
||||||
|
return this._hasQuery > 0;
|
||||||
|
}
|
||||||
|
|
||||||
tokenHashFromString(s) {
|
tokenHashFromString(s) {
|
||||||
const l = s.length;
|
const l = s.length;
|
||||||
if ( l === 0 ) { return this.emptyTokenHash; }
|
if ( l === 0 ) { return this.emptyTokenHash; }
|
||||||
@ -155,33 +165,47 @@
|
|||||||
l = 2048;
|
l = 2048;
|
||||||
}
|
}
|
||||||
encodeInto.haystackLen = l;
|
encodeInto.haystackLen = l;
|
||||||
const knownTokens = this.knownTokens;
|
let j = 0;
|
||||||
const vtc = this._validTokenChars;
|
let hasq = -1;
|
||||||
const charCodes = encodeInto.haystack;
|
mainLoop: {
|
||||||
let i = 0, j = 0, n, ti, th;
|
const knownTokens = this.knownTokens;
|
||||||
for (;;) {
|
const vtc = this._validTokenChars;
|
||||||
|
const charCodes = encodeInto.haystack;
|
||||||
|
let i = 0, n = 0, ti = 0, th = 0;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if ( i === l ) { return j; }
|
for (;;) {
|
||||||
th = vtc[(charCodes[i] = url.charCodeAt(i))];
|
if ( i === l ) { break mainLoop; }
|
||||||
i += 1;
|
const cc = url.charCodeAt(i);
|
||||||
if ( th !== 0 ) { break; }
|
charCodes[i] = cc;
|
||||||
}
|
i += 1;
|
||||||
ti = i - 1; n = 1;
|
th = vtc[cc];
|
||||||
for (;;) {
|
if ( th !== 0 ) { break; }
|
||||||
if ( i === l ) { break; }
|
if ( cc === 0x3F /* '?' */ ) { hasq = i; }
|
||||||
const v = vtc[(charCodes[i] = url.charCodeAt(i))];
|
}
|
||||||
i += 1;
|
ti = i - 1; n = 1;
|
||||||
if ( v === 0 ) { break; }
|
for (;;) {
|
||||||
if ( n === 7 ) { continue; }
|
if ( i === l ) { break; }
|
||||||
th = th << 4 ^ v;
|
const cc = url.charCodeAt(i);
|
||||||
n += 1;
|
charCodes[i] = cc;
|
||||||
}
|
i += 1;
|
||||||
if ( knownTokens[th & 0xFFFF ^ th >>> 16] !== 0 ) {
|
const v = vtc[cc];
|
||||||
tokens[j+0] = th;
|
if ( v === 0 ) {
|
||||||
tokens[j+1] = ti;
|
if ( cc === 0x3F /* '?' */ ) { hasq = i; }
|
||||||
j += 2;
|
break;
|
||||||
|
}
|
||||||
|
if ( n === 7 ) { continue; }
|
||||||
|
th = th << 4 ^ v;
|
||||||
|
n += 1;
|
||||||
|
}
|
||||||
|
if ( knownTokens[th & 0xFFFF ^ th >>> 16] !== 0 ) {
|
||||||
|
tokens[j+0] = th;
|
||||||
|
tokens[j+1] = ti;
|
||||||
|
j += 2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this._hasQuery = hasq;
|
||||||
|
return j;
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
@ -58,11 +58,11 @@
|
|||||||
<input type="search" placeholder="logFilterPrompt">
|
<input type="search" placeholder="logFilterPrompt">
|
||||||
<span id="filterExprButton" class="button fa-icon expanded" data-i18n-title="loggerRowFiltererBuiltinTip">angle-up</span>
|
<span id="filterExprButton" class="button fa-icon expanded" data-i18n-title="loggerRowFiltererBuiltinTip">angle-up</span>
|
||||||
<div id="filterExprPicker">
|
<div id="filterExprPicker">
|
||||||
<div><span data-filtex="!" data-i18n="loggerRowFiltererBuiltinNot"></span><span data-filtex="\t--\t|\t<<\t|\t##" data-i18n="loggerRowFiltererBuiltinBlocked"></span><span data-filtex="\t\+\+\t|\t\*\*\t|\t#@#" data-i18n="loggerRowFiltererBuiltinAllowed"></span></div>
|
<div><span data-filtex="!" data-i18n="loggerRowFiltererBuiltinNot"></span><span data-filtex="\t--\t|\t<<\t|\t##" data-i18n="loggerRowFiltererBuiltinBlocked"></span><span data-filtex="\t\+\+\t|\t\*\*\t|\t#@#" data-i18n="loggerRowFiltererBuiltinAllowed"></span><span data-filtex="[$,](?:csp|queryprune)=|\t\<\<\t">modified</span></div>
|
||||||
<div><span data-filtex="!" data-i18n="loggerRowFiltererBuiltinNot"></span>
|
<div><span data-filtex="!" data-i18n="loggerRowFiltererBuiltinNot"></span>
|
||||||
<span style="flex-direction: column;">
|
<span style="flex-direction: column;">
|
||||||
<div style="margin-bottom: 1px;"><span data-filtex="\t(?:css|(?:inline-)?font)\t">css/font</span><span data-filtex="\timage\t">image</span><span data-filtex="\tmedia\t">media</span><span data-filtex="\t(?:inline-)?script(?:ing)?\t">script</span></div>
|
<div style="margin-bottom: 1px;"><span data-filtex="\t(?:css|(?:inline-)?font)\t">css/font</span><span data-filtex="\timage\t">image</span><span data-filtex="\tmedia\t">media</span><span data-filtex="\t(?:inline-)?script(?:ing)?\t">script</span></div>
|
||||||
<div><span data-filtex="\t(?:websocket|xhr)\t">xhr</span><span data-filtex="\tframe\t">frame</span><span data-filtex="\t(?:dom|g(?:eneric)?hide|s(?:pecific)?hide)\t">dom</span><span data-filtex="\t(?:beacon|csp|csp_report|doc|ping|popup|popunder|other)\t">other</span></div>
|
<div><span data-filtex="\t(?:websocket|xhr)\t">xhr</span><span data-filtex="\tframe\t">frame</span><span data-filtex="\t(?:dom|g(?:eneric)?hide|s(?:pecific)?hide)\t">dom</span><span data-filtex="\t(?:beacon|csp_report|doc|ping|popup|popunder|other)\t">other</span></div>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div><span data-filtex="!" data-i18n="loggerRowFiltererBuiltinNot"></span><span data-filtex="\t(?:0,)?1\t" data-i18n="loggerRowFiltererBuiltin1p"></span><span data-filtex="\t(?:3(?:,\d)?|0,3)\t" data-i18n="loggerRowFiltererBuiltin3p"></span></div>
|
<div><span data-filtex="!" data-i18n="loggerRowFiltererBuiltinNot"></span><span data-filtex="\t(?:0,)?1\t" data-i18n="loggerRowFiltererBuiltin1p"></span><span data-filtex="\t(?:3(?:,\d)?|0,3)\t" data-i18n="loggerRowFiltererBuiltin3p"></span></div>
|
||||||
|
Loading…
Reference in New Issue
Block a user