mirror of https://github.com/gorhill/uBlock.git
Improve `prevent-setTimeout`/`prevent-setInterval` scriptlet
Add support for range for the `delay` paramater: --- @param [delay] A value to match against the delay. Can be a single value for exact match, or a range: - `min-max`: matches if delay >= min and delay <= max - `min-`: matches if delay >= min - `-max`: matches if delay <= max No delay means to match any delay value. Prepend with `!` to reverse the match condition. --- As discussed with filter list maintainers.
This commit is contained in:
parent
703fdf673c
commit
3b7fa79a68
|
@ -0,0 +1,236 @@
|
||||||
|
/*******************************************************************************
|
||||||
|
|
||||||
|
uBlock Origin - a comprehensive, efficient content blocker
|
||||||
|
Copyright (C) 2019-present Raymond Hill
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see {http://www.gnu.org/licenses/}.
|
||||||
|
|
||||||
|
Home: https://github.com/gorhill/uBlock
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { proxyApplyFn } from './proxy-apply.js';
|
||||||
|
import { registerScriptlet } from './base.js';
|
||||||
|
import { safeSelf } from './safe-self.js';
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
class RangeParser {
|
||||||
|
constructor(s) {
|
||||||
|
this.not = s.charAt(0) === '!';
|
||||||
|
if ( this.not ) { s = s.slice(1); }
|
||||||
|
if ( s === '' ) { return; }
|
||||||
|
const pos = s.indexOf('-');
|
||||||
|
if ( pos !== 0 ) {
|
||||||
|
this.min = this.max = parseInt(s, 10) || 0;
|
||||||
|
}
|
||||||
|
if ( pos !== -1 ) {
|
||||||
|
this.max = parseInt(s.slice(1), 10) || Number.MAX_SAFE_INTEGER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unbound() {
|
||||||
|
return this.min === undefined && this.max === undefined;
|
||||||
|
}
|
||||||
|
test(v) {
|
||||||
|
const n = Math.min(Math.max(Number(v) || 0, 0), Number.MAX_SAFE_INTEGER);
|
||||||
|
if ( this.min === this.max ) {
|
||||||
|
return (this.min === undefined || n === this.min) !== this.not;
|
||||||
|
}
|
||||||
|
if ( this.min === undefined ) {
|
||||||
|
return (n <= this.max) !== this.not;
|
||||||
|
}
|
||||||
|
if ( this.max === undefined ) {
|
||||||
|
return (n >= this.min) !== this.not;
|
||||||
|
}
|
||||||
|
return (n >= this.min && n <= this.max) !== this.not;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
registerScriptlet(RangeParser, {
|
||||||
|
name: 'range-parser.fn',
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @scriptlet prevent-setTimeout
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* Conditionally prevent execution of the callback function passed to native
|
||||||
|
* setTimeout method. With no parameters, all calls to setTimeout will be
|
||||||
|
* shown in the logger.
|
||||||
|
*
|
||||||
|
* @param [needle]
|
||||||
|
* A pattern to match against the stringified callback. The pattern can be a
|
||||||
|
* plain string, or a regex. Prepend with `!` to reverse the match condition.
|
||||||
|
*
|
||||||
|
* @param [delay]
|
||||||
|
* A value to match against the delay. Can be a single value for exact match,
|
||||||
|
* or a range:
|
||||||
|
* - `min-max`: matches if delay >= min and delay <= max
|
||||||
|
* - `min-`: matches if delay >= min
|
||||||
|
* - `-max`: matches if delay <= max
|
||||||
|
* No delay means to match any delay value.
|
||||||
|
* Prepend with `!` to reverse the match condition.
|
||||||
|
*
|
||||||
|
* */
|
||||||
|
|
||||||
|
export function preventSetTimeout(
|
||||||
|
needleRaw = '',
|
||||||
|
delayRaw = ''
|
||||||
|
) {
|
||||||
|
const safe = safeSelf();
|
||||||
|
const logPrefix = safe.makeLogPrefix('prevent-setTimeout', needleRaw, delayRaw);
|
||||||
|
const needleNot = needleRaw.charAt(0) === '!';
|
||||||
|
const reNeedle = safe.patternToRegex(needleNot ? needleRaw.slice(1) : needleRaw);
|
||||||
|
const range = new RangeParser(delayRaw);
|
||||||
|
proxyApplyFn('setTimeout', function(context) {
|
||||||
|
const { callArgs } = context;
|
||||||
|
const a = callArgs[0] instanceof Function
|
||||||
|
? String(safe.Function_toString(callArgs[0]))
|
||||||
|
: String(callArgs[0]);
|
||||||
|
const b = callArgs[1];
|
||||||
|
if ( needleRaw === '' && range.unbound() ) {
|
||||||
|
safe.uboLog(logPrefix, `Called:\n${a}\n${b}`);
|
||||||
|
return context.reflect();
|
||||||
|
}
|
||||||
|
if ( reNeedle.test(a) !== needleNot && range.test(b) ) {
|
||||||
|
callArgs[0] = function(){};
|
||||||
|
safe.uboLog(logPrefix, `Prevented:\n${a}\n${b}`);
|
||||||
|
}
|
||||||
|
return context.reflect();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
registerScriptlet(preventSetTimeout, {
|
||||||
|
name: 'prevent-setTimeout.js',
|
||||||
|
aliases: [
|
||||||
|
'no-setTimeout-if.js',
|
||||||
|
'nostif.js',
|
||||||
|
'setTimeout-defuser.js',
|
||||||
|
],
|
||||||
|
dependencies: [
|
||||||
|
proxyApplyFn,
|
||||||
|
RangeParser,
|
||||||
|
safeSelf,
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @scriptlet prevent-setInterval
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* Conditionally prevent execution of the callback function passed to native
|
||||||
|
* setInterval method. With no parameters, all calls to setInterval will be
|
||||||
|
* shown in the logger.
|
||||||
|
*
|
||||||
|
* @param [needle]
|
||||||
|
* A pattern to match against the stringified callback. The pattern can be a
|
||||||
|
* plain string, or a regex. Prepend with `!` to reverse the match condition.
|
||||||
|
* No pattern means to match anything.
|
||||||
|
*
|
||||||
|
* @param [delay]
|
||||||
|
* A value to match against the delay. Can be a single value for exact match,
|
||||||
|
* or a range:
|
||||||
|
* - `min-max`: matches if delay >= min and delay <= max
|
||||||
|
* - `min-`: matches if delay >= min
|
||||||
|
* - `-max`: matches if delay <= max
|
||||||
|
* No delay means to match any delay value.
|
||||||
|
* Prepend with `!` to reverse the match condition.
|
||||||
|
*
|
||||||
|
* */
|
||||||
|
|
||||||
|
export function preventSetInterval(
|
||||||
|
needleRaw = '',
|
||||||
|
delayRaw = ''
|
||||||
|
) {
|
||||||
|
const safe = safeSelf();
|
||||||
|
const logPrefix = safe.makeLogPrefix('prevent-setInterval', needleRaw, delayRaw);
|
||||||
|
const needleNot = needleRaw.charAt(0) === '!';
|
||||||
|
const reNeedle = safe.patternToRegex(needleNot ? needleRaw.slice(1) : needleRaw);
|
||||||
|
const range = new RangeParser(delayRaw);
|
||||||
|
proxyApplyFn('setInterval', function(context) {
|
||||||
|
const { callArgs } = context;
|
||||||
|
const a = callArgs[0] instanceof Function
|
||||||
|
? String(safe.Function_toString(callArgs[0]))
|
||||||
|
: String(callArgs[0]);
|
||||||
|
const b = callArgs[1];
|
||||||
|
if ( needleRaw === '' && range.unbound() ) {
|
||||||
|
safe.uboLog(logPrefix, `Called:\n${a}\n${b}`);
|
||||||
|
return context.reflect();
|
||||||
|
}
|
||||||
|
if ( reNeedle.test(a) !== needleNot && range.test(b) ) {
|
||||||
|
callArgs[0] = function(){};
|
||||||
|
safe.uboLog(logPrefix, `Prevented:\n${a}\n${b}`);
|
||||||
|
}
|
||||||
|
return context.reflect();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
registerScriptlet(preventSetInterval, {
|
||||||
|
name: 'prevent-setInterval.js',
|
||||||
|
aliases: [
|
||||||
|
'no-setInterval-if.js',
|
||||||
|
'nosiif.js',
|
||||||
|
'setInterval-defuser.js',
|
||||||
|
],
|
||||||
|
dependencies: [
|
||||||
|
proxyApplyFn,
|
||||||
|
RangeParser,
|
||||||
|
safeSelf,
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @scriptlet prevent-requestAnimationFrame
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* Conditionally prevent execution of the callback function passed to native
|
||||||
|
* requestAnimationFrame method. With no parameters, all calls to
|
||||||
|
* requestAnimationFrame will be shown in the logger.
|
||||||
|
*
|
||||||
|
* @param [needle]
|
||||||
|
* A pattern to match against the stringified callback. The pattern can be a
|
||||||
|
* plain string, or a regex.
|
||||||
|
* Prepend with `!` to reverse the match condition.
|
||||||
|
*
|
||||||
|
* */
|
||||||
|
|
||||||
|
export function preventRequestAnimationFrame(
|
||||||
|
needleRaw = ''
|
||||||
|
) {
|
||||||
|
const safe = safeSelf();
|
||||||
|
const logPrefix = safe.makeLogPrefix('prevent-requestAnimationFrame', needleRaw);
|
||||||
|
const needleNot = needleRaw.charAt(0) === '!';
|
||||||
|
const reNeedle = safe.patternToRegex(needleNot ? needleRaw.slice(1) : needleRaw);
|
||||||
|
proxyApplyFn('requestAnimationFrame', function(context) {
|
||||||
|
const { callArgs } = context;
|
||||||
|
const a = callArgs[0] instanceof Function
|
||||||
|
? String(safe.Function_toString(callArgs[0]))
|
||||||
|
: String(callArgs[0]);
|
||||||
|
if ( needleRaw === '' ) {
|
||||||
|
safe.uboLog(logPrefix, `Called:\n${a}`);
|
||||||
|
} else if ( reNeedle.test(a) !== needleNot ) {
|
||||||
|
callArgs[0] = function(){};
|
||||||
|
safe.uboLog(logPrefix, `Prevented:\n${a}`);
|
||||||
|
}
|
||||||
|
return context.reflect();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
registerScriptlet(preventRequestAnimationFrame, {
|
||||||
|
name: 'prevent-requestAnimationFrame.js',
|
||||||
|
aliases: [
|
||||||
|
'no-requestAnimationFrame-if.js',
|
||||||
|
'norafif.js',
|
||||||
|
],
|
||||||
|
dependencies: [
|
||||||
|
proxyApplyFn,
|
||||||
|
safeSelf,
|
||||||
|
],
|
||||||
|
});
|
|
@ -23,6 +23,7 @@
|
||||||
import './attribute.js';
|
import './attribute.js';
|
||||||
import './replace-argument.js';
|
import './replace-argument.js';
|
||||||
import './spoof-css.js';
|
import './spoof-css.js';
|
||||||
|
import './prevent-settimeout.js';
|
||||||
|
|
||||||
import { runAt, runAtHtmlElementFn } from './run-at.js';
|
import { runAt, runAtHtmlElementFn } from './run-at.js';
|
||||||
|
|
||||||
|
@ -1852,161 +1853,6 @@ function removeClass(
|
||||||
|
|
||||||
/******************************************************************************/
|
/******************************************************************************/
|
||||||
|
|
||||||
builtinScriptlets.push({
|
|
||||||
name: 'no-requestAnimationFrame-if.js',
|
|
||||||
aliases: [
|
|
||||||
'norafif.js',
|
|
||||||
'prevent-requestAnimationFrame.js',
|
|
||||||
],
|
|
||||||
fn: noRequestAnimationFrameIf,
|
|
||||||
dependencies: [
|
|
||||||
'safe-self.fn',
|
|
||||||
],
|
|
||||||
});
|
|
||||||
function noRequestAnimationFrameIf(
|
|
||||||
needle = ''
|
|
||||||
) {
|
|
||||||
if ( typeof needle !== 'string' ) { return; }
|
|
||||||
const safe = safeSelf();
|
|
||||||
const needleNot = needle.charAt(0) === '!';
|
|
||||||
if ( needleNot ) { needle = needle.slice(1); }
|
|
||||||
const log = needleNot === false && needle === '' ? console.log : undefined;
|
|
||||||
const reNeedle = safe.patternToRegex(needle);
|
|
||||||
window.requestAnimationFrame = new Proxy(window.requestAnimationFrame, {
|
|
||||||
apply: function(target, thisArg, args) {
|
|
||||||
const a = args[0] instanceof Function
|
|
||||||
? String(safe.Function_toString(args[0]))
|
|
||||||
: String(args[0]);
|
|
||||||
let defuse = false;
|
|
||||||
if ( log !== undefined ) {
|
|
||||||
log('uBO: requestAnimationFrame("%s")', a);
|
|
||||||
} else {
|
|
||||||
defuse = reNeedle.test(a) !== needleNot;
|
|
||||||
}
|
|
||||||
if ( defuse ) {
|
|
||||||
args[0] = function(){};
|
|
||||||
}
|
|
||||||
return target.apply(thisArg, args);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
builtinScriptlets.push({
|
|
||||||
name: 'no-setInterval-if.js',
|
|
||||||
aliases: [
|
|
||||||
'nosiif.js',
|
|
||||||
'prevent-setInterval.js',
|
|
||||||
'setInterval-defuser.js',
|
|
||||||
],
|
|
||||||
fn: noSetIntervalIf,
|
|
||||||
dependencies: [
|
|
||||||
'proxy-apply.fn',
|
|
||||||
'safe-self.fn',
|
|
||||||
],
|
|
||||||
});
|
|
||||||
function noSetIntervalIf(
|
|
||||||
needle = '',
|
|
||||||
delay = ''
|
|
||||||
) {
|
|
||||||
if ( typeof needle !== 'string' ) { return; }
|
|
||||||
const safe = safeSelf();
|
|
||||||
const logPrefix = safe.makeLogPrefix('prevent-setInterval', needle, delay);
|
|
||||||
const needleNot = needle.charAt(0) === '!';
|
|
||||||
if ( needleNot ) { needle = needle.slice(1); }
|
|
||||||
if ( delay === '' ) { delay = undefined; }
|
|
||||||
let delayNot = false;
|
|
||||||
if ( delay !== undefined ) {
|
|
||||||
delayNot = delay.charAt(0) === '!';
|
|
||||||
if ( delayNot ) { delay = delay.slice(1); }
|
|
||||||
delay = parseInt(delay, 10);
|
|
||||||
}
|
|
||||||
const reNeedle = safe.patternToRegex(needle);
|
|
||||||
proxyApplyFn('setInterval', function setInterval(context) {
|
|
||||||
const { callArgs } = context;
|
|
||||||
const a = callArgs[0] instanceof Function
|
|
||||||
? String(safe.Function_toString(callArgs[0]))
|
|
||||||
: String(callArgs[0]);
|
|
||||||
const b = callArgs[1];
|
|
||||||
if ( needle === '' && delay === undefined ) {
|
|
||||||
safe.uboLog(logPrefix, `Called:\n${a}\n${b}`);
|
|
||||||
return context.reflect();
|
|
||||||
}
|
|
||||||
let defuse;
|
|
||||||
if ( needle !== '' ) {
|
|
||||||
defuse = reNeedle.test(a) !== needleNot;
|
|
||||||
}
|
|
||||||
if ( defuse !== false && delay !== undefined ) {
|
|
||||||
defuse = (b === delay || isNaN(b) && isNaN(delay) ) !== delayNot;
|
|
||||||
}
|
|
||||||
if ( defuse ) {
|
|
||||||
callArgs[0] = function(){};
|
|
||||||
safe.uboLog(logPrefix, `Prevented:\n${a}\n${b}`);
|
|
||||||
}
|
|
||||||
return context.reflect();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
builtinScriptlets.push({
|
|
||||||
name: 'no-setTimeout-if.js',
|
|
||||||
aliases: [
|
|
||||||
'nostif.js',
|
|
||||||
'prevent-setTimeout.js',
|
|
||||||
'setTimeout-defuser.js',
|
|
||||||
],
|
|
||||||
fn: noSetTimeoutIf,
|
|
||||||
dependencies: [
|
|
||||||
'proxy-apply.fn',
|
|
||||||
'safe-self.fn',
|
|
||||||
],
|
|
||||||
});
|
|
||||||
function noSetTimeoutIf(
|
|
||||||
needle = '',
|
|
||||||
delay = ''
|
|
||||||
) {
|
|
||||||
if ( typeof needle !== 'string' ) { return; }
|
|
||||||
const safe = safeSelf();
|
|
||||||
const logPrefix = safe.makeLogPrefix('prevent-setTimeout', needle, delay);
|
|
||||||
const needleNot = needle.charAt(0) === '!';
|
|
||||||
if ( needleNot ) { needle = needle.slice(1); }
|
|
||||||
if ( delay === '' ) { delay = undefined; }
|
|
||||||
let delayNot = false;
|
|
||||||
if ( delay !== undefined ) {
|
|
||||||
delayNot = delay.charAt(0) === '!';
|
|
||||||
if ( delayNot ) { delay = delay.slice(1); }
|
|
||||||
delay = parseInt(delay, 10);
|
|
||||||
}
|
|
||||||
const reNeedle = safe.patternToRegex(needle);
|
|
||||||
proxyApplyFn('setTimeout', function setTimeout(context) {
|
|
||||||
const { callArgs } = context;
|
|
||||||
const a = callArgs[0] instanceof Function
|
|
||||||
? String(safe.Function_toString(callArgs[0]))
|
|
||||||
: String(callArgs[0]);
|
|
||||||
const b = callArgs[1];
|
|
||||||
if ( needle === '' && delay === undefined ) {
|
|
||||||
safe.uboLog(logPrefix, `Called:\n${a}\n${b}`);
|
|
||||||
return context.reflect();
|
|
||||||
}
|
|
||||||
let defuse;
|
|
||||||
if ( needle !== '' ) {
|
|
||||||
defuse = reNeedle.test(a) !== needleNot;
|
|
||||||
}
|
|
||||||
if ( defuse !== false && delay !== undefined ) {
|
|
||||||
defuse = (b === delay || isNaN(b) && isNaN(delay) ) !== delayNot;
|
|
||||||
}
|
|
||||||
if ( defuse ) {
|
|
||||||
callArgs[0] = function(){};
|
|
||||||
safe.uboLog(logPrefix, `Prevented:\n${a}\n${b}`);
|
|
||||||
}
|
|
||||||
return context.reflect();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/******************************************************************************/
|
|
||||||
|
|
||||||
builtinScriptlets.push({
|
builtinScriptlets.push({
|
||||||
name: 'webrtc-if.js',
|
name: 'webrtc-if.js',
|
||||||
fn: webrtcIf,
|
fn: webrtcIf,
|
||||||
|
|
Loading…
Reference in New Issue