1
0
mirror of https://github.com/gorhill/uBlock.git synced 2024-10-06 09:37:12 +02:00

Add support for AdGuard's empty option

Related issue:
- https://github.com/uBlockOrigin/uBlock-issues/issues/701

The filter option `empty` is converted to `redirect=empty`
by uBO internally; however unlike when the `redirect=`
option is used expressly, the `empty` option does not
require a resource type.

When `empty` is used, only network requests which are meant
to return a text response will be redirected to an empty
response body by uBO -- so `empty` will not work for
resources such as images, media, or other binary resources.
This commit is contained in:
Raymond Hill 2019-08-13 08:16:21 -04:00
parent 2c39a1af02
commit 3e5c9e00ab
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
3 changed files with 138 additions and 83 deletions

View File

@ -56,15 +56,15 @@ const redirectableResources = new Map([
[ 'addthis_widget.js', { [ 'addthis_widget.js', {
alias: 'addthis.com/addthis_widget.js', alias: 'addthis.com/addthis_widget.js',
} ], } ],
[ 'amazon_ads.js', {
alias: 'amazon-adsystem.com/aax2/amzn_ads.js',
} ],
[ 'ampproject_v0.js', { [ 'ampproject_v0.js', {
alias: 'ampproject.org/v0.js', alias: 'ampproject.org/v0.js',
} ], } ],
[ 'chartbeat.js', { [ 'chartbeat.js', {
alias: 'static.chartbeat.com/chartbeat.js', alias: 'static.chartbeat.com/chartbeat.js',
} ], } ],
[ 'amazon_ads.js', {
alias: 'amazon-adsystem.com/aax2/amzn_ads.js',
} ],
[ 'disqus_embed.js', { [ 'disqus_embed.js', {
alias: 'disqus.com/embed.js', alias: 'disqus.com/embed.js',
} ], } ],
@ -74,6 +74,9 @@ const redirectableResources = new Map([
[ 'doubleclick_instream_ad_status.js', { [ 'doubleclick_instream_ad_status.js', {
alias: 'doubleclick.net/instream/ad_status.js', alias: 'doubleclick.net/instream/ad_status.js',
} ], } ],
[ 'empty', {
data: 'text', // Important!
} ],
[ 'google-analytics_analytics.js', { [ 'google-analytics_analytics.js', {
alias: 'google-analytics.com/analytics.js', alias: 'google-analytics.com/analytics.js',
} ], } ],
@ -165,6 +168,15 @@ const extToMimeMap = new Map([
[ 'txt', 'text/plain' ], [ 'txt', 'text/plain' ],
]); ]);
const typeToMimeMap = new Map([
[ 'main_frame', 'text/html' ],
[ 'other', 'text/plain' ],
[ 'script', 'application/javascript' ],
[ 'stylesheet', 'text/css' ],
[ 'sub_frame', 'text/html' ],
[ 'xmlhttprequest', 'text/plain' ],
]);
const validMimes = new Set(extToMimeMap.values()); const validMimes = new Set(extToMimeMap.values());
const mimeFromName = function(name) { const mimeFromName = function(name) {
@ -177,13 +189,12 @@ const mimeFromName = function(name) {
/******************************************************************************/ /******************************************************************************/
/******************************************************************************/ /******************************************************************************/
const RedirectEntry = function() { const RedirectEntry = class {
constructor() {
this.mime = ''; this.mime = '';
this.data = ''; this.data = '';
this.warURL = undefined; this.warURL = undefined;
}; }
/******************************************************************************/
// Prevent redirection to web accessible resources when the request is // Prevent redirection to web accessible resources when the request is
// of type 'xmlhttprequest', because XMLHttpRequest.responseURL would // of type 'xmlhttprequest', because XMLHttpRequest.responseURL would
@ -191,7 +202,7 @@ const RedirectEntry = function() {
// - https://stackoverflow.com/a/8056313 // - https://stackoverflow.com/a/8056313
// - https://bugzilla.mozilla.org/show_bug.cgi?id=998076 // - https://bugzilla.mozilla.org/show_bug.cgi?id=998076
RedirectEntry.prototype.toURL = function(fctxt, asDataURI = false) { toURL(fctxt, asDataURI = false) {
if ( if (
this.warURL !== undefined && this.warURL !== undefined &&
asDataURI !== true && asDataURI !== true &&
@ -201,15 +212,19 @@ RedirectEntry.prototype.toURL = function(fctxt, asDataURI = false) {
return `${this.warURL}${vAPI.warSecret()}`; return `${this.warURL}${vAPI.warSecret()}`;
} }
if ( this.data === undefined ) { return; } if ( this.data === undefined ) { return; }
// https://github.com/uBlockOrigin/uBlock-issues/issues/701
if ( this.data === '' ) {
const mime = typeToMimeMap.get(fctxt.type);
if ( mime === undefined ) { return; }
return `data:${mime},`;
}
if ( this.data.startsWith('data:') === false ) { if ( this.data.startsWith('data:') === false ) {
this.data = `data:${this.mime};base64,${btoa(this.data)}`; this.data = `data:${this.mime};base64,${btoa(this.data)}`;
} }
return this.data; return this.data;
}; }
/******************************************************************************/ toContent() {
RedirectEntry.prototype.toContent = function() {
if ( this.data.startsWith('data:') ) { if ( this.data.startsWith('data:') ) {
const pos = this.data.indexOf(','); const pos = this.data.indexOf(',');
const base64 = this.data.endsWith(';base64', pos); const base64 = this.data.endsWith(';base64', pos);
@ -219,25 +234,22 @@ RedirectEntry.prototype.toContent = function() {
} }
} }
return this.data; return this.data;
}; }
/******************************************************************************/ static fromContent(mime, content) {
RedirectEntry.fromContent = function(mime, content) {
const r = new RedirectEntry(); const r = new RedirectEntry();
r.mime = mime; r.mime = mime;
r.data = content; r.data = content;
return r; return r;
}; }
/******************************************************************************/ static fromSelfie(selfie) {
RedirectEntry.fromSelfie = function(selfie) {
const r = new RedirectEntry(); const r = new RedirectEntry();
r.mime = selfie.mime; r.mime = selfie.mime;
r.data = selfie.data; r.data = selfie.data;
r.warURL = selfie.warURL; r.warURL = selfie.warURL;
return r; return r;
}
}; };
/******************************************************************************/ /******************************************************************************/
@ -248,7 +260,13 @@ const RedirectEngine = function() {
this.resources = new Map(); this.resources = new Map();
this.reset(); this.reset();
this.resourceNameRegister = ''; this.resourceNameRegister = '';
this._desAll = []; // re-use better than re-allocate
// Internal use
this._missedQueryHash = '';
this._src = '';
this._srcAll = [ '*' ];
this._des = '';
this._desAll = [ '*' ];
}; };
/******************************************************************************/ /******************************************************************************/
@ -278,35 +296,60 @@ RedirectEngine.prototype.toBroaderHostname = function(hostname) {
/******************************************************************************/ /******************************************************************************/
RedirectEngine.prototype.decomposeHostname = function(hn, dict, out) {
let i = 0;
for (;;) {
if ( dict.has(hn) ) {
out[i] = hn; i += 1;
}
hn = this.toBroaderHostname(hn);
if ( hn === '' ) { break; }
}
out.length = i;
};
/******************************************************************************/
RedirectEngine.prototype.lookup = function(fctxt) { RedirectEngine.prototype.lookup = function(fctxt) {
const src = fctxt.getDocHostname();
const des = fctxt.getHostname();
const type = fctxt.type; const type = fctxt.type;
if ( this.ruleTypes.has(type) === false ) { return; } const queryHash = `${src} ${des} ${type}`;
const desAll = this._desAll; if ( queryHash === this._missedQueryHash ) {
return;
}
if ( src !== this._src ) {
this._src = src;
this.decomposeHostname(src, this.ruleSources, this._srcAll);
}
if ( this._srcAll.length === 0 ) {
this._missedQueryHash = queryHash;
return;
}
if ( des !== this._des ) {
this._des = des;
this.decomposeHostname(des, this.ruleDestinations, this._desAll);
}
if ( this._desAll.length === 0 ) {
this._missedQueryHash = queryHash;
return;
}
const reqURL = fctxt.url; const reqURL = fctxt.url;
let src = fctxt.getDocHostname(); for ( const src of this._srcAll ) {
let des = fctxt.getHostname(); for ( const des of this._desAll ) {
let n = 0; let entries = this.rules.get(`${src} ${des} ${type}`);
for (;;) { if ( entries !== undefined ) {
if ( this.ruleDestinations.has(des) ) {
desAll[n] = des; n += 1;
}
des = this.toBroaderHostname(des);
if ( des === '' ) { break; }
}
if ( n === 0 ) { return; }
for (;;) {
if ( this.ruleSources.has(src) ) {
for ( let i = 0; i < n; i++ ) {
const entries = this.rules.get(`${src} ${desAll[i]} ${type}`);
if ( entries === undefined ) { continue; }
const rule = this.lookupRule(entries, reqURL); const rule = this.lookupRule(entries, reqURL);
if ( rule === undefined ) { continue; } if ( rule !== undefined ) { return rule; }
return rule; }
entries = this.rules.get(`${src} ${des} *`);
if ( entries !== undefined ) {
const rule = this.lookupRule(entries, reqURL);
if ( rule !== undefined ) { return rule; }
} }
} }
src = this.toBroaderHostname(src);
if ( src === '' ) { break; }
} }
this._missedQueryHash = queryHash;
}; };
RedirectEngine.prototype.lookupRule = function(entries, reqURL) { RedirectEngine.prototype.lookupRule = function(entries, reqURL) {
@ -428,6 +471,10 @@ RedirectEngine.prototype.compileRuleFromStaticFilter = function(line) {
redirect = option.slice(14); redirect = option.slice(14);
continue; continue;
} }
if ( option === 'empty' ) {
redirect = 'empty';
continue;
}
if ( option.startsWith('domain=') ) { if ( option.startsWith('domain=') ) {
srchns = option.slice(7).split('|'); srchns = option.slice(7).split('|');
continue; continue;
@ -448,7 +495,10 @@ RedirectEngine.prototype.compileRuleFromStaticFilter = function(line) {
if ( redirect === '' ) { return; } if ( redirect === '' ) { return; }
// Need one single type -- not negated. // Need one single type -- not negated.
if ( type === undefined ) { return; } if ( type === undefined ) {
if ( redirect !== 'empty' ) { return; }
type = '*';
}
if ( deshn === '' ) { if ( deshn === '' ) {
deshn = '*'; deshn = '*';
@ -649,12 +699,12 @@ const removeTopCommentBlock = function(text) {
/******************************************************************************/ /******************************************************************************/
RedirectEngine.prototype.loadBuiltinResources = function() { RedirectEngine.prototype.loadBuiltinResources = function() {
this.resources = new Map();
this.aliases = new Map();
// TODO: remove once usage of uBO 1.20.4 is widespread. // TODO: remove once usage of uBO 1.20.4 is widespread.
µBlock.assets.remove('ublock-resources'); µBlock.assets.remove('ublock-resources');
this.resources = new Map();
this.aliases = new Map();
const fetches = [ const fetches = [
µBlock.assets.fetchText( µBlock.assets.fetchText(
'/assets/resources/scriptlets.js' '/assets/resources/scriptlets.js'

View File

@ -2012,6 +2012,11 @@ FilterParser.prototype.parseOptions = function(s) {
} }
// Used by Adguard, purpose is unclear -- just ignore for now. // Used by Adguard, purpose is unclear -- just ignore for now.
if ( opt === 'empty' ) { if ( opt === 'empty' ) {
if ( this.redirect !== 0 ) {
this.unsupported = true;
break;
}
this.redirect = 1;
continue; continue;
} }
// https://github.com/uBlockOrigin/uAssets/issues/192 // https://github.com/uBlockOrigin/uAssets/issues/192

View File