-
Notifications
You must be signed in to change notification settings - Fork 30
/
prevent-setInterval.js
226 lines (219 loc) · 7 KB
/
prevent-setInterval.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
import {
hit,
noopFunc,
isPreventionNeeded,
logMessage,
toRegExp,
nativeIsNaN,
parseMatchArg,
parseDelayArg,
isValidCallback,
isValidMatchStr,
isValidStrPattern,
escapeRegExp,
nativeIsFinite,
isValidMatchNumber,
parseRawDelay,
} from '../helpers';
/* eslint-disable max-len */
/**
* @scriptlet prevent-setInterval
*
* @description
* Prevents a `setInterval` call if:
*
* 1. The text of the callback is matching the specified `matchCallback` string/regexp which does not start with `!`;
* otherwise mismatched calls should be defused.
* 1. The delay is matching the specified `matchDelay`; otherwise mismatched calls should be defused.
*
* Related UBO scriptlet:
* https://github.com/gorhill/uBlock/wiki/Resources-Library#no-setinterval-ifjs-
*
* ### Syntax
*
* ```text
* example.org#%#//scriptlet('prevent-setInterval'[, matchCallback[, matchDelay]])
* ```
*
* > Call with no arguments will log all setInterval calls (`log-setInterval` superseding),
* > it may be useful for debugging but it is not allowed for prod versions of filter lists.
*
* - `matchCallback` — optional, string or regular expression;
* invalid regular expression will be skipped and all callbacks will be matched.
* If starts with `!`, scriptlet will not match the stringified callback but all other will be defused.
* If do not start with `!`, the stringified callback will be matched.
* If not set, prevents all `setInterval` calls due to specified `matchDelay`.
* - `matchDelay` — optional, must be an integer.
* If starts with `!`, scriptlet will not match the delay but all other will be defused.
* If do not start with `!`, the delay passed to the `setInterval` call will be matched.
* Decimal delay values will be rounded down, e.g `10.95` will be matched by `matchDelay` with value `10`.
*
* > If `prevent-setInterval` log looks like `setInterval(undefined, 1000)`,
* > it means that no callback was passed to setInterval() and that's not scriptlet issue
* > and obviously it can not be matched by `matchCallback`.
*
* ### Examples
*
* 1. Prevents `setInterval` calls if the callback matches `/\.test/` regardless of the delay
*
* ```adblock
* example.org#%#//scriptlet('prevent-setInterval', '/\.test/')
* ```
*
* For instance, the following call will be prevented:
*
* ```javascript
* setInterval(function () {
* window.test = "value";
* }, 100);
* ```
*
* 1. Prevents `setInterval` calls if the callback does not contain `value`
*
* ```adblock
* example.org#%#//scriptlet('prevent-setInterval', '!value')
* ```
*
* For instance, only the first of the following calls will be prevented:
*
* ```javascript
* setInterval(function () {
* window.test = "test -- prevented";
* }, 300);
* setInterval(function () {
* window.test = "value -- executed";
* }, 400);
* setInterval(function () {
* window.value = "test -- executed";
* }, 500);
* ```
*
* 1. Prevents `setInterval` calls if the callback contains `value` and the delay is not set to `300`
*
* ```adblock
* example.org#%#//scriptlet('prevent-setInterval', 'value', '!300')
* ```
*
* For instance, only the first of the following calls will not be prevented:
*
* ```javascript
* setInterval(function () {
* window.test = "value 1 -- executed";
* }, 300);
* setInterval(function () {
* window.test = "value 2 -- prevented";
* }, 400);
* setInterval(function () {
* window.test = "value 3 -- prevented";
* }, 500);
* ```
*
* 1. Prevents `setInterval` calls if the callback does not contain `value` and the delay is not set to `300`
*
* ```adblock
* example.org#%#//scriptlet('prevent-setInterval', '!value', '!300')
* ```
*
* For instance, only the second of the following calls will be prevented:
*
* ```javascript
* setInterval(function () {
* window.test = "test -- executed";
* }, 300);
* setInterval(function () {
* window.test = "test -- prevented";
* }, 400);
* setInterval(function () {
* window.test = "value -- executed";
* }, 400);
* setInterval(function () {
* window.value = "test -- executed";
* }, 500);
* ```
*
* 1. Prevents `setInterval` calls if the callback contains `value` and delay is a decimal number
*
* ```adblock
* example.org#%#//scriptlet('prevent-setInterval', 'value', '300')
* ```
*
* For instance, the following calls will be prevented:
*
* ```javascript
* setInterval(function () {
* window.test = "value";
* }, 300);
* setInterval(function () {
* window.test = "value";
* }, 300 + Math.random());
* ```
*
* @added v1.0.4.
*/
/* eslint-enable max-len */
export function preventSetInterval(source, matchCallback, matchDelay) {
// logs setIntervals to console if no arguments have been specified
const shouldLog = ((typeof matchCallback === 'undefined') && (typeof matchDelay === 'undefined'));
const handlerWrapper = (target, thisArg, args) => {
const callback = args[0];
const delay = args[1];
let shouldPrevent = false;
if (shouldLog) {
hit(source);
// https://github.com/AdguardTeam/Scriptlets/issues/105
logMessage(source, `setInterval(${String(callback)}, ${delay})`, true);
} else {
shouldPrevent = isPreventionNeeded({
callback,
delay,
matchCallback,
matchDelay,
});
}
if (shouldPrevent) {
hit(source);
args[0] = noopFunc;
}
return target.apply(thisArg, args);
};
const setIntervalHandler = {
apply: handlerWrapper,
};
window.setInterval = new Proxy(window.setInterval, setIntervalHandler);
}
export const preventSetIntervalNames = [
'prevent-setInterval',
// aliases are needed for matching the related scriptlet converted into our syntax
'no-setInterval-if.js', // new implementation of setInterval-defuser.js
'ubo-no-setInterval-if.js',
'setInterval-defuser.js', // old name should be supported as well
'ubo-setInterval-defuser.js',
'nosiif.js', // new short name of no-setInterval-if
'ubo-nosiif.js',
'sid.js', // old short scriptlet name
'ubo-sid.js',
'ubo-no-setInterval-if',
'ubo-setInterval-defuser',
'ubo-nosiif',
'ubo-sid',
];
// eslint-disable-next-line prefer-destructuring
preventSetInterval.primaryName = preventSetIntervalNames[0];
preventSetInterval.injections = [
hit,
noopFunc,
isPreventionNeeded,
logMessage,
// following helpers should be injected as helpers above use them
toRegExp,
nativeIsNaN,
parseMatchArg,
parseDelayArg,
isValidCallback,
isValidMatchStr,
isValidStrPattern,
escapeRegExp,
nativeIsFinite,
isValidMatchNumber,
parseRawDelay,
];