Add new scriptlet: no-fetch-if

The new scriptlet allows to defuse calls to fetch() by returning
a promise which always resolve to an empty response.

There is only one argument, which is a space-separated list
of conditions which must be ALL fulfilled in order for the
defusing to take place.

Each condition is a pair of property name and property value
separated by a column. Valid property names are those
documented as valid `init` options:

https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch

The URL of the fetch() is a special case and does not have to
be associated with a property name. Example of usage:

  ...##+js(no-fetch-if, method:HEAD)

Which means: defuse the call to fetch() if there is an
explicit option which contains `HEAD`. Another example:

  ...##+js(no-fetch-if, adsbygoogle.js)

Which means: defuse the call to fetch() if the URL contains
`adsbygoogle.js`. Multiple conditions can be provided:

  ...##+js(no-fetch-if, adsbygoogle.js method:HEAD)

If at least one condition does not match, the defusing will
not take place.

The string against which to match can be a literal regular
expression:

  ...##+js(no-fetch-if, /adsbygoogle.js$/ method:/HEAD|POST/)

Additonally, the following deprecated scriplets have been
removed:

- requestAnimationFrame-if.js
- setInterval-defuser.js
- setTimeout-logger.js
This commit is contained in:
Raymond Hill 2020-12-11 08:29:23 -05:00
parent 75ac182fe1
commit ba11a70013
No known key found for this signature in database
GPG Key ID: 25E1490B761470C2
1 changed files with 60 additions and 82 deletions

View File

@ -583,6 +583,65 @@
})();
/// no-fetch-if.js
(function() {
let arg1 = '{{1}}';
if ( arg1 === '{{1}}' ) { arg1 = ''; }
const needles = [];
for ( const condition of arg1.split(/\s+/) ) {
const pos = condition.indexOf(':');
let key, value;
if ( pos !== -1 ) {
key = condition.slice(0, pos);
value = condition.slice(pos + 1);
} else {
key = 'url';
value = condition;
}
if ( value === '' ) {
value = '^';
} else if ( value.startsWith('/') && value.endsWith('/') ) {
value = value.slice(1, -1);
} else {
value = value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
needles.push({ key, re: new RegExp(value) });
}
self.fetch = new Proxy(self.fetch, {
apply: function(target, thisArg, args) {
let proceed = true;
try {
const url = args[0] instanceof self.Request
? args[0].url
: args[0];
const props = new Map([ [ 'url', url ] ]);
const init = args[1];
if ( init instanceof Object ) {
for ( const prop in init ) {
if ( init.hasOwnProperty(prop) === false ) { continue; }
props.set( prop, init[prop]);
}
}
proceed = false;
for ( const { key, re } of needles ) {
if (
props.has(key) === false ||
re.test(props.get(key)) === false
) {
proceed = true;
break;
}
}
} catch(ex) {
}
return proceed
? Reflect.apply(target, thisArg, args)
: Promise.resolve(new Response());
}
});
})();
/// remove-attr.js
/// alias ra.js
(function() {
@ -646,36 +705,6 @@
})();
/// requestAnimationFrame-if.js
/// alias raf-if.js
// Deprecated, use "no-requestAnimationFrame-if.js"
(function() {
let needle = '{{1}}';
const not = needle.charAt(0) === '!';
if ( not ) { needle = needle.slice(1); }
if ( needle === '' || needle === '{{1}}' ) {
needle = '.?';
} else if ( needle.startsWith('/') && needle.endsWith('/') ) {
needle = needle.slice(1,-1);
} else {
needle = needle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
const log = needle === '.?' && not === false ? console.log : undefined;
needle = new RegExp(needle);
window.requestAnimationFrame = new Proxy(window.requestAnimationFrame, {
apply: function(target, thisArg, args) {
const a = String(args[0]);
if ( log !== undefined ) {
log('uBO: requestAnimationFrame("%s")', a);
} else if ( needle.test(a) === not ) {
args[0] = function(){};
}
return target.apply(thisArg, args);
}
});
})();
/// no-requestAnimationFrame-if.js
/// alias norafif.js
(function() {
@ -827,32 +856,6 @@
})();
/// setInterval-defuser.js
/// alias sid.js
(function() {
let needle = '{{1}}';
const delay = parseInt('{{2}}', 10);
if ( needle === '' || needle === '{{1}}' ) {
needle = '.?';
} else if ( needle.startsWith('/') && needle.endsWith('/') ) {
needle = needle.slice(1,-1);
} else {
needle = needle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
needle = new RegExp(needle);
window.setInterval = new Proxy(window.setInterval, {
apply: function(target, thisArg, args) {
const a = args[0];
const b = args[1];
if ( (isNaN(delay) || b === delay) && needle.test(a.toString()) ) {
args[0] = function(){};
}
return target.apply(thisArg, args);
}
});
})();
/// no-setInterval-if.js
/// alias nosiif.js
(function() {
@ -902,34 +905,9 @@
})();
/// setTimeout-defuser.js
/// alias std.js
(function() {
let needle = '{{1}}';
const delay = parseInt('{{2}}', 10);
if ( needle === '' || needle === '{{1}}' ) {
needle = '.?';
} else if ( needle.startsWith('/') && needle.endsWith('/') ) {
needle = needle.slice(1,-1);
} else {
needle = needle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
needle = new RegExp(needle);
window.setTimeout = new Proxy(window.setTimeout, {
apply: function(target, thisArg, args) {
const a = args[0];
const b = args[1];
if ( (isNaN(delay) || b === delay) && needle.test(a.toString()) ) {
args[0] = function(){};
}
return target.apply(thisArg, args);
}
});
})();
/// no-setTimeout-if.js
/// alias nostif.js
/// alias setTimeout-defuser.js
(function() {
let needle = '{{1}}';
const needleNot = needle.charAt(0) === '!';