@browserless.io/browserless 2.24.0 → 2.24.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -3
- package/build/browsers/index.js +1 -1
- package/build/routes/chrome/http/content.post.body.json +8 -8
- package/build/routes/chrome/http/pdf.post.body.json +8 -8
- package/build/routes/chrome/http/scrape.post.body.json +8 -8
- package/build/routes/chrome/http/screenshot.post.body.json +8 -8
- package/build/routes/chromium/http/content.post.body.json +8 -8
- package/build/routes/chromium/http/pdf.post.body.json +8 -8
- package/build/routes/chromium/http/scrape.post.body.json +8 -8
- package/build/routes/chromium/http/screenshot.post.body.json +8 -8
- package/build/routes/management/http/meta.get.js +3 -1
- package/build/shared/utils/performance/main.js +2 -1
- package/extensions/ublock/_locales/ar/messages.json +3 -3
- package/extensions/ublock/_locales/bg/messages.json +1 -1
- package/extensions/ublock/_locales/br_FR/messages.json +2 -2
- package/extensions/ublock/_locales/cy/messages.json +11 -11
- package/extensions/ublock/_locales/el/messages.json +2 -2
- package/extensions/ublock/_locales/hu/messages.json +1 -1
- package/extensions/ublock/_locales/id/messages.json +1 -1
- package/extensions/ublock/_locales/lv/messages.json +4 -4
- package/extensions/ublock/_locales/mk/messages.json +130 -130
- package/extensions/ublock/_locales/oc/messages.json +1 -1
- package/extensions/ublock/_locales/pt_BR/messages.json +1 -1
- package/extensions/ublock/_locales/pt_PT/messages.json +2 -2
- package/extensions/ublock/_locales/si/messages.json +100 -100
- package/extensions/ublock/_locales/sr/messages.json +4 -4
- package/extensions/ublock/_locales/vi/messages.json +19 -19
- package/extensions/ublock/_locales/zh_TW/messages.json +28 -28
- package/extensions/ublock/assets/assets.json +33 -29
- package/extensions/ublock/assets/thirdparties/easylist/easylist.txt +2984 -3287
- package/extensions/ublock/assets/thirdparties/easylist/easyprivacy.txt +150 -171
- package/extensions/ublock/assets/thirdparties/pgl.yoyo.org/as/serverlist +37 -27
- package/extensions/ublock/assets/thirdparties/publicsuffix.org/list/effective_tld_names.dat +802 -888
- package/extensions/ublock/assets/thirdparties/urlhaus-filter/urlhaus-filter-online.txt +2355 -2071
- package/extensions/ublock/assets/ublock/badlists.txt +9 -1
- package/extensions/ublock/assets/ublock/badware.min.txt +354 -243
- package/extensions/ublock/assets/ublock/filters.min.txt +5837 -5737
- package/extensions/ublock/assets/ublock/privacy.min.txt +151 -38
- package/extensions/ublock/assets/ublock/quick-fixes.min.txt +83 -127
- package/extensions/ublock/assets/ublock/unbreak.min.txt +66 -50
- package/extensions/ublock/css/codemirror.css +4 -0
- package/extensions/ublock/document-blocked.html +3 -1
- package/extensions/ublock/js/arglist-parser.js +116 -0
- package/extensions/ublock/js/background.js +1 -1
- package/extensions/ublock/js/logger-ui.js +1 -1
- package/extensions/ublock/js/messaging.js +9 -2
- package/extensions/ublock/js/pagestore.js +3 -1
- package/extensions/ublock/js/redirect-engine.js +3 -1
- package/extensions/ublock/{assets/resources/set-attr.js → js/resources/attribute.js} +115 -11
- package/extensions/ublock/js/resources/base.js +38 -0
- package/extensions/ublock/js/resources/cookie.js +419 -0
- package/extensions/ublock/js/resources/href-sanitizer.js +188 -0
- package/extensions/ublock/js/resources/localstorage.js +235 -0
- package/extensions/ublock/js/resources/parse-replace.js +54 -0
- package/extensions/ublock/js/resources/prevent-settimeout.js +236 -0
- package/extensions/ublock/js/resources/proxy-apply.js +109 -0
- package/extensions/ublock/js/resources/replace-argument.js +120 -0
- package/extensions/ublock/{assets → js}/resources/run-at.js +20 -4
- package/extensions/ublock/{assets → js}/resources/safe-self.js +5 -4
- package/extensions/ublock/{assets → js}/resources/scriptlets.js +90 -1589
- package/extensions/ublock/js/resources/set-constant.js +287 -0
- package/extensions/ublock/js/resources/shared.js +44 -0
- package/extensions/ublock/js/resources/spoof-css.js +163 -0
- package/extensions/ublock/js/s14e-serializer.js +2 -1
- package/extensions/ublock/js/scriptlet-filtering-core.js +1 -1
- package/extensions/ublock/js/scriptlet-filtering.js +1 -31
- package/extensions/ublock/js/static-dnr-filtering.js +143 -129
- package/extensions/ublock/js/static-filtering-parser.js +27 -117
- package/extensions/ublock/js/static-net-filtering.js +53 -141
- package/extensions/ublock/js/traffic.js +1 -1
- package/extensions/ublock/js/urlskip.js +166 -0
- package/extensions/ublock/js/vapi-background-ext.js +38 -14
- package/extensions/ublock/manifest.json +1 -1
- package/package.json +11 -11
- package/src/browsers/index.ts +1 -1
- package/src/routes/management/http/meta.get.ts +6 -1
- package/src/shared/utils/performance/main.ts +2 -8
- package/static/docs/swagger.json +10 -10
- package/static/docs/swagger.min.json +9 -9
- package/static/function/client.js +119 -18
- package/static/function/index.html +119 -18
|
@@ -18,44 +18,29 @@
|
|
|
18
18
|
|
|
19
19
|
Home: https://github.com/gorhill/uBlock
|
|
20
20
|
|
|
21
|
-
The scriptlets below are meant to be injected only into a
|
|
22
|
-
web page context.
|
|
23
21
|
*/
|
|
24
22
|
|
|
25
|
-
import
|
|
26
|
-
import
|
|
27
|
-
import
|
|
23
|
+
import './attribute.js';
|
|
24
|
+
import './href-sanitizer.js';
|
|
25
|
+
import './replace-argument.js';
|
|
26
|
+
import './spoof-css.js';
|
|
27
|
+
import './prevent-settimeout.js';
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
import { runAt, runAtHtmlElementFn } from './run-at.js';
|
|
30
|
+
|
|
31
|
+
import { getAllCookiesFn } from './cookie.js';
|
|
32
|
+
import { getAllLocalStorageFn } from './localstorage.js';
|
|
33
|
+
import { proxyApplyFn } from './proxy-apply.js';
|
|
34
|
+
import { registeredScriptlets } from './base.js';
|
|
35
|
+
import { safeSelf } from './safe-self.js';
|
|
36
|
+
import { validateConstantFn } from './set-constant.js';
|
|
30
37
|
|
|
31
38
|
// Externally added to the private namespace in which scriptlets execute.
|
|
32
39
|
/* global scriptletGlobals */
|
|
33
40
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
/******************************************************************************/
|
|
37
|
-
|
|
38
|
-
// Register scriptlets declared in other files.
|
|
39
|
-
|
|
40
|
-
const registerScriptlet = fn => {
|
|
41
|
-
const details = fn.details;
|
|
42
|
-
if ( typeof details !== 'object' ) {
|
|
43
|
-
throw new ReferenceError('Unknown scriptlet function');
|
|
44
|
-
}
|
|
45
|
-
details.fn = fn;
|
|
46
|
-
if ( Array.isArray(details.dependencies) ) {
|
|
47
|
-
details.dependencies.forEach((fn, i, array) => {
|
|
48
|
-
if ( typeof fn !== 'function' ) { return; }
|
|
49
|
-
array[i] = fn.details.name;
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
builtinScriptlets.push(details);
|
|
53
|
-
};
|
|
41
|
+
/* eslint no-prototype-builtins: 0 */
|
|
54
42
|
|
|
55
|
-
|
|
56
|
-
registerScriptlet(setAttrFn);
|
|
57
|
-
registerScriptlet(setAttr);
|
|
58
|
-
registerScriptlet(trustedSetAttr);
|
|
43
|
+
export const builtinScriptlets = registeredScriptlets;
|
|
59
44
|
|
|
60
45
|
/*******************************************************************************
|
|
61
46
|
|
|
@@ -111,34 +96,6 @@ function shouldDebug(details) {
|
|
|
111
96
|
|
|
112
97
|
/******************************************************************************/
|
|
113
98
|
|
|
114
|
-
builtinScriptlets.push({
|
|
115
|
-
name: 'run-at.fn',
|
|
116
|
-
fn: runAt,
|
|
117
|
-
dependencies: [
|
|
118
|
-
'safe-self.fn',
|
|
119
|
-
],
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
/******************************************************************************/
|
|
123
|
-
|
|
124
|
-
builtinScriptlets.push({
|
|
125
|
-
name: 'run-at-html-element.fn',
|
|
126
|
-
fn: runAtHtmlElementFn,
|
|
127
|
-
});
|
|
128
|
-
function runAtHtmlElementFn(fn) {
|
|
129
|
-
if ( document.documentElement ) {
|
|
130
|
-
fn();
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
const observer = new MutationObserver(( ) => {
|
|
134
|
-
observer.disconnect();
|
|
135
|
-
fn();
|
|
136
|
-
});
|
|
137
|
-
observer.observe(document, { childList: true });
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/******************************************************************************/
|
|
141
|
-
|
|
142
99
|
// Reference:
|
|
143
100
|
// https://github.com/AdguardTeam/Scriptlets/blob/master/wiki/about-scriptlets.md#prevent-xhr
|
|
144
101
|
//
|
|
@@ -236,7 +193,7 @@ function abortCurrentScriptCore(
|
|
|
236
193
|
const reContext = safe.patternToRegex(context);
|
|
237
194
|
const extraArgs = safe.getExtraArgs(Array.from(arguments), 3);
|
|
238
195
|
const thisScript = document.currentScript;
|
|
239
|
-
const chain =
|
|
196
|
+
const chain = safe.String_split.call(target, '.');
|
|
240
197
|
let owner = window;
|
|
241
198
|
let prop;
|
|
242
199
|
for (;;) {
|
|
@@ -331,228 +288,6 @@ function abortCurrentScriptCore(
|
|
|
331
288
|
|
|
332
289
|
/******************************************************************************/
|
|
333
290
|
|
|
334
|
-
builtinScriptlets.push({
|
|
335
|
-
name: 'validate-constant.fn',
|
|
336
|
-
fn: validateConstantFn,
|
|
337
|
-
dependencies: [
|
|
338
|
-
'safe-self.fn',
|
|
339
|
-
],
|
|
340
|
-
});
|
|
341
|
-
function validateConstantFn(trusted, raw, extraArgs = {}) {
|
|
342
|
-
const safe = safeSelf();
|
|
343
|
-
let value;
|
|
344
|
-
if ( raw === 'undefined' ) {
|
|
345
|
-
value = undefined;
|
|
346
|
-
} else if ( raw === 'false' ) {
|
|
347
|
-
value = false;
|
|
348
|
-
} else if ( raw === 'true' ) {
|
|
349
|
-
value = true;
|
|
350
|
-
} else if ( raw === 'null' ) {
|
|
351
|
-
value = null;
|
|
352
|
-
} else if ( raw === "''" || raw === '' ) {
|
|
353
|
-
value = '';
|
|
354
|
-
} else if ( raw === '[]' || raw === 'emptyArr' ) {
|
|
355
|
-
value = [];
|
|
356
|
-
} else if ( raw === '{}' || raw === 'emptyObj' ) {
|
|
357
|
-
value = {};
|
|
358
|
-
} else if ( raw === 'noopFunc' ) {
|
|
359
|
-
value = function(){};
|
|
360
|
-
} else if ( raw === 'trueFunc' ) {
|
|
361
|
-
value = function(){ return true; };
|
|
362
|
-
} else if ( raw === 'falseFunc' ) {
|
|
363
|
-
value = function(){ return false; };
|
|
364
|
-
} else if ( raw === 'throwFunc' ) {
|
|
365
|
-
value = function(){ throw ''; };
|
|
366
|
-
} else if ( /^-?\d+$/.test(raw) ) {
|
|
367
|
-
value = parseInt(raw);
|
|
368
|
-
if ( isNaN(raw) ) { return; }
|
|
369
|
-
if ( Math.abs(raw) > 0x7FFF ) { return; }
|
|
370
|
-
} else if ( trusted ) {
|
|
371
|
-
if ( raw.startsWith('json:') ) {
|
|
372
|
-
try { value = safe.JSON_parse(raw.slice(5)); } catch(ex) { return; }
|
|
373
|
-
} else if ( raw.startsWith('{') && raw.endsWith('}') ) {
|
|
374
|
-
try { value = safe.JSON_parse(raw).value; } catch(ex) { return; }
|
|
375
|
-
}
|
|
376
|
-
} else {
|
|
377
|
-
return;
|
|
378
|
-
}
|
|
379
|
-
if ( extraArgs.as !== undefined ) {
|
|
380
|
-
if ( extraArgs.as === 'function' ) {
|
|
381
|
-
return ( ) => value;
|
|
382
|
-
} else if ( extraArgs.as === 'callback' ) {
|
|
383
|
-
return ( ) => (( ) => value);
|
|
384
|
-
} else if ( extraArgs.as === 'resolved' ) {
|
|
385
|
-
return Promise.resolve(value);
|
|
386
|
-
} else if ( extraArgs.as === 'rejected' ) {
|
|
387
|
-
return Promise.reject(value);
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
return value;
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
/******************************************************************************/
|
|
394
|
-
|
|
395
|
-
builtinScriptlets.push({
|
|
396
|
-
name: 'set-constant.fn',
|
|
397
|
-
fn: setConstantFn,
|
|
398
|
-
dependencies: [
|
|
399
|
-
'run-at.fn',
|
|
400
|
-
'safe-self.fn',
|
|
401
|
-
'validate-constant.fn',
|
|
402
|
-
],
|
|
403
|
-
});
|
|
404
|
-
function setConstantFn(
|
|
405
|
-
trusted = false,
|
|
406
|
-
chain = '',
|
|
407
|
-
rawValue = ''
|
|
408
|
-
) {
|
|
409
|
-
if ( chain === '' ) { return; }
|
|
410
|
-
const safe = safeSelf();
|
|
411
|
-
const logPrefix = safe.makeLogPrefix('set-constant', chain, rawValue);
|
|
412
|
-
const extraArgs = safe.getExtraArgs(Array.from(arguments), 3);
|
|
413
|
-
function setConstant(chain, rawValue) {
|
|
414
|
-
const trappedProp = (( ) => {
|
|
415
|
-
const pos = chain.lastIndexOf('.');
|
|
416
|
-
if ( pos === -1 ) { return chain; }
|
|
417
|
-
return chain.slice(pos+1);
|
|
418
|
-
})();
|
|
419
|
-
const cloakFunc = fn => {
|
|
420
|
-
safe.Object_defineProperty(fn, 'name', { value: trappedProp });
|
|
421
|
-
return new Proxy(fn, {
|
|
422
|
-
defineProperty(target, prop) {
|
|
423
|
-
if ( prop !== 'toString' ) {
|
|
424
|
-
return Reflect.defineProperty(...arguments);
|
|
425
|
-
}
|
|
426
|
-
return true;
|
|
427
|
-
},
|
|
428
|
-
deleteProperty(target, prop) {
|
|
429
|
-
if ( prop !== 'toString' ) {
|
|
430
|
-
return Reflect.deleteProperty(...arguments);
|
|
431
|
-
}
|
|
432
|
-
return true;
|
|
433
|
-
},
|
|
434
|
-
get(target, prop) {
|
|
435
|
-
if ( prop === 'toString' ) {
|
|
436
|
-
return function() {
|
|
437
|
-
return `function ${trappedProp}() { [native code] }`;
|
|
438
|
-
}.bind(null);
|
|
439
|
-
}
|
|
440
|
-
return Reflect.get(...arguments);
|
|
441
|
-
},
|
|
442
|
-
});
|
|
443
|
-
};
|
|
444
|
-
if ( trappedProp === '' ) { return; }
|
|
445
|
-
const thisScript = document.currentScript;
|
|
446
|
-
let normalValue = validateConstantFn(trusted, rawValue, extraArgs);
|
|
447
|
-
if ( rawValue === 'noopFunc' || rawValue === 'trueFunc' || rawValue === 'falseFunc' ) {
|
|
448
|
-
normalValue = cloakFunc(normalValue);
|
|
449
|
-
}
|
|
450
|
-
let aborted = false;
|
|
451
|
-
const mustAbort = function(v) {
|
|
452
|
-
if ( trusted ) { return false; }
|
|
453
|
-
if ( aborted ) { return true; }
|
|
454
|
-
aborted =
|
|
455
|
-
(v !== undefined && v !== null) &&
|
|
456
|
-
(normalValue !== undefined && normalValue !== null) &&
|
|
457
|
-
(typeof v !== typeof normalValue);
|
|
458
|
-
if ( aborted ) {
|
|
459
|
-
safe.uboLog(logPrefix, `Aborted because value set to ${v}`);
|
|
460
|
-
}
|
|
461
|
-
return aborted;
|
|
462
|
-
};
|
|
463
|
-
// https://github.com/uBlockOrigin/uBlock-issues/issues/156
|
|
464
|
-
// Support multiple trappers for the same property.
|
|
465
|
-
const trapProp = function(owner, prop, configurable, handler) {
|
|
466
|
-
if ( handler.init(configurable ? owner[prop] : normalValue) === false ) { return; }
|
|
467
|
-
const odesc = safe.Object_getOwnPropertyDescriptor(owner, prop);
|
|
468
|
-
let prevGetter, prevSetter;
|
|
469
|
-
if ( odesc instanceof safe.Object ) {
|
|
470
|
-
owner[prop] = normalValue;
|
|
471
|
-
if ( odesc.get instanceof Function ) {
|
|
472
|
-
prevGetter = odesc.get;
|
|
473
|
-
}
|
|
474
|
-
if ( odesc.set instanceof Function ) {
|
|
475
|
-
prevSetter = odesc.set;
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
try {
|
|
479
|
-
safe.Object_defineProperty(owner, prop, {
|
|
480
|
-
configurable,
|
|
481
|
-
get() {
|
|
482
|
-
if ( prevGetter !== undefined ) {
|
|
483
|
-
prevGetter();
|
|
484
|
-
}
|
|
485
|
-
return handler.getter();
|
|
486
|
-
},
|
|
487
|
-
set(a) {
|
|
488
|
-
if ( prevSetter !== undefined ) {
|
|
489
|
-
prevSetter(a);
|
|
490
|
-
}
|
|
491
|
-
handler.setter(a);
|
|
492
|
-
}
|
|
493
|
-
});
|
|
494
|
-
safe.uboLog(logPrefix, 'Trap installed');
|
|
495
|
-
} catch(ex) {
|
|
496
|
-
safe.uboErr(logPrefix, ex);
|
|
497
|
-
}
|
|
498
|
-
};
|
|
499
|
-
const trapChain = function(owner, chain) {
|
|
500
|
-
const pos = chain.indexOf('.');
|
|
501
|
-
if ( pos === -1 ) {
|
|
502
|
-
trapProp(owner, chain, false, {
|
|
503
|
-
v: undefined,
|
|
504
|
-
init: function(v) {
|
|
505
|
-
if ( mustAbort(v) ) { return false; }
|
|
506
|
-
this.v = v;
|
|
507
|
-
return true;
|
|
508
|
-
},
|
|
509
|
-
getter: function() {
|
|
510
|
-
if ( document.currentScript === thisScript ) {
|
|
511
|
-
return this.v;
|
|
512
|
-
}
|
|
513
|
-
safe.uboLog(logPrefix, 'Property read');
|
|
514
|
-
return normalValue;
|
|
515
|
-
},
|
|
516
|
-
setter: function(a) {
|
|
517
|
-
if ( mustAbort(a) === false ) { return; }
|
|
518
|
-
normalValue = a;
|
|
519
|
-
}
|
|
520
|
-
});
|
|
521
|
-
return;
|
|
522
|
-
}
|
|
523
|
-
const prop = chain.slice(0, pos);
|
|
524
|
-
const v = owner[prop];
|
|
525
|
-
chain = chain.slice(pos + 1);
|
|
526
|
-
if ( v instanceof safe.Object || typeof v === 'object' && v !== null ) {
|
|
527
|
-
trapChain(v, chain);
|
|
528
|
-
return;
|
|
529
|
-
}
|
|
530
|
-
trapProp(owner, prop, true, {
|
|
531
|
-
v: undefined,
|
|
532
|
-
init: function(v) {
|
|
533
|
-
this.v = v;
|
|
534
|
-
return true;
|
|
535
|
-
},
|
|
536
|
-
getter: function() {
|
|
537
|
-
return this.v;
|
|
538
|
-
},
|
|
539
|
-
setter: function(a) {
|
|
540
|
-
this.v = a;
|
|
541
|
-
if ( a instanceof safe.Object ) {
|
|
542
|
-
trapChain(a, chain);
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
});
|
|
546
|
-
};
|
|
547
|
-
trapChain(window, chain);
|
|
548
|
-
}
|
|
549
|
-
runAt(( ) => {
|
|
550
|
-
setConstant(chain, rawValue);
|
|
551
|
-
}, extraArgs.runAt);
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
/******************************************************************************/
|
|
555
|
-
|
|
556
291
|
builtinScriptlets.push({
|
|
557
292
|
name: 'replace-node-text.fn',
|
|
558
293
|
fn: replaceNodeTextFn,
|
|
@@ -672,6 +407,7 @@ builtinScriptlets.push({
|
|
|
672
407
|
dependencies: [
|
|
673
408
|
'matches-stack-trace.fn',
|
|
674
409
|
'object-find-owner.fn',
|
|
410
|
+
'safe-self.fn',
|
|
675
411
|
],
|
|
676
412
|
});
|
|
677
413
|
// When no "prune paths" argument is provided, the scriptlet is
|
|
@@ -688,14 +424,15 @@ function objectPruneFn(
|
|
|
688
424
|
extraArgs = {}
|
|
689
425
|
) {
|
|
690
426
|
if ( typeof rawPrunePaths !== 'string' ) { return; }
|
|
427
|
+
const safe = safeSelf();
|
|
691
428
|
const prunePaths = rawPrunePaths !== ''
|
|
692
|
-
?
|
|
429
|
+
? safe.String_split.call(rawPrunePaths, / +/)
|
|
693
430
|
: [];
|
|
694
431
|
const needlePaths = prunePaths.length !== 0 && rawNeedlePaths !== ''
|
|
695
|
-
?
|
|
432
|
+
? safe.String_split.call(rawNeedlePaths, / +/)
|
|
696
433
|
: [];
|
|
697
434
|
if ( stackNeedleDetails.matchAll !== true ) {
|
|
698
|
-
if (
|
|
435
|
+
if ( matchesStackTraceFn(stackNeedleDetails, extraArgs.logstack) === false ) {
|
|
699
436
|
return;
|
|
700
437
|
}
|
|
701
438
|
}
|
|
@@ -793,235 +530,15 @@ function objectFindOwnerFn(
|
|
|
793
530
|
|
|
794
531
|
/******************************************************************************/
|
|
795
532
|
|
|
796
|
-
builtinScriptlets.push({
|
|
797
|
-
name: 'get-safe-cookie-values.fn',
|
|
798
|
-
fn: getSafeCookieValuesFn,
|
|
799
|
-
});
|
|
800
|
-
function getSafeCookieValuesFn() {
|
|
801
|
-
return [
|
|
802
|
-
'accept', 'reject',
|
|
803
|
-
'accepted', 'rejected', 'notaccepted',
|
|
804
|
-
'allow', 'disallow', 'deny',
|
|
805
|
-
'allowed', 'denied',
|
|
806
|
-
'approved', 'disapproved',
|
|
807
|
-
'checked', 'unchecked',
|
|
808
|
-
'dismiss', 'dismissed',
|
|
809
|
-
'enable', 'disable',
|
|
810
|
-
'enabled', 'disabled',
|
|
811
|
-
'essential', 'nonessential',
|
|
812
|
-
'forbidden', 'forever',
|
|
813
|
-
'hide', 'hidden',
|
|
814
|
-
'necessary', 'required',
|
|
815
|
-
'ok',
|
|
816
|
-
'on', 'off',
|
|
817
|
-
'true', 't', 'false', 'f',
|
|
818
|
-
'yes', 'y', 'no', 'n',
|
|
819
|
-
];
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
/******************************************************************************/
|
|
823
|
-
|
|
824
|
-
builtinScriptlets.push({
|
|
825
|
-
name: 'get-all-cookies.fn',
|
|
826
|
-
fn: getAllCookiesFn,
|
|
827
|
-
});
|
|
828
|
-
function getAllCookiesFn() {
|
|
829
|
-
return document.cookie.split(/\s*;\s*/).map(s => {
|
|
830
|
-
const pos = s.indexOf('=');
|
|
831
|
-
if ( pos === 0 ) { return; }
|
|
832
|
-
if ( pos === -1 ) { return `${s.trim()}=`; }
|
|
833
|
-
const key = s.slice(0, pos).trim();
|
|
834
|
-
const value = s.slice(pos+1).trim();
|
|
835
|
-
return { key, value };
|
|
836
|
-
}).filter(s => s !== undefined);
|
|
837
|
-
}
|
|
838
|
-
|
|
839
|
-
/******************************************************************************/
|
|
840
|
-
|
|
841
|
-
builtinScriptlets.push({
|
|
842
|
-
name: 'get-all-local-storage.fn',
|
|
843
|
-
fn: getAllLocalStorageFn,
|
|
844
|
-
});
|
|
845
|
-
function getAllLocalStorageFn(which = 'localStorage') {
|
|
846
|
-
const storage = self[which];
|
|
847
|
-
const out = [];
|
|
848
|
-
for ( let i = 0; i < storage.length; i++ ) {
|
|
849
|
-
const key = storage.key(i);
|
|
850
|
-
const value = storage.getItem(key);
|
|
851
|
-
return { key, value };
|
|
852
|
-
}
|
|
853
|
-
return out;
|
|
854
|
-
}
|
|
855
|
-
|
|
856
|
-
/******************************************************************************/
|
|
857
|
-
|
|
858
|
-
builtinScriptlets.push({
|
|
859
|
-
name: 'get-cookie.fn',
|
|
860
|
-
fn: getCookieFn,
|
|
861
|
-
});
|
|
862
|
-
function getCookieFn(
|
|
863
|
-
name = ''
|
|
864
|
-
) {
|
|
865
|
-
for ( const s of document.cookie.split(/\s*;\s*/) ) {
|
|
866
|
-
const pos = s.indexOf('=');
|
|
867
|
-
if ( pos === -1 ) { continue; }
|
|
868
|
-
if ( s.slice(0, pos) !== name ) { continue; }
|
|
869
|
-
return s.slice(pos+1).trim();
|
|
870
|
-
}
|
|
871
|
-
}
|
|
872
|
-
|
|
873
|
-
/******************************************************************************/
|
|
874
|
-
|
|
875
|
-
builtinScriptlets.push({
|
|
876
|
-
name: 'set-cookie.fn',
|
|
877
|
-
fn: setCookieFn,
|
|
878
|
-
dependencies: [
|
|
879
|
-
'get-cookie.fn',
|
|
880
|
-
],
|
|
881
|
-
});
|
|
882
|
-
function setCookieFn(
|
|
883
|
-
trusted = false,
|
|
884
|
-
name = '',
|
|
885
|
-
value = '',
|
|
886
|
-
expires = '',
|
|
887
|
-
path = '',
|
|
888
|
-
options = {},
|
|
889
|
-
) {
|
|
890
|
-
// https://datatracker.ietf.org/doc/html/rfc2616#section-2.2
|
|
891
|
-
// https://github.com/uBlockOrigin/uBlock-issues/issues/2777
|
|
892
|
-
if ( trusted === false && /[^!#$%&'*+\-.0-9A-Z[\]^_`a-z|~]/.test(name) ) {
|
|
893
|
-
name = encodeURIComponent(name);
|
|
894
|
-
}
|
|
895
|
-
// https://datatracker.ietf.org/doc/html/rfc6265#section-4.1.1
|
|
896
|
-
// The characters [",] are given a pass from the RFC requirements because
|
|
897
|
-
// apparently browsers do not follow the RFC to the letter.
|
|
898
|
-
if ( /[^ -:<-[\]-~]/.test(value) ) {
|
|
899
|
-
value = encodeURIComponent(value);
|
|
900
|
-
}
|
|
901
|
-
|
|
902
|
-
const cookieBefore = getCookieFn(name);
|
|
903
|
-
if ( cookieBefore !== undefined && options.dontOverwrite ) { return; }
|
|
904
|
-
if ( cookieBefore === value && options.reload ) { return; }
|
|
905
|
-
|
|
906
|
-
const cookieParts = [ name, '=', value ];
|
|
907
|
-
if ( expires !== '' ) {
|
|
908
|
-
cookieParts.push('; expires=', expires);
|
|
909
|
-
}
|
|
910
|
-
|
|
911
|
-
if ( path === '' ) { path = '/'; }
|
|
912
|
-
else if ( path === 'none' ) { path = ''; }
|
|
913
|
-
if ( path !== '' && path !== '/' ) { return; }
|
|
914
|
-
if ( path === '/' ) {
|
|
915
|
-
cookieParts.push('; path=/');
|
|
916
|
-
}
|
|
917
|
-
|
|
918
|
-
if ( trusted ) {
|
|
919
|
-
if ( options.domain ) {
|
|
920
|
-
cookieParts.push(`; domain=${options.domain}`);
|
|
921
|
-
}
|
|
922
|
-
cookieParts.push('; Secure');
|
|
923
|
-
} else if ( /^__(Host|Secure)-/.test(name) ) {
|
|
924
|
-
cookieParts.push('; Secure');
|
|
925
|
-
}
|
|
926
|
-
|
|
927
|
-
try {
|
|
928
|
-
document.cookie = cookieParts.join('');
|
|
929
|
-
} catch(_) {
|
|
930
|
-
}
|
|
931
|
-
|
|
932
|
-
const done = getCookieFn(name) === value;
|
|
933
|
-
if ( done && options.reload ) {
|
|
934
|
-
window.location.reload();
|
|
935
|
-
}
|
|
936
|
-
|
|
937
|
-
return done;
|
|
938
|
-
}
|
|
939
|
-
|
|
940
|
-
/******************************************************************************/
|
|
941
|
-
|
|
942
|
-
builtinScriptlets.push({
|
|
943
|
-
name: 'set-local-storage-item.fn',
|
|
944
|
-
fn: setLocalStorageItemFn,
|
|
945
|
-
dependencies: [
|
|
946
|
-
'get-safe-cookie-values.fn',
|
|
947
|
-
'safe-self.fn',
|
|
948
|
-
],
|
|
949
|
-
});
|
|
950
|
-
function setLocalStorageItemFn(
|
|
951
|
-
which = 'local',
|
|
952
|
-
trusted = false,
|
|
953
|
-
key = '',
|
|
954
|
-
value = '',
|
|
955
|
-
) {
|
|
956
|
-
if ( key === '' ) { return; }
|
|
957
|
-
|
|
958
|
-
// For increased compatibility with AdGuard
|
|
959
|
-
if ( value === 'emptyArr' ) {
|
|
960
|
-
value = '[]';
|
|
961
|
-
} else if ( value === 'emptyObj' ) {
|
|
962
|
-
value = '{}';
|
|
963
|
-
}
|
|
964
|
-
|
|
965
|
-
const trustedValues = [
|
|
966
|
-
'',
|
|
967
|
-
'undefined', 'null',
|
|
968
|
-
'{}', '[]', '""',
|
|
969
|
-
'$remove$',
|
|
970
|
-
...getSafeCookieValuesFn(),
|
|
971
|
-
];
|
|
972
|
-
|
|
973
|
-
if ( trusted ) {
|
|
974
|
-
if ( value.includes('$now$') ) {
|
|
975
|
-
value = value.replaceAll('$now$', Date.now());
|
|
976
|
-
}
|
|
977
|
-
if ( value.includes('$currentDate$') ) {
|
|
978
|
-
value = value.replaceAll('$currentDate$', `${Date()}`);
|
|
979
|
-
}
|
|
980
|
-
if ( value.includes('$currentISODate$') ) {
|
|
981
|
-
value = value.replaceAll('$currentISODate$', (new Date()).toISOString());
|
|
982
|
-
}
|
|
983
|
-
} else {
|
|
984
|
-
const normalized = value.toLowerCase();
|
|
985
|
-
const match = /^("?)(.+)\1$/.exec(normalized);
|
|
986
|
-
const unquoted = match && match[2] || normalized;
|
|
987
|
-
if ( trustedValues.includes(unquoted) === false ) {
|
|
988
|
-
if ( /^\d+$/.test(unquoted) === false ) { return; }
|
|
989
|
-
const n = parseInt(unquoted, 10);
|
|
990
|
-
if ( n > 32767 ) { return; }
|
|
991
|
-
}
|
|
992
|
-
}
|
|
993
|
-
|
|
994
|
-
try {
|
|
995
|
-
const storage = self[`${which}Storage`];
|
|
996
|
-
if ( value === '$remove$' ) {
|
|
997
|
-
const safe = safeSelf();
|
|
998
|
-
const pattern = safe.patternToRegex(key, undefined, true );
|
|
999
|
-
const toRemove = [];
|
|
1000
|
-
for ( let i = 0, n = storage.length; i < n; i++ ) {
|
|
1001
|
-
const key = storage.key(i);
|
|
1002
|
-
if ( pattern.test(key) ) { toRemove.push(key); }
|
|
1003
|
-
}
|
|
1004
|
-
for ( const key of toRemove ) {
|
|
1005
|
-
storage.removeItem(key);
|
|
1006
|
-
}
|
|
1007
|
-
} else {
|
|
1008
|
-
storage.setItem(key, `${value}`);
|
|
1009
|
-
}
|
|
1010
|
-
} catch(ex) {
|
|
1011
|
-
}
|
|
1012
|
-
}
|
|
1013
|
-
|
|
1014
|
-
/******************************************************************************/
|
|
1015
|
-
|
|
1016
533
|
builtinScriptlets.push({
|
|
1017
534
|
name: 'matches-stack-trace.fn',
|
|
1018
|
-
fn:
|
|
535
|
+
fn: matchesStackTraceFn,
|
|
1019
536
|
dependencies: [
|
|
1020
537
|
'get-exception-token.fn',
|
|
1021
538
|
'safe-self.fn',
|
|
1022
539
|
],
|
|
1023
540
|
});
|
|
1024
|
-
function
|
|
541
|
+
function matchesStackTraceFn(
|
|
1025
542
|
needleDetails,
|
|
1026
543
|
logLevel = ''
|
|
1027
544
|
) {
|
|
@@ -1033,7 +550,7 @@ function matchesStackTrace(
|
|
|
1033
550
|
// Normalize stack trace
|
|
1034
551
|
const reLine = /(.*?@)?(\S+)(:\d+):\d+\)?$/;
|
|
1035
552
|
const lines = [];
|
|
1036
|
-
for ( let line of error.stack
|
|
553
|
+
for ( let line of safe.String_split.call(error.stack, /[\n\r]+/) ) {
|
|
1037
554
|
if ( line.includes(exceptionToken) ) { continue; }
|
|
1038
555
|
line = line.trim();
|
|
1039
556
|
const match = safe.RegExp_exec.call(reLine, line);
|
|
@@ -1080,9 +597,13 @@ function parsePropertiesToMatch(propsToMatch, implicit = '') {
|
|
|
1080
597
|
const needles = new Map();
|
|
1081
598
|
if ( propsToMatch === undefined || propsToMatch === '' ) { return needles; }
|
|
1082
599
|
const options = { canNegate: true };
|
|
1083
|
-
for ( const needle of
|
|
1084
|
-
|
|
600
|
+
for ( const needle of safe.String_split.call(propsToMatch, /\s+/) ) {
|
|
601
|
+
let [ prop, pattern ] = safe.String_split.call(needle, ':');
|
|
1085
602
|
if ( prop === '' ) { continue; }
|
|
603
|
+
if ( pattern !== undefined && /[^$\w -]/.test(prop) ) {
|
|
604
|
+
prop = `${prop}:${pattern}`;
|
|
605
|
+
pattern = undefined;
|
|
606
|
+
}
|
|
1086
607
|
if ( pattern !== undefined ) {
|
|
1087
608
|
needles.set(prop, safe.initPattern(pattern, options));
|
|
1088
609
|
} else if ( implicit !== '' ) {
|
|
@@ -1307,93 +828,6 @@ function replaceFetchResponseFn(
|
|
|
1307
828
|
|
|
1308
829
|
/******************************************************************************/
|
|
1309
830
|
|
|
1310
|
-
builtinScriptlets.push({
|
|
1311
|
-
name: 'proxy-apply.fn',
|
|
1312
|
-
fn: proxyApplyFn,
|
|
1313
|
-
});
|
|
1314
|
-
function proxyApplyFn(
|
|
1315
|
-
target = '',
|
|
1316
|
-
handler = ''
|
|
1317
|
-
) {
|
|
1318
|
-
let context = globalThis;
|
|
1319
|
-
let prop = target;
|
|
1320
|
-
for (;;) {
|
|
1321
|
-
const pos = prop.indexOf('.');
|
|
1322
|
-
if ( pos === -1 ) { break; }
|
|
1323
|
-
context = context[prop.slice(0, pos)];
|
|
1324
|
-
if ( context instanceof Object === false ) { return; }
|
|
1325
|
-
prop = prop.slice(pos+1);
|
|
1326
|
-
}
|
|
1327
|
-
const fn = context[prop];
|
|
1328
|
-
if ( typeof fn !== 'function' ) { return; }
|
|
1329
|
-
if ( proxyApplyFn.CtorContext === undefined ) {
|
|
1330
|
-
proxyApplyFn.ctorContexts = [];
|
|
1331
|
-
proxyApplyFn.CtorContext = class {
|
|
1332
|
-
constructor(...args) {
|
|
1333
|
-
this.init(...args);
|
|
1334
|
-
}
|
|
1335
|
-
init(callFn, callArgs) {
|
|
1336
|
-
this.callFn = callFn;
|
|
1337
|
-
this.callArgs = callArgs;
|
|
1338
|
-
return this;
|
|
1339
|
-
}
|
|
1340
|
-
reflect() {
|
|
1341
|
-
const r = Reflect.construct(this.callFn, this.callArgs);
|
|
1342
|
-
this.callFn = this.callArgs = undefined;
|
|
1343
|
-
proxyApplyFn.ctorContexts.push(this);
|
|
1344
|
-
return r;
|
|
1345
|
-
}
|
|
1346
|
-
static factory(...args) {
|
|
1347
|
-
return proxyApplyFn.ctorContexts.length !== 0
|
|
1348
|
-
? proxyApplyFn.ctorContexts.pop().init(...args)
|
|
1349
|
-
: new proxyApplyFn.CtorContext(...args);
|
|
1350
|
-
}
|
|
1351
|
-
};
|
|
1352
|
-
proxyApplyFn.applyContexts = [];
|
|
1353
|
-
proxyApplyFn.ApplyContext = class {
|
|
1354
|
-
constructor(...args) {
|
|
1355
|
-
this.init(...args);
|
|
1356
|
-
}
|
|
1357
|
-
init(callFn, thisArg, callArgs) {
|
|
1358
|
-
this.callFn = callFn;
|
|
1359
|
-
this.thisArg = thisArg;
|
|
1360
|
-
this.callArgs = callArgs;
|
|
1361
|
-
return this;
|
|
1362
|
-
}
|
|
1363
|
-
reflect() {
|
|
1364
|
-
const r = Reflect.apply(this.callFn, this.thisArg, this.callArgs);
|
|
1365
|
-
this.callFn = this.thisArg = this.callArgs = undefined;
|
|
1366
|
-
proxyApplyFn.applyContexts.push(this);
|
|
1367
|
-
return r;
|
|
1368
|
-
}
|
|
1369
|
-
static factory(...args) {
|
|
1370
|
-
return proxyApplyFn.applyContexts.length !== 0
|
|
1371
|
-
? proxyApplyFn.applyContexts.pop().init(...args)
|
|
1372
|
-
: new proxyApplyFn.ApplyContext(...args);
|
|
1373
|
-
}
|
|
1374
|
-
};
|
|
1375
|
-
}
|
|
1376
|
-
const fnStr = fn.toString();
|
|
1377
|
-
const toString = (function toString() { return fnStr; }).bind(null);
|
|
1378
|
-
const proxyDetails = {
|
|
1379
|
-
apply(target, thisArg, args) {
|
|
1380
|
-
return handler(proxyApplyFn.ApplyContext.factory(target, thisArg, args));
|
|
1381
|
-
},
|
|
1382
|
-
get(target, prop) {
|
|
1383
|
-
if ( prop === 'toString' ) { return toString; }
|
|
1384
|
-
return Reflect.get(target, prop);
|
|
1385
|
-
},
|
|
1386
|
-
};
|
|
1387
|
-
if ( fn.prototype?.constructor === fn ) {
|
|
1388
|
-
proxyDetails.construct = function(target, args) {
|
|
1389
|
-
return handler(proxyApplyFn.CtorContext.factory(target, args));
|
|
1390
|
-
};
|
|
1391
|
-
}
|
|
1392
|
-
context[prop] = new Proxy(fn, proxyDetails);
|
|
1393
|
-
}
|
|
1394
|
-
|
|
1395
|
-
/******************************************************************************/
|
|
1396
|
-
|
|
1397
831
|
builtinScriptlets.push({
|
|
1398
832
|
name: 'prevent-xhr.fn',
|
|
1399
833
|
fn: preventXhrFn,
|
|
@@ -1444,11 +878,11 @@ function preventXhrFn(
|
|
|
1444
878
|
'content-type': '',
|
|
1445
879
|
'content-length': '',
|
|
1446
880
|
},
|
|
881
|
+
url: haystack.url,
|
|
1447
882
|
props: {
|
|
1448
883
|
response: { value: '' },
|
|
1449
884
|
responseText: { value: '' },
|
|
1450
885
|
responseXML: { value: null },
|
|
1451
|
-
responseURL: { value: haystack.url },
|
|
1452
886
|
},
|
|
1453
887
|
});
|
|
1454
888
|
xhrInstances.set(this, xhrDetails);
|
|
@@ -1504,6 +938,7 @@ function preventXhrFn(
|
|
|
1504
938
|
xhrDetails.headers['content-length'] = `${xhrDetails.props.response.value}`.length;
|
|
1505
939
|
Object.defineProperties(xhrDetails.xhr, {
|
|
1506
940
|
readyState: { value: 4 },
|
|
941
|
+
responseURL: { value: xhrDetails.url },
|
|
1507
942
|
status: { value: 200 },
|
|
1508
943
|
statusText: { value: 'OK' },
|
|
1509
944
|
});
|
|
@@ -1513,6 +948,7 @@ function preventXhrFn(
|
|
|
1513
948
|
Promise.resolve(xhrText).then(( ) => xhrDetails).then(details => {
|
|
1514
949
|
Object.defineProperties(details.xhr, {
|
|
1515
950
|
readyState: { value: 1, configurable: true },
|
|
951
|
+
responseURL: { value: xhrDetails.url },
|
|
1516
952
|
});
|
|
1517
953
|
safeDispatchEvent(details.xhr, 'readystatechange');
|
|
1518
954
|
return details;
|
|
@@ -1738,13 +1174,13 @@ function abortOnStackTrace(
|
|
|
1738
1174
|
let v = owner[chain];
|
|
1739
1175
|
Object.defineProperty(owner, chain, {
|
|
1740
1176
|
get: function() {
|
|
1741
|
-
if (
|
|
1177
|
+
if ( matchesStackTraceFn(needleDetails, extraArgs.log) ) {
|
|
1742
1178
|
throw new ReferenceError(getExceptionToken());
|
|
1743
1179
|
}
|
|
1744
1180
|
return v;
|
|
1745
1181
|
},
|
|
1746
1182
|
set: function(a) {
|
|
1747
|
-
if (
|
|
1183
|
+
if ( matchesStackTraceFn(needleDetails, extraArgs.log) ) {
|
|
1748
1184
|
throw new ReferenceError(getExceptionToken());
|
|
1749
1185
|
}
|
|
1750
1186
|
v = a;
|
|
@@ -2214,7 +1650,7 @@ function noFetchIf(
|
|
|
2214
1650
|
const safe = safeSelf();
|
|
2215
1651
|
const logPrefix = safe.makeLogPrefix('prevent-fetch', propsToMatch, responseBody, responseType);
|
|
2216
1652
|
const needles = [];
|
|
2217
|
-
for ( const condition of
|
|
1653
|
+
for ( const condition of safe.String_split.call(propsToMatch, /\s+/) ) {
|
|
2218
1654
|
if ( condition === '' ) { continue; }
|
|
2219
1655
|
const pos = condition.indexOf(':');
|
|
2220
1656
|
let key, value;
|
|
@@ -2348,17 +1784,18 @@ function preventRefresh(
|
|
|
2348
1784
|
/******************************************************************************/
|
|
2349
1785
|
|
|
2350
1786
|
builtinScriptlets.push({
|
|
2351
|
-
name: 'remove-
|
|
1787
|
+
name: 'remove-class.js',
|
|
2352
1788
|
aliases: [
|
|
2353
|
-
'
|
|
1789
|
+
'rc.js',
|
|
2354
1790
|
],
|
|
2355
|
-
fn:
|
|
1791
|
+
fn: removeClass,
|
|
1792
|
+
world: 'ISOLATED',
|
|
2356
1793
|
dependencies: [
|
|
2357
1794
|
'run-at.fn',
|
|
2358
1795
|
'safe-self.fn',
|
|
2359
1796
|
],
|
|
2360
1797
|
});
|
|
2361
|
-
function
|
|
1798
|
+
function removeClass(
|
|
2362
1799
|
rawToken = '',
|
|
2363
1800
|
rawSelector = '',
|
|
2364
1801
|
behavior = ''
|
|
@@ -2366,42 +1803,32 @@ function removeAttr(
|
|
|
2366
1803
|
if ( typeof rawToken !== 'string' ) { return; }
|
|
2367
1804
|
if ( rawToken === '' ) { return; }
|
|
2368
1805
|
const safe = safeSelf();
|
|
2369
|
-
const logPrefix = safe.makeLogPrefix('remove-
|
|
2370
|
-
const tokens =
|
|
1806
|
+
const logPrefix = safe.makeLogPrefix('remove-class', rawToken, rawSelector, behavior);
|
|
1807
|
+
const tokens = safe.String_split.call(rawToken, /\s*\|\s*/);
|
|
2371
1808
|
const selector = tokens
|
|
2372
|
-
.map(a => `${rawSelector}
|
|
1809
|
+
.map(a => `${rawSelector}.${CSS.escape(a)}`)
|
|
2373
1810
|
.join(',');
|
|
2374
1811
|
if ( safe.logLevel > 1 ) {
|
|
2375
1812
|
safe.uboLog(logPrefix, `Target selector:\n\t${selector}`);
|
|
2376
1813
|
}
|
|
2377
|
-
const
|
|
2378
|
-
let
|
|
2379
|
-
const
|
|
2380
|
-
|
|
2381
|
-
timerId = safe.onIdle(( ) => {
|
|
2382
|
-
timerId = undefined;
|
|
2383
|
-
rmattr();
|
|
2384
|
-
}, { timeout: 17 });
|
|
2385
|
-
};
|
|
2386
|
-
const rmattr = ( ) => {
|
|
2387
|
-
if ( timerId !== undefined ) {
|
|
2388
|
-
safe.offIdle(timerId);
|
|
2389
|
-
timerId = undefined;
|
|
2390
|
-
}
|
|
1814
|
+
const mustStay = /\bstay\b/.test(behavior);
|
|
1815
|
+
let timer;
|
|
1816
|
+
const rmclass = ( ) => {
|
|
1817
|
+
timer = undefined;
|
|
2391
1818
|
try {
|
|
2392
1819
|
const nodes = document.querySelectorAll(selector);
|
|
2393
1820
|
for ( const node of nodes ) {
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
node.removeAttribute(attr);
|
|
2397
|
-
safe.uboLog(logPrefix, `Removed attribute '${attr}'`);
|
|
2398
|
-
}
|
|
1821
|
+
node.classList.remove(...tokens);
|
|
1822
|
+
safe.uboLog(logPrefix, 'Removed class(es)');
|
|
2399
1823
|
}
|
|
2400
1824
|
} catch(ex) {
|
|
2401
1825
|
}
|
|
1826
|
+
if ( mustStay ) { return; }
|
|
1827
|
+
if ( document.readyState !== 'complete' ) { return; }
|
|
1828
|
+
observer.disconnect();
|
|
2402
1829
|
};
|
|
2403
1830
|
const mutationHandler = mutations => {
|
|
2404
|
-
if (
|
|
1831
|
+
if ( timer !== undefined ) { return; }
|
|
2405
1832
|
let skip = true;
|
|
2406
1833
|
for ( let i = 0; i < mutations.length && skip; i++ ) {
|
|
2407
1834
|
const { type, addedNodes, removedNodes } = mutations[i];
|
|
@@ -2414,270 +1841,21 @@ function removeAttr(
|
|
|
2414
1841
|
}
|
|
2415
1842
|
}
|
|
2416
1843
|
if ( skip ) { return; }
|
|
2417
|
-
|
|
1844
|
+
timer = safe.onIdle(rmclass, { timeout: 67 });
|
|
2418
1845
|
};
|
|
1846
|
+
const observer = new MutationObserver(mutationHandler);
|
|
2419
1847
|
const start = ( ) => {
|
|
2420
|
-
|
|
2421
|
-
if ( /\bstay\b/.test(behavior) === false ) { return; }
|
|
2422
|
-
const observer = new MutationObserver(mutationHandler);
|
|
1848
|
+
rmclass();
|
|
2423
1849
|
observer.observe(document, {
|
|
2424
1850
|
attributes: true,
|
|
2425
|
-
attributeFilter:
|
|
1851
|
+
attributeFilter: [ 'class' ],
|
|
2426
1852
|
childList: true,
|
|
2427
1853
|
subtree: true,
|
|
2428
1854
|
});
|
|
2429
1855
|
};
|
|
2430
|
-
runAt(( ) => {
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
/******************************************************************************/
|
|
2434
|
-
|
|
2435
|
-
builtinScriptlets.push({
|
|
2436
|
-
name: 'remove-class.js',
|
|
2437
|
-
aliases: [
|
|
2438
|
-
'rc.js',
|
|
2439
|
-
],
|
|
2440
|
-
fn: removeClass,
|
|
2441
|
-
world: 'ISOLATED',
|
|
2442
|
-
dependencies: [
|
|
2443
|
-
'run-at.fn',
|
|
2444
|
-
'safe-self.fn',
|
|
2445
|
-
],
|
|
2446
|
-
});
|
|
2447
|
-
function removeClass(
|
|
2448
|
-
rawToken = '',
|
|
2449
|
-
rawSelector = '',
|
|
2450
|
-
behavior = ''
|
|
2451
|
-
) {
|
|
2452
|
-
if ( typeof rawToken !== 'string' ) { return; }
|
|
2453
|
-
if ( rawToken === '' ) { return; }
|
|
2454
|
-
const safe = safeSelf();
|
|
2455
|
-
const logPrefix = safe.makeLogPrefix('remove-class', rawToken, rawSelector, behavior);
|
|
2456
|
-
const tokens = rawToken.split(/\s*\|\s*/);
|
|
2457
|
-
const selector = tokens
|
|
2458
|
-
.map(a => `${rawSelector}.${CSS.escape(a)}`)
|
|
2459
|
-
.join(',');
|
|
2460
|
-
if ( safe.logLevel > 1 ) {
|
|
2461
|
-
safe.uboLog(logPrefix, `Target selector:\n\t${selector}`);
|
|
2462
|
-
}
|
|
2463
|
-
const mustStay = /\bstay\b/.test(behavior);
|
|
2464
|
-
let timer;
|
|
2465
|
-
const rmclass = ( ) => {
|
|
2466
|
-
timer = undefined;
|
|
2467
|
-
try {
|
|
2468
|
-
const nodes = document.querySelectorAll(selector);
|
|
2469
|
-
for ( const node of nodes ) {
|
|
2470
|
-
node.classList.remove(...tokens);
|
|
2471
|
-
safe.uboLog(logPrefix, 'Removed class(es)');
|
|
2472
|
-
}
|
|
2473
|
-
} catch(ex) {
|
|
2474
|
-
}
|
|
2475
|
-
if ( mustStay ) { return; }
|
|
2476
|
-
if ( document.readyState !== 'complete' ) { return; }
|
|
2477
|
-
observer.disconnect();
|
|
2478
|
-
};
|
|
2479
|
-
const mutationHandler = mutations => {
|
|
2480
|
-
if ( timer !== undefined ) { return; }
|
|
2481
|
-
let skip = true;
|
|
2482
|
-
for ( let i = 0; i < mutations.length && skip; i++ ) {
|
|
2483
|
-
const { type, addedNodes, removedNodes } = mutations[i];
|
|
2484
|
-
if ( type === 'attributes' ) { skip = false; }
|
|
2485
|
-
for ( let j = 0; j < addedNodes.length && skip; j++ ) {
|
|
2486
|
-
if ( addedNodes[j].nodeType === 1 ) { skip = false; break; }
|
|
2487
|
-
}
|
|
2488
|
-
for ( let j = 0; j < removedNodes.length && skip; j++ ) {
|
|
2489
|
-
if ( removedNodes[j].nodeType === 1 ) { skip = false; break; }
|
|
2490
|
-
}
|
|
2491
|
-
}
|
|
2492
|
-
if ( skip ) { return; }
|
|
2493
|
-
timer = safe.onIdle(rmclass, { timeout: 67 });
|
|
2494
|
-
};
|
|
2495
|
-
const observer = new MutationObserver(mutationHandler);
|
|
2496
|
-
const start = ( ) => {
|
|
2497
|
-
rmclass();
|
|
2498
|
-
observer.observe(document, {
|
|
2499
|
-
attributes: true,
|
|
2500
|
-
attributeFilter: [ 'class' ],
|
|
2501
|
-
childList: true,
|
|
2502
|
-
subtree: true,
|
|
2503
|
-
});
|
|
2504
|
-
};
|
|
2505
|
-
runAt(( ) => {
|
|
2506
|
-
start();
|
|
2507
|
-
}, /\bcomplete\b/.test(behavior) ? 'idle' : 'loading');
|
|
2508
|
-
}
|
|
2509
|
-
|
|
2510
|
-
/******************************************************************************/
|
|
2511
|
-
|
|
2512
|
-
builtinScriptlets.push({
|
|
2513
|
-
name: 'no-requestAnimationFrame-if.js',
|
|
2514
|
-
aliases: [
|
|
2515
|
-
'norafif.js',
|
|
2516
|
-
'prevent-requestAnimationFrame.js',
|
|
2517
|
-
],
|
|
2518
|
-
fn: noRequestAnimationFrameIf,
|
|
2519
|
-
dependencies: [
|
|
2520
|
-
'safe-self.fn',
|
|
2521
|
-
],
|
|
2522
|
-
});
|
|
2523
|
-
function noRequestAnimationFrameIf(
|
|
2524
|
-
needle = ''
|
|
2525
|
-
) {
|
|
2526
|
-
if ( typeof needle !== 'string' ) { return; }
|
|
2527
|
-
const safe = safeSelf();
|
|
2528
|
-
const needleNot = needle.charAt(0) === '!';
|
|
2529
|
-
if ( needleNot ) { needle = needle.slice(1); }
|
|
2530
|
-
const log = needleNot === false && needle === '' ? console.log : undefined;
|
|
2531
|
-
const reNeedle = safe.patternToRegex(needle);
|
|
2532
|
-
window.requestAnimationFrame = new Proxy(window.requestAnimationFrame, {
|
|
2533
|
-
apply: function(target, thisArg, args) {
|
|
2534
|
-
const a = args[0] instanceof Function
|
|
2535
|
-
? String(safe.Function_toString(args[0]))
|
|
2536
|
-
: String(args[0]);
|
|
2537
|
-
let defuse = false;
|
|
2538
|
-
if ( log !== undefined ) {
|
|
2539
|
-
log('uBO: requestAnimationFrame("%s")', a);
|
|
2540
|
-
} else {
|
|
2541
|
-
defuse = reNeedle.test(a) !== needleNot;
|
|
2542
|
-
}
|
|
2543
|
-
if ( defuse ) {
|
|
2544
|
-
args[0] = function(){};
|
|
2545
|
-
}
|
|
2546
|
-
return target.apply(thisArg, args);
|
|
2547
|
-
}
|
|
2548
|
-
});
|
|
2549
|
-
}
|
|
2550
|
-
|
|
2551
|
-
/******************************************************************************/
|
|
2552
|
-
|
|
2553
|
-
builtinScriptlets.push({
|
|
2554
|
-
name: 'set-constant.js',
|
|
2555
|
-
aliases: [
|
|
2556
|
-
'set.js',
|
|
2557
|
-
],
|
|
2558
|
-
fn: setConstant,
|
|
2559
|
-
dependencies: [
|
|
2560
|
-
'set-constant.fn'
|
|
2561
|
-
],
|
|
2562
|
-
});
|
|
2563
|
-
function setConstant(
|
|
2564
|
-
...args
|
|
2565
|
-
) {
|
|
2566
|
-
setConstantFn(false, ...args);
|
|
2567
|
-
}
|
|
2568
|
-
|
|
2569
|
-
/******************************************************************************/
|
|
2570
|
-
|
|
2571
|
-
builtinScriptlets.push({
|
|
2572
|
-
name: 'no-setInterval-if.js',
|
|
2573
|
-
aliases: [
|
|
2574
|
-
'nosiif.js',
|
|
2575
|
-
'prevent-setInterval.js',
|
|
2576
|
-
'setInterval-defuser.js',
|
|
2577
|
-
],
|
|
2578
|
-
fn: noSetIntervalIf,
|
|
2579
|
-
dependencies: [
|
|
2580
|
-
'proxy-apply.fn',
|
|
2581
|
-
'safe-self.fn',
|
|
2582
|
-
],
|
|
2583
|
-
});
|
|
2584
|
-
function noSetIntervalIf(
|
|
2585
|
-
needle = '',
|
|
2586
|
-
delay = ''
|
|
2587
|
-
) {
|
|
2588
|
-
if ( typeof needle !== 'string' ) { return; }
|
|
2589
|
-
const safe = safeSelf();
|
|
2590
|
-
const logPrefix = safe.makeLogPrefix('prevent-setInterval', needle, delay);
|
|
2591
|
-
const needleNot = needle.charAt(0) === '!';
|
|
2592
|
-
if ( needleNot ) { needle = needle.slice(1); }
|
|
2593
|
-
if ( delay === '' ) { delay = undefined; }
|
|
2594
|
-
let delayNot = false;
|
|
2595
|
-
if ( delay !== undefined ) {
|
|
2596
|
-
delayNot = delay.charAt(0) === '!';
|
|
2597
|
-
if ( delayNot ) { delay = delay.slice(1); }
|
|
2598
|
-
delay = parseInt(delay, 10);
|
|
2599
|
-
}
|
|
2600
|
-
const reNeedle = safe.patternToRegex(needle);
|
|
2601
|
-
proxyApplyFn('setInterval', function setInterval(context) {
|
|
2602
|
-
const { callArgs } = context;
|
|
2603
|
-
const a = callArgs[0] instanceof Function
|
|
2604
|
-
? String(safe.Function_toString(callArgs[0]))
|
|
2605
|
-
: String(callArgs[0]);
|
|
2606
|
-
const b = callArgs[1];
|
|
2607
|
-
if ( needle === '' && delay === undefined ) {
|
|
2608
|
-
safe.uboLog(logPrefix, `Called:\n${a}\n${b}`);
|
|
2609
|
-
return context.reflect();
|
|
2610
|
-
}
|
|
2611
|
-
let defuse;
|
|
2612
|
-
if ( needle !== '' ) {
|
|
2613
|
-
defuse = reNeedle.test(a) !== needleNot;
|
|
2614
|
-
}
|
|
2615
|
-
if ( defuse !== false && delay !== undefined ) {
|
|
2616
|
-
defuse = (b === delay || isNaN(b) && isNaN(delay) ) !== delayNot;
|
|
2617
|
-
}
|
|
2618
|
-
if ( defuse ) {
|
|
2619
|
-
callArgs[0] = function(){};
|
|
2620
|
-
safe.uboLog(logPrefix, `Prevented:\n${a}\n${b}`);
|
|
2621
|
-
}
|
|
2622
|
-
return context.reflect();
|
|
2623
|
-
});
|
|
2624
|
-
}
|
|
2625
|
-
|
|
2626
|
-
/******************************************************************************/
|
|
2627
|
-
|
|
2628
|
-
builtinScriptlets.push({
|
|
2629
|
-
name: 'no-setTimeout-if.js',
|
|
2630
|
-
aliases: [
|
|
2631
|
-
'nostif.js',
|
|
2632
|
-
'prevent-setTimeout.js',
|
|
2633
|
-
'setTimeout-defuser.js',
|
|
2634
|
-
],
|
|
2635
|
-
fn: noSetTimeoutIf,
|
|
2636
|
-
dependencies: [
|
|
2637
|
-
'proxy-apply.fn',
|
|
2638
|
-
'safe-self.fn',
|
|
2639
|
-
],
|
|
2640
|
-
});
|
|
2641
|
-
function noSetTimeoutIf(
|
|
2642
|
-
needle = '',
|
|
2643
|
-
delay = ''
|
|
2644
|
-
) {
|
|
2645
|
-
if ( typeof needle !== 'string' ) { return; }
|
|
2646
|
-
const safe = safeSelf();
|
|
2647
|
-
const logPrefix = safe.makeLogPrefix('prevent-setTimeout', needle, delay);
|
|
2648
|
-
const needleNot = needle.charAt(0) === '!';
|
|
2649
|
-
if ( needleNot ) { needle = needle.slice(1); }
|
|
2650
|
-
if ( delay === '' ) { delay = undefined; }
|
|
2651
|
-
let delayNot = false;
|
|
2652
|
-
if ( delay !== undefined ) {
|
|
2653
|
-
delayNot = delay.charAt(0) === '!';
|
|
2654
|
-
if ( delayNot ) { delay = delay.slice(1); }
|
|
2655
|
-
delay = parseInt(delay, 10);
|
|
2656
|
-
}
|
|
2657
|
-
const reNeedle = safe.patternToRegex(needle);
|
|
2658
|
-
proxyApplyFn('setTimeout', function setTimeout(context) {
|
|
2659
|
-
const { callArgs } = context;
|
|
2660
|
-
const a = callArgs[0] instanceof Function
|
|
2661
|
-
? String(safe.Function_toString(callArgs[0]))
|
|
2662
|
-
: String(callArgs[0]);
|
|
2663
|
-
const b = callArgs[1];
|
|
2664
|
-
if ( needle === '' && delay === undefined ) {
|
|
2665
|
-
safe.uboLog(logPrefix, `Called:\n${a}\n${b}`);
|
|
2666
|
-
return context.reflect();
|
|
2667
|
-
}
|
|
2668
|
-
let defuse;
|
|
2669
|
-
if ( needle !== '' ) {
|
|
2670
|
-
defuse = reNeedle.test(a) !== needleNot;
|
|
2671
|
-
}
|
|
2672
|
-
if ( defuse !== false && delay !== undefined ) {
|
|
2673
|
-
defuse = (b === delay || isNaN(b) && isNaN(delay) ) !== delayNot;
|
|
2674
|
-
}
|
|
2675
|
-
if ( defuse ) {
|
|
2676
|
-
callArgs[0] = function(){};
|
|
2677
|
-
safe.uboLog(logPrefix, `Prevented:\n${a}\n${b}`);
|
|
2678
|
-
}
|
|
2679
|
-
return context.reflect();
|
|
2680
|
-
});
|
|
1856
|
+
runAt(( ) => {
|
|
1857
|
+
start();
|
|
1858
|
+
}, /\bcomplete\b/.test(behavior) ? 'idle' : 'loading');
|
|
2681
1859
|
}
|
|
2682
1860
|
|
|
2683
1861
|
/******************************************************************************/
|
|
@@ -3090,82 +2268,6 @@ function disableNewtabLinks() {
|
|
|
3090
2268
|
|
|
3091
2269
|
/******************************************************************************/
|
|
3092
2270
|
|
|
3093
|
-
builtinScriptlets.push({
|
|
3094
|
-
name: 'remove-cookie.js',
|
|
3095
|
-
aliases: [
|
|
3096
|
-
'cookie-remover.js',
|
|
3097
|
-
],
|
|
3098
|
-
fn: cookieRemover,
|
|
3099
|
-
world: 'ISOLATED',
|
|
3100
|
-
dependencies: [
|
|
3101
|
-
'safe-self.fn',
|
|
3102
|
-
],
|
|
3103
|
-
});
|
|
3104
|
-
// https://github.com/NanoAdblocker/NanoFilters/issues/149
|
|
3105
|
-
function cookieRemover(
|
|
3106
|
-
needle = ''
|
|
3107
|
-
) {
|
|
3108
|
-
if ( typeof needle !== 'string' ) { return; }
|
|
3109
|
-
const safe = safeSelf();
|
|
3110
|
-
const reName = safe.patternToRegex(needle);
|
|
3111
|
-
const extraArgs = safe.getExtraArgs(Array.from(arguments), 1);
|
|
3112
|
-
const throttle = (fn, ms = 500) => {
|
|
3113
|
-
if ( throttle.timer !== undefined ) { return; }
|
|
3114
|
-
throttle.timer = setTimeout(( ) => {
|
|
3115
|
-
throttle.timer = undefined;
|
|
3116
|
-
fn();
|
|
3117
|
-
}, ms);
|
|
3118
|
-
};
|
|
3119
|
-
const removeCookie = ( ) => {
|
|
3120
|
-
document.cookie.split(';').forEach(cookieStr => {
|
|
3121
|
-
const pos = cookieStr.indexOf('=');
|
|
3122
|
-
if ( pos === -1 ) { return; }
|
|
3123
|
-
const cookieName = cookieStr.slice(0, pos).trim();
|
|
3124
|
-
if ( reName.test(cookieName) === false ) { return; }
|
|
3125
|
-
const part1 = cookieName + '=';
|
|
3126
|
-
const part2a = '; domain=' + document.location.hostname;
|
|
3127
|
-
const part2b = '; domain=.' + document.location.hostname;
|
|
3128
|
-
let part2c, part2d;
|
|
3129
|
-
const domain = document.domain;
|
|
3130
|
-
if ( domain ) {
|
|
3131
|
-
if ( domain !== document.location.hostname ) {
|
|
3132
|
-
part2c = '; domain=.' + domain;
|
|
3133
|
-
}
|
|
3134
|
-
if ( domain.startsWith('www.') ) {
|
|
3135
|
-
part2d = '; domain=' + domain.replace('www', '');
|
|
3136
|
-
}
|
|
3137
|
-
}
|
|
3138
|
-
const part3 = '; path=/';
|
|
3139
|
-
const part4 = '; Max-Age=-1000; expires=Thu, 01 Jan 1970 00:00:00 GMT';
|
|
3140
|
-
document.cookie = part1 + part4;
|
|
3141
|
-
document.cookie = part1 + part2a + part4;
|
|
3142
|
-
document.cookie = part1 + part2b + part4;
|
|
3143
|
-
document.cookie = part1 + part3 + part4;
|
|
3144
|
-
document.cookie = part1 + part2a + part3 + part4;
|
|
3145
|
-
document.cookie = part1 + part2b + part3 + part4;
|
|
3146
|
-
if ( part2c !== undefined ) {
|
|
3147
|
-
document.cookie = part1 + part2c + part3 + part4;
|
|
3148
|
-
}
|
|
3149
|
-
if ( part2d !== undefined ) {
|
|
3150
|
-
document.cookie = part1 + part2d + part3 + part4;
|
|
3151
|
-
}
|
|
3152
|
-
});
|
|
3153
|
-
};
|
|
3154
|
-
removeCookie();
|
|
3155
|
-
window.addEventListener('beforeunload', removeCookie);
|
|
3156
|
-
if ( typeof extraArgs.when !== 'string' ) { return; }
|
|
3157
|
-
const supportedEventTypes = [ 'scroll', 'keydown' ];
|
|
3158
|
-
const eventTypes = extraArgs.when.split(/\s/);
|
|
3159
|
-
for ( const type of eventTypes ) {
|
|
3160
|
-
if ( supportedEventTypes.includes(type) === false ) { continue; }
|
|
3161
|
-
document.addEventListener(type, ( ) => {
|
|
3162
|
-
throttle(removeCookie);
|
|
3163
|
-
}, { passive: true });
|
|
3164
|
-
}
|
|
3165
|
-
}
|
|
3166
|
-
|
|
3167
|
-
/******************************************************************************/
|
|
3168
|
-
|
|
3169
2271
|
builtinScriptlets.push({
|
|
3170
2272
|
name: 'xml-prune.js',
|
|
3171
2273
|
fn: xmlPrune,
|
|
@@ -3415,12 +2517,12 @@ function m3uPrune(
|
|
|
3415
2517
|
}
|
|
3416
2518
|
text = before.trim() + '\n' + after.trim();
|
|
3417
2519
|
reM3u.lastIndex = before.length + 1;
|
|
3418
|
-
toLog.push('Discarding', ...
|
|
2520
|
+
toLog.push('Discarding', ...safe.String_split.call(discard, /\n+/).map(s => `\t${s}`));
|
|
3419
2521
|
if ( reM3u.global === false ) { break; }
|
|
3420
2522
|
}
|
|
3421
2523
|
return text;
|
|
3422
2524
|
}
|
|
3423
|
-
const lines =
|
|
2525
|
+
const lines = safe.String_split.call(text, /\n\r|\n|\r/);
|
|
3424
2526
|
for ( let i = 0; i < lines.length; i++ ) {
|
|
3425
2527
|
if ( lines[i] === undefined ) { continue; }
|
|
3426
2528
|
if ( pruneSpliceoutBlock(lines, i) ) { continue; }
|
|
@@ -3479,163 +2581,6 @@ function m3uPrune(
|
|
|
3479
2581
|
});
|
|
3480
2582
|
}
|
|
3481
2583
|
|
|
3482
|
-
/*******************************************************************************
|
|
3483
|
-
*
|
|
3484
|
-
* @scriptlet href-sanitizer
|
|
3485
|
-
*
|
|
3486
|
-
* @description
|
|
3487
|
-
* Set the `href` attribute to a value found in the DOM at, or below the
|
|
3488
|
-
* targeted `a` element.
|
|
3489
|
-
*
|
|
3490
|
-
* ### Syntax
|
|
3491
|
-
*
|
|
3492
|
-
* ```text
|
|
3493
|
-
* example.org##+js(href-sanitizer, selector [, source])
|
|
3494
|
-
* ```
|
|
3495
|
-
*
|
|
3496
|
-
* - `selector`: required, CSS selector, specifies `a` elements for which the
|
|
3497
|
-
* `href` attribute must be overridden.
|
|
3498
|
-
* - `source`: optional, default to `text`, specifies from where to get the
|
|
3499
|
-
* value which will override the `href` attribute.
|
|
3500
|
-
* - `text`: the value will be the first valid URL found in the text
|
|
3501
|
-
* content of the targeted `a` element.
|
|
3502
|
-
* - `[attr]`: the value will be the attribute _attr_ of the targeted `a`
|
|
3503
|
-
* element.
|
|
3504
|
-
* - `?param`: the value will be the query parameter _param_ of the URL
|
|
3505
|
-
* found in the `href` attribute of the targeted `a` element.
|
|
3506
|
-
*
|
|
3507
|
-
* ### Examples
|
|
3508
|
-
*
|
|
3509
|
-
* example.org##+js(href-sanitizer, a)
|
|
3510
|
-
* example.org##+js(href-sanitizer, a[title], [title])
|
|
3511
|
-
* example.org##+js(href-sanitizer, a[href*="/away.php?to="], ?to)
|
|
3512
|
-
*
|
|
3513
|
-
* */
|
|
3514
|
-
|
|
3515
|
-
builtinScriptlets.push({
|
|
3516
|
-
name: 'href-sanitizer.js',
|
|
3517
|
-
fn: hrefSanitizer,
|
|
3518
|
-
world: 'ISOLATED',
|
|
3519
|
-
dependencies: [
|
|
3520
|
-
'run-at.fn',
|
|
3521
|
-
'safe-self.fn',
|
|
3522
|
-
],
|
|
3523
|
-
});
|
|
3524
|
-
function hrefSanitizer(
|
|
3525
|
-
selector = '',
|
|
3526
|
-
source = ''
|
|
3527
|
-
) {
|
|
3528
|
-
if ( typeof selector !== 'string' ) { return; }
|
|
3529
|
-
if ( selector === '' ) { return; }
|
|
3530
|
-
const safe = safeSelf();
|
|
3531
|
-
const logPrefix = safe.makeLogPrefix('href-sanitizer', selector, source);
|
|
3532
|
-
if ( source === '' ) { source = 'text'; }
|
|
3533
|
-
const sanitizeCopycats = (href, text) => {
|
|
3534
|
-
let elems = [];
|
|
3535
|
-
try {
|
|
3536
|
-
elems = document.querySelectorAll(`a[href="${href}"`);
|
|
3537
|
-
}
|
|
3538
|
-
catch(ex) {
|
|
3539
|
-
}
|
|
3540
|
-
for ( const elem of elems ) {
|
|
3541
|
-
elem.setAttribute('href', text);
|
|
3542
|
-
}
|
|
3543
|
-
return elems.length;
|
|
3544
|
-
};
|
|
3545
|
-
const validateURL = text => {
|
|
3546
|
-
if ( text === '' ) { return ''; }
|
|
3547
|
-
if ( /[\x00-\x20\x7f]/.test(text) ) { return ''; }
|
|
3548
|
-
try {
|
|
3549
|
-
const url = new URL(text, document.location);
|
|
3550
|
-
return url.href;
|
|
3551
|
-
} catch(ex) {
|
|
3552
|
-
}
|
|
3553
|
-
return '';
|
|
3554
|
-
};
|
|
3555
|
-
const extractParam = (href, source) => {
|
|
3556
|
-
if ( Boolean(source) === false ) { return href; }
|
|
3557
|
-
const recursive = source.includes('?', 1);
|
|
3558
|
-
const end = recursive ? source.indexOf('?', 1) : source.length;
|
|
3559
|
-
try {
|
|
3560
|
-
const url = new URL(href, document.location);
|
|
3561
|
-
let value = url.searchParams.get(source.slice(1, end));
|
|
3562
|
-
if ( value === null ) { return href }
|
|
3563
|
-
if ( recursive ) { return extractParam(value, source.slice(end)); }
|
|
3564
|
-
if ( value.includes(' ') ) {
|
|
3565
|
-
value = value.replace(/ /g, '%20');
|
|
3566
|
-
}
|
|
3567
|
-
return value;
|
|
3568
|
-
} catch(x) {
|
|
3569
|
-
}
|
|
3570
|
-
return href;
|
|
3571
|
-
};
|
|
3572
|
-
const extractText = (elem, source) => {
|
|
3573
|
-
if ( /^\[.*\]$/.test(source) ) {
|
|
3574
|
-
return elem.getAttribute(source.slice(1,-1).trim()) || '';
|
|
3575
|
-
}
|
|
3576
|
-
if ( source.startsWith('?') ) {
|
|
3577
|
-
return extractParam(elem.href, source);
|
|
3578
|
-
}
|
|
3579
|
-
if ( source === 'text' ) {
|
|
3580
|
-
return elem.textContent
|
|
3581
|
-
.replace(/^[^\x21-\x7e]+/, '') // remove leading invalid characters
|
|
3582
|
-
.replace(/[^\x21-\x7e]+$/, '') // remove trailing invalid characters
|
|
3583
|
-
;
|
|
3584
|
-
}
|
|
3585
|
-
return '';
|
|
3586
|
-
};
|
|
3587
|
-
const sanitize = ( ) => {
|
|
3588
|
-
let elems = [];
|
|
3589
|
-
try {
|
|
3590
|
-
elems = document.querySelectorAll(selector);
|
|
3591
|
-
}
|
|
3592
|
-
catch(ex) {
|
|
3593
|
-
return false;
|
|
3594
|
-
}
|
|
3595
|
-
for ( const elem of elems ) {
|
|
3596
|
-
if ( elem.localName !== 'a' ) { continue; }
|
|
3597
|
-
if ( elem.hasAttribute('href') === false ) { continue; }
|
|
3598
|
-
const href = elem.getAttribute('href');
|
|
3599
|
-
const text = extractText(elem, source);
|
|
3600
|
-
const hrefAfter = validateURL(text);
|
|
3601
|
-
if ( hrefAfter === '' ) { continue; }
|
|
3602
|
-
if ( hrefAfter === href ) { continue; }
|
|
3603
|
-
elem.setAttribute('href', hrefAfter);
|
|
3604
|
-
const count = sanitizeCopycats(href, hrefAfter);
|
|
3605
|
-
safe.uboLog(logPrefix, `Sanitized ${count+1} links to\n${hrefAfter}`);
|
|
3606
|
-
}
|
|
3607
|
-
return true;
|
|
3608
|
-
};
|
|
3609
|
-
let observer, timer;
|
|
3610
|
-
const onDomChanged = mutations => {
|
|
3611
|
-
if ( timer !== undefined ) { return; }
|
|
3612
|
-
let shouldSanitize = false;
|
|
3613
|
-
for ( const mutation of mutations ) {
|
|
3614
|
-
if ( mutation.addedNodes.length === 0 ) { continue; }
|
|
3615
|
-
for ( const node of mutation.addedNodes ) {
|
|
3616
|
-
if ( node.nodeType !== 1 ) { continue; }
|
|
3617
|
-
shouldSanitize = true;
|
|
3618
|
-
break;
|
|
3619
|
-
}
|
|
3620
|
-
if ( shouldSanitize ) { break; }
|
|
3621
|
-
}
|
|
3622
|
-
if ( shouldSanitize === false ) { return; }
|
|
3623
|
-
timer = safe.onIdle(( ) => {
|
|
3624
|
-
timer = undefined;
|
|
3625
|
-
sanitize();
|
|
3626
|
-
});
|
|
3627
|
-
};
|
|
3628
|
-
const start = ( ) => {
|
|
3629
|
-
if ( sanitize() === false ) { return; }
|
|
3630
|
-
observer = new MutationObserver(onDomChanged);
|
|
3631
|
-
observer.observe(document.body, {
|
|
3632
|
-
subtree: true,
|
|
3633
|
-
childList: true,
|
|
3634
|
-
});
|
|
3635
|
-
};
|
|
3636
|
-
runAt(( ) => { start(); }, 'interactive');
|
|
3637
|
-
}
|
|
3638
|
-
|
|
3639
2584
|
/*******************************************************************************
|
|
3640
2585
|
*
|
|
3641
2586
|
* @scriptlet call-nothrow
|
|
@@ -3663,13 +2608,17 @@ function hrefSanitizer(
|
|
|
3663
2608
|
builtinScriptlets.push({
|
|
3664
2609
|
name: 'call-nothrow.js',
|
|
3665
2610
|
fn: callNothrow,
|
|
2611
|
+
dependencies: [
|
|
2612
|
+
'safe-self.fn',
|
|
2613
|
+
],
|
|
3666
2614
|
});
|
|
3667
2615
|
function callNothrow(
|
|
3668
2616
|
chain = ''
|
|
3669
2617
|
) {
|
|
3670
2618
|
if ( typeof chain !== 'string' ) { return; }
|
|
3671
2619
|
if ( chain === '' ) { return; }
|
|
3672
|
-
const
|
|
2620
|
+
const safe = safeSelf();
|
|
2621
|
+
const parts = safe.String_split.call(chain, '.');
|
|
3673
2622
|
let owner = window, prop;
|
|
3674
2623
|
for (;;) {
|
|
3675
2624
|
prop = parts.shift();
|
|
@@ -3692,120 +2641,6 @@ function callNothrow(
|
|
|
3692
2641
|
});
|
|
3693
2642
|
}
|
|
3694
2643
|
|
|
3695
|
-
|
|
3696
|
-
/******************************************************************************/
|
|
3697
|
-
|
|
3698
|
-
builtinScriptlets.push({
|
|
3699
|
-
name: 'spoof-css.js',
|
|
3700
|
-
fn: spoofCSS,
|
|
3701
|
-
dependencies: [
|
|
3702
|
-
'safe-self.fn',
|
|
3703
|
-
],
|
|
3704
|
-
});
|
|
3705
|
-
function spoofCSS(
|
|
3706
|
-
selector,
|
|
3707
|
-
...args
|
|
3708
|
-
) {
|
|
3709
|
-
if ( typeof selector !== 'string' ) { return; }
|
|
3710
|
-
if ( selector === '' ) { return; }
|
|
3711
|
-
const toCamelCase = s => s.replace(/-[a-z]/g, s => s.charAt(1).toUpperCase());
|
|
3712
|
-
const propToValueMap = new Map();
|
|
3713
|
-
for ( let i = 0; i < args.length; i += 2 ) {
|
|
3714
|
-
if ( typeof args[i+0] !== 'string' ) { break; }
|
|
3715
|
-
if ( args[i+0] === '' ) { break; }
|
|
3716
|
-
if ( typeof args[i+1] !== 'string' ) { break; }
|
|
3717
|
-
propToValueMap.set(toCamelCase(args[i+0]), args[i+1]);
|
|
3718
|
-
}
|
|
3719
|
-
const safe = safeSelf();
|
|
3720
|
-
const logPrefix = safe.makeLogPrefix('spoof-css', selector, ...args);
|
|
3721
|
-
const canDebug = scriptletGlobals.canDebug;
|
|
3722
|
-
const shouldDebug = canDebug && propToValueMap.get('debug') || 0;
|
|
3723
|
-
const instanceProperties = [ 'cssText', 'length', 'parentRule' ];
|
|
3724
|
-
const spoofStyle = (prop, real) => {
|
|
3725
|
-
const normalProp = toCamelCase(prop);
|
|
3726
|
-
const shouldSpoof = propToValueMap.has(normalProp);
|
|
3727
|
-
const value = shouldSpoof ? propToValueMap.get(normalProp) : real;
|
|
3728
|
-
if ( shouldSpoof ) {
|
|
3729
|
-
safe.uboLog(logPrefix, `Spoofing ${prop} to ${value}`);
|
|
3730
|
-
}
|
|
3731
|
-
return value;
|
|
3732
|
-
};
|
|
3733
|
-
const cloackFunc = (fn, thisArg, name) => {
|
|
3734
|
-
const trap = fn.bind(thisArg);
|
|
3735
|
-
Object.defineProperty(trap, 'name', { value: name });
|
|
3736
|
-
Object.defineProperty(trap, 'toString', {
|
|
3737
|
-
value: ( ) => `function ${name}() { [native code] }`
|
|
3738
|
-
});
|
|
3739
|
-
return trap;
|
|
3740
|
-
};
|
|
3741
|
-
self.getComputedStyle = new Proxy(self.getComputedStyle, {
|
|
3742
|
-
apply: function(target, thisArg, args) {
|
|
3743
|
-
// eslint-disable-next-line no-debugger
|
|
3744
|
-
if ( shouldDebug !== 0 ) { debugger; }
|
|
3745
|
-
const style = Reflect.apply(target, thisArg, args);
|
|
3746
|
-
const targetElements = new WeakSet(document.querySelectorAll(selector));
|
|
3747
|
-
if ( targetElements.has(args[0]) === false ) { return style; }
|
|
3748
|
-
const proxiedStyle = new Proxy(style, {
|
|
3749
|
-
get(target, prop) {
|
|
3750
|
-
if ( typeof target[prop] === 'function' ) {
|
|
3751
|
-
if ( prop === 'getPropertyValue' ) {
|
|
3752
|
-
return cloackFunc(function getPropertyValue(prop) {
|
|
3753
|
-
return spoofStyle(prop, target[prop]);
|
|
3754
|
-
}, target, 'getPropertyValue');
|
|
3755
|
-
}
|
|
3756
|
-
return cloackFunc(target[prop], target, prop);
|
|
3757
|
-
}
|
|
3758
|
-
if ( instanceProperties.includes(prop) ) {
|
|
3759
|
-
return Reflect.get(target, prop);
|
|
3760
|
-
}
|
|
3761
|
-
return spoofStyle(prop, Reflect.get(target, prop));
|
|
3762
|
-
},
|
|
3763
|
-
getOwnPropertyDescriptor(target, prop) {
|
|
3764
|
-
if ( propToValueMap.has(prop) ) {
|
|
3765
|
-
return {
|
|
3766
|
-
configurable: true,
|
|
3767
|
-
enumerable: true,
|
|
3768
|
-
value: propToValueMap.get(prop),
|
|
3769
|
-
writable: true,
|
|
3770
|
-
};
|
|
3771
|
-
}
|
|
3772
|
-
return Reflect.getOwnPropertyDescriptor(target, prop);
|
|
3773
|
-
},
|
|
3774
|
-
});
|
|
3775
|
-
return proxiedStyle;
|
|
3776
|
-
},
|
|
3777
|
-
get(target, prop) {
|
|
3778
|
-
if ( prop === 'toString' ) {
|
|
3779
|
-
return target.toString.bind(target);
|
|
3780
|
-
}
|
|
3781
|
-
return Reflect.get(target, prop);
|
|
3782
|
-
},
|
|
3783
|
-
});
|
|
3784
|
-
Element.prototype.getBoundingClientRect = new Proxy(Element.prototype.getBoundingClientRect, {
|
|
3785
|
-
apply: function(target, thisArg, args) {
|
|
3786
|
-
// eslint-disable-next-line no-debugger
|
|
3787
|
-
if ( shouldDebug !== 0 ) { debugger; }
|
|
3788
|
-
const rect = Reflect.apply(target, thisArg, args);
|
|
3789
|
-
const targetElements = new WeakSet(document.querySelectorAll(selector));
|
|
3790
|
-
if ( targetElements.has(thisArg) === false ) { return rect; }
|
|
3791
|
-
let { height, width } = rect;
|
|
3792
|
-
if ( propToValueMap.has('width') ) {
|
|
3793
|
-
width = parseFloat(propToValueMap.get('width'));
|
|
3794
|
-
}
|
|
3795
|
-
if ( propToValueMap.has('height') ) {
|
|
3796
|
-
height = parseFloat(propToValueMap.get('height'));
|
|
3797
|
-
}
|
|
3798
|
-
return new self.DOMRect(rect.x, rect.y, width, height);
|
|
3799
|
-
},
|
|
3800
|
-
get(target, prop) {
|
|
3801
|
-
if ( prop === 'toString' ) {
|
|
3802
|
-
return target.toString.bind(target);
|
|
3803
|
-
}
|
|
3804
|
-
return Reflect.get(target, prop);
|
|
3805
|
-
},
|
|
3806
|
-
});
|
|
3807
|
-
}
|
|
3808
|
-
|
|
3809
2644
|
/******************************************************************************/
|
|
3810
2645
|
|
|
3811
2646
|
builtinScriptlets.push({
|
|
@@ -3827,109 +2662,6 @@ function removeNodeText(
|
|
|
3827
2662
|
replaceNodeTextFn(nodeName, '', '', 'includes', includes || '', ...extraArgs);
|
|
3828
2663
|
}
|
|
3829
2664
|
|
|
3830
|
-
/*******************************************************************************
|
|
3831
|
-
*
|
|
3832
|
-
* set-cookie.js
|
|
3833
|
-
*
|
|
3834
|
-
* Set specified cookie to a specific value.
|
|
3835
|
-
*
|
|
3836
|
-
* Reference:
|
|
3837
|
-
* https://github.com/AdguardTeam/Scriptlets/blob/master/src/scriptlets/set-cookie.js
|
|
3838
|
-
*
|
|
3839
|
-
**/
|
|
3840
|
-
|
|
3841
|
-
builtinScriptlets.push({
|
|
3842
|
-
name: 'set-cookie.js',
|
|
3843
|
-
fn: setCookie,
|
|
3844
|
-
world: 'ISOLATED',
|
|
3845
|
-
dependencies: [
|
|
3846
|
-
'get-safe-cookie-values.fn',
|
|
3847
|
-
'safe-self.fn',
|
|
3848
|
-
'set-cookie.fn',
|
|
3849
|
-
],
|
|
3850
|
-
});
|
|
3851
|
-
function setCookie(
|
|
3852
|
-
name = '',
|
|
3853
|
-
value = '',
|
|
3854
|
-
path = ''
|
|
3855
|
-
) {
|
|
3856
|
-
if ( name === '' ) { return; }
|
|
3857
|
-
const safe = safeSelf();
|
|
3858
|
-
const logPrefix = safe.makeLogPrefix('set-cookie', name, value, path);
|
|
3859
|
-
const normalized = value.toLowerCase();
|
|
3860
|
-
const match = /^("?)(.+)\1$/.exec(normalized);
|
|
3861
|
-
const unquoted = match && match[2] || normalized;
|
|
3862
|
-
const validValues = getSafeCookieValuesFn();
|
|
3863
|
-
if ( validValues.includes(unquoted) === false ) {
|
|
3864
|
-
if ( /^\d+$/.test(unquoted) === false ) { return; }
|
|
3865
|
-
const n = parseInt(value, 10);
|
|
3866
|
-
if ( n > 32767 ) { return; }
|
|
3867
|
-
}
|
|
3868
|
-
|
|
3869
|
-
const done = setCookieFn(
|
|
3870
|
-
false,
|
|
3871
|
-
name,
|
|
3872
|
-
value,
|
|
3873
|
-
'',
|
|
3874
|
-
path,
|
|
3875
|
-
safe.getExtraArgs(Array.from(arguments), 3)
|
|
3876
|
-
);
|
|
3877
|
-
|
|
3878
|
-
if ( done ) {
|
|
3879
|
-
safe.uboLog(logPrefix, 'Done');
|
|
3880
|
-
}
|
|
3881
|
-
}
|
|
3882
|
-
|
|
3883
|
-
// For compatibility with AdGuard
|
|
3884
|
-
builtinScriptlets.push({
|
|
3885
|
-
name: 'set-cookie-reload.js',
|
|
3886
|
-
fn: setCookieReload,
|
|
3887
|
-
world: 'ISOLATED',
|
|
3888
|
-
dependencies: [
|
|
3889
|
-
'set-cookie.js',
|
|
3890
|
-
],
|
|
3891
|
-
});
|
|
3892
|
-
function setCookieReload(name, value, path, ...args) {
|
|
3893
|
-
setCookie(name, value, path, 'reload', '1', ...args);
|
|
3894
|
-
}
|
|
3895
|
-
|
|
3896
|
-
/*******************************************************************************
|
|
3897
|
-
*
|
|
3898
|
-
* set-local-storage-item.js
|
|
3899
|
-
* set-session-storage-item.js
|
|
3900
|
-
*
|
|
3901
|
-
* Set a local/session storage entry to a specific, allowed value.
|
|
3902
|
-
*
|
|
3903
|
-
* Reference:
|
|
3904
|
-
* https://github.com/AdguardTeam/Scriptlets/blob/master/src/scriptlets/set-local-storage-item.js
|
|
3905
|
-
* https://github.com/AdguardTeam/Scriptlets/blob/master/src/scriptlets/set-session-storage-item.js
|
|
3906
|
-
*
|
|
3907
|
-
**/
|
|
3908
|
-
|
|
3909
|
-
builtinScriptlets.push({
|
|
3910
|
-
name: 'set-local-storage-item.js',
|
|
3911
|
-
fn: setLocalStorageItem,
|
|
3912
|
-
world: 'ISOLATED',
|
|
3913
|
-
dependencies: [
|
|
3914
|
-
'set-local-storage-item.fn',
|
|
3915
|
-
],
|
|
3916
|
-
});
|
|
3917
|
-
function setLocalStorageItem(key = '', value = '') {
|
|
3918
|
-
setLocalStorageItemFn('local', false, key, value);
|
|
3919
|
-
}
|
|
3920
|
-
|
|
3921
|
-
builtinScriptlets.push({
|
|
3922
|
-
name: 'set-session-storage-item.js',
|
|
3923
|
-
fn: setSessionStorageItem,
|
|
3924
|
-
world: 'ISOLATED',
|
|
3925
|
-
dependencies: [
|
|
3926
|
-
'set-local-storage-item.fn',
|
|
3927
|
-
],
|
|
3928
|
-
});
|
|
3929
|
-
function setSessionStorageItem(key = '', value = '') {
|
|
3930
|
-
setLocalStorageItemFn('session', false, key, value);
|
|
3931
|
-
}
|
|
3932
|
-
|
|
3933
2665
|
/*******************************************************************************
|
|
3934
2666
|
*
|
|
3935
2667
|
* @scriptlet prevent-canvas
|
|
@@ -4017,57 +2749,6 @@ function multiup() {
|
|
|
4017
2749
|
document.addEventListener('click', handler, { capture: true });
|
|
4018
2750
|
}
|
|
4019
2751
|
|
|
4020
|
-
/******************************************************************************/
|
|
4021
|
-
|
|
4022
|
-
builtinScriptlets.push({
|
|
4023
|
-
name: 'remove-cache-storage-item.js',
|
|
4024
|
-
fn: removeCacheStorageItem,
|
|
4025
|
-
world: 'ISOLATED',
|
|
4026
|
-
dependencies: [
|
|
4027
|
-
'safe-self.fn',
|
|
4028
|
-
],
|
|
4029
|
-
});
|
|
4030
|
-
function removeCacheStorageItem(
|
|
4031
|
-
cacheNamePattern = '',
|
|
4032
|
-
requestPattern = ''
|
|
4033
|
-
) {
|
|
4034
|
-
if ( cacheNamePattern === '' ) { return; }
|
|
4035
|
-
const safe = safeSelf();
|
|
4036
|
-
const logPrefix = safe.makeLogPrefix('remove-cache-storage-item', cacheNamePattern, requestPattern);
|
|
4037
|
-
const cacheStorage = self.caches;
|
|
4038
|
-
if ( cacheStorage instanceof Object === false ) { return; }
|
|
4039
|
-
const reCache = safe.patternToRegex(cacheNamePattern, undefined, true);
|
|
4040
|
-
const reRequest = safe.patternToRegex(requestPattern, undefined, true);
|
|
4041
|
-
cacheStorage.keys().then(cacheNames => {
|
|
4042
|
-
for ( const cacheName of cacheNames ) {
|
|
4043
|
-
if ( reCache.test(cacheName) === false ) { continue; }
|
|
4044
|
-
if ( requestPattern === '' ) {
|
|
4045
|
-
cacheStorage.delete(cacheName).then(result => {
|
|
4046
|
-
if ( safe.logLevel > 1 ) {
|
|
4047
|
-
safe.uboLog(logPrefix, `Deleting ${cacheName}`);
|
|
4048
|
-
}
|
|
4049
|
-
if ( result !== true ) { return; }
|
|
4050
|
-
safe.uboLog(logPrefix, `Deleted ${cacheName}: ${result}`);
|
|
4051
|
-
});
|
|
4052
|
-
continue;
|
|
4053
|
-
}
|
|
4054
|
-
cacheStorage.open(cacheName).then(cache => {
|
|
4055
|
-
cache.keys().then(requests => {
|
|
4056
|
-
for ( const request of requests ) {
|
|
4057
|
-
if ( reRequest.test(request.url) === false ) { continue; }
|
|
4058
|
-
if ( safe.logLevel > 1 ) {
|
|
4059
|
-
safe.uboLog(logPrefix, `Deleting ${cacheName}/${request.url}`);
|
|
4060
|
-
}
|
|
4061
|
-
cache.delete(request).then(result => {
|
|
4062
|
-
if ( result !== true ) { return; }
|
|
4063
|
-
safe.uboLog(logPrefix, `Deleted ${cacheName}/${request.url}: ${result}`);
|
|
4064
|
-
});
|
|
4065
|
-
}
|
|
4066
|
-
});
|
|
4067
|
-
});
|
|
4068
|
-
}
|
|
4069
|
-
});
|
|
4070
|
-
}
|
|
4071
2752
|
|
|
4072
2753
|
|
|
4073
2754
|
/*******************************************************************************
|
|
@@ -4129,153 +2810,6 @@ function replaceNodeText(
|
|
|
4129
2810
|
replaceNodeTextFn(nodeName, pattern, replacement, ...extraArgs);
|
|
4130
2811
|
}
|
|
4131
2812
|
|
|
4132
|
-
/*******************************************************************************
|
|
4133
|
-
*
|
|
4134
|
-
* trusted-set-constant.js
|
|
4135
|
-
*
|
|
4136
|
-
* Set specified property to any value. This is essentially the same as
|
|
4137
|
-
* set-constant.js, but with no restriction as to which values can be used.
|
|
4138
|
-
*
|
|
4139
|
-
**/
|
|
4140
|
-
|
|
4141
|
-
builtinScriptlets.push({
|
|
4142
|
-
name: 'trusted-set-constant.js',
|
|
4143
|
-
requiresTrust: true,
|
|
4144
|
-
aliases: [
|
|
4145
|
-
'trusted-set.js',
|
|
4146
|
-
],
|
|
4147
|
-
fn: trustedSetConstant,
|
|
4148
|
-
dependencies: [
|
|
4149
|
-
'set-constant.fn'
|
|
4150
|
-
],
|
|
4151
|
-
});
|
|
4152
|
-
function trustedSetConstant(
|
|
4153
|
-
...args
|
|
4154
|
-
) {
|
|
4155
|
-
setConstantFn(true, ...args);
|
|
4156
|
-
}
|
|
4157
|
-
|
|
4158
|
-
/*******************************************************************************
|
|
4159
|
-
*
|
|
4160
|
-
* trusted-set-cookie.js
|
|
4161
|
-
*
|
|
4162
|
-
* Set specified cookie to an arbitrary value.
|
|
4163
|
-
*
|
|
4164
|
-
* Reference:
|
|
4165
|
-
* https://github.com/AdguardTeam/Scriptlets/blob/master/src/scriptlets/trusted-set-cookie.js#L23
|
|
4166
|
-
*
|
|
4167
|
-
**/
|
|
4168
|
-
|
|
4169
|
-
builtinScriptlets.push({
|
|
4170
|
-
name: 'trusted-set-cookie.js',
|
|
4171
|
-
requiresTrust: true,
|
|
4172
|
-
fn: trustedSetCookie,
|
|
4173
|
-
world: 'ISOLATED',
|
|
4174
|
-
dependencies: [
|
|
4175
|
-
'safe-self.fn',
|
|
4176
|
-
'set-cookie.fn',
|
|
4177
|
-
],
|
|
4178
|
-
});
|
|
4179
|
-
function trustedSetCookie(
|
|
4180
|
-
name = '',
|
|
4181
|
-
value = '',
|
|
4182
|
-
offsetExpiresSec = '',
|
|
4183
|
-
path = ''
|
|
4184
|
-
) {
|
|
4185
|
-
if ( name === '' ) { return; }
|
|
4186
|
-
|
|
4187
|
-
const safe = safeSelf();
|
|
4188
|
-
const logPrefix = safe.makeLogPrefix('set-cookie', name, value, path);
|
|
4189
|
-
const time = new Date();
|
|
4190
|
-
|
|
4191
|
-
if ( value.includes('$now$') ) {
|
|
4192
|
-
value = value.replaceAll('$now$', time.getTime());
|
|
4193
|
-
}
|
|
4194
|
-
if ( value.includes('$currentDate$') ) {
|
|
4195
|
-
value = value.replaceAll('$currentDate$', time.toUTCString());
|
|
4196
|
-
}
|
|
4197
|
-
if ( value.includes('$currentISODate$') ) {
|
|
4198
|
-
value = value.replaceAll('$currentISODate$', time.toISOString());
|
|
4199
|
-
}
|
|
4200
|
-
|
|
4201
|
-
let expires = '';
|
|
4202
|
-
if ( offsetExpiresSec !== '' ) {
|
|
4203
|
-
if ( offsetExpiresSec === '1day' ) {
|
|
4204
|
-
time.setDate(time.getDate() + 1);
|
|
4205
|
-
} else if ( offsetExpiresSec === '1year' ) {
|
|
4206
|
-
time.setFullYear(time.getFullYear() + 1);
|
|
4207
|
-
} else {
|
|
4208
|
-
if ( /^\d+$/.test(offsetExpiresSec) === false ) { return; }
|
|
4209
|
-
time.setSeconds(time.getSeconds() + parseInt(offsetExpiresSec, 10));
|
|
4210
|
-
}
|
|
4211
|
-
expires = time.toUTCString();
|
|
4212
|
-
}
|
|
4213
|
-
|
|
4214
|
-
const done = setCookieFn(
|
|
4215
|
-
true,
|
|
4216
|
-
name,
|
|
4217
|
-
value,
|
|
4218
|
-
expires,
|
|
4219
|
-
path,
|
|
4220
|
-
safeSelf().getExtraArgs(Array.from(arguments), 4)
|
|
4221
|
-
);
|
|
4222
|
-
|
|
4223
|
-
if ( done ) {
|
|
4224
|
-
safe.uboLog(logPrefix, 'Done');
|
|
4225
|
-
}
|
|
4226
|
-
}
|
|
4227
|
-
|
|
4228
|
-
// For compatibility with AdGuard
|
|
4229
|
-
builtinScriptlets.push({
|
|
4230
|
-
name: 'trusted-set-cookie-reload.js',
|
|
4231
|
-
requiresTrust: true,
|
|
4232
|
-
fn: trustedSetCookieReload,
|
|
4233
|
-
world: 'ISOLATED',
|
|
4234
|
-
dependencies: [
|
|
4235
|
-
'trusted-set-cookie.js',
|
|
4236
|
-
],
|
|
4237
|
-
});
|
|
4238
|
-
function trustedSetCookieReload(name, value, offsetExpiresSec, path, ...args) {
|
|
4239
|
-
trustedSetCookie(name, value, offsetExpiresSec, path, 'reload', '1', ...args);
|
|
4240
|
-
}
|
|
4241
|
-
|
|
4242
|
-
/*******************************************************************************
|
|
4243
|
-
*
|
|
4244
|
-
* trusted-set-local-storage-item.js
|
|
4245
|
-
*
|
|
4246
|
-
* Set a local storage entry to an arbitrary value.
|
|
4247
|
-
*
|
|
4248
|
-
* Reference:
|
|
4249
|
-
* https://github.com/AdguardTeam/Scriptlets/blob/master/src/scriptlets/trusted-set-local-storage-item.js
|
|
4250
|
-
*
|
|
4251
|
-
**/
|
|
4252
|
-
|
|
4253
|
-
builtinScriptlets.push({
|
|
4254
|
-
name: 'trusted-set-local-storage-item.js',
|
|
4255
|
-
requiresTrust: true,
|
|
4256
|
-
fn: trustedSetLocalStorageItem,
|
|
4257
|
-
world: 'ISOLATED',
|
|
4258
|
-
dependencies: [
|
|
4259
|
-
'set-local-storage-item.fn',
|
|
4260
|
-
],
|
|
4261
|
-
});
|
|
4262
|
-
function trustedSetLocalStorageItem(key = '', value = '') {
|
|
4263
|
-
setLocalStorageItemFn('local', true, key, value);
|
|
4264
|
-
}
|
|
4265
|
-
|
|
4266
|
-
builtinScriptlets.push({
|
|
4267
|
-
name: 'trusted-set-session-storage-item.js',
|
|
4268
|
-
requiresTrust: true,
|
|
4269
|
-
fn: trustedSetSessionStorageItem,
|
|
4270
|
-
world: 'ISOLATED',
|
|
4271
|
-
dependencies: [
|
|
4272
|
-
'set-local-storage-item.fn',
|
|
4273
|
-
],
|
|
4274
|
-
});
|
|
4275
|
-
function trustedSetSessionStorageItem(key = '', value = '') {
|
|
4276
|
-
setLocalStorageItemFn('session', true, key, value);
|
|
4277
|
-
}
|
|
4278
|
-
|
|
4279
2813
|
/*******************************************************************************
|
|
4280
2814
|
*
|
|
4281
2815
|
* trusted-replace-fetch-response.js
|
|
@@ -4415,7 +2949,7 @@ function trustedClickElement(
|
|
|
4415
2949
|
const logPrefix = safe.makeLogPrefix('trusted-click-element', selectors, extraMatch, delay);
|
|
4416
2950
|
|
|
4417
2951
|
if ( extraMatch !== '' ) {
|
|
4418
|
-
const assertions =
|
|
2952
|
+
const assertions = safe.String_split.call(extraMatch, ',').map(s => {
|
|
4419
2953
|
const pos1 = s.indexOf(':');
|
|
4420
2954
|
const s1 = pos1 !== -1 ? s.slice(0, pos1) : s;
|
|
4421
2955
|
const not = s1.startsWith('!');
|
|
@@ -4483,7 +3017,7 @@ function trustedClickElement(
|
|
|
4483
3017
|
return shadowRoot && querySelectorEx(inside, shadowRoot);
|
|
4484
3018
|
};
|
|
4485
3019
|
|
|
4486
|
-
const selectorList =
|
|
3020
|
+
const selectorList = safe.String_split.call(selectors, /\s*,\s*/)
|
|
4487
3021
|
.filter(s => {
|
|
4488
3022
|
try {
|
|
4489
3023
|
void querySelectorEx(s);
|
|
@@ -4610,10 +3144,10 @@ function trustedPruneInboundObject(
|
|
|
4610
3144
|
const extraArgs = safe.getExtraArgs(Array.from(arguments), 4);
|
|
4611
3145
|
const needlePaths = [];
|
|
4612
3146
|
if ( rawPrunePaths !== '' ) {
|
|
4613
|
-
needlePaths.push(...
|
|
3147
|
+
needlePaths.push(...safe.String_split.call(rawPrunePaths, / +/));
|
|
4614
3148
|
}
|
|
4615
3149
|
if ( rawNeedlePaths !== '' ) {
|
|
4616
|
-
needlePaths.push(...
|
|
3150
|
+
needlePaths.push(...safe.String_split.call(rawNeedlePaths, / +/));
|
|
4617
3151
|
}
|
|
4618
3152
|
const stackNeedle = safe.initPattern(extraArgs.stackToMatch || '', { canNegate: true });
|
|
4619
3153
|
const mustProcess = root => {
|
|
@@ -4690,50 +3224,6 @@ function trustedPruneOutboundObject(
|
|
|
4690
3224
|
|
|
4691
3225
|
/******************************************************************************/
|
|
4692
3226
|
|
|
4693
|
-
builtinScriptlets.push({
|
|
4694
|
-
name: 'trusted-replace-argument.js',
|
|
4695
|
-
requiresTrust: true,
|
|
4696
|
-
fn: trustedReplaceArgument,
|
|
4697
|
-
dependencies: [
|
|
4698
|
-
'proxy-apply.fn',
|
|
4699
|
-
'safe-self.fn',
|
|
4700
|
-
'validate-constant.fn',
|
|
4701
|
-
],
|
|
4702
|
-
});
|
|
4703
|
-
function trustedReplaceArgument(
|
|
4704
|
-
propChain = '',
|
|
4705
|
-
argposRaw = '',
|
|
4706
|
-
argraw = ''
|
|
4707
|
-
) {
|
|
4708
|
-
if ( propChain === '' ) { return; }
|
|
4709
|
-
const safe = safeSelf();
|
|
4710
|
-
const logPrefix = safe.makeLogPrefix('trusted-replace-argument', propChain, argposRaw, argraw);
|
|
4711
|
-
const argoffset = parseInt(argposRaw, 10) || 0;
|
|
4712
|
-
const extraArgs = safe.getExtraArgs(Array.from(arguments), 3);
|
|
4713
|
-
const normalValue = validateConstantFn(true, argraw, extraArgs);
|
|
4714
|
-
const reCondition = extraArgs.condition
|
|
4715
|
-
? safe.patternToRegex(extraArgs.condition)
|
|
4716
|
-
: /^/;
|
|
4717
|
-
proxyApplyFn(propChain, function(context) {
|
|
4718
|
-
const { callArgs } = context;
|
|
4719
|
-
if ( argposRaw === '' ) {
|
|
4720
|
-
safe.uboLog(logPrefix, `Arguments:\n${callArgs.join('\n')}`);
|
|
4721
|
-
return context.reflect();
|
|
4722
|
-
}
|
|
4723
|
-
const argpos = argoffset >= 0 ? argoffset : callArgs.length - argoffset;
|
|
4724
|
-
if ( argpos >= 0 && argpos < callArgs.length ) {
|
|
4725
|
-
const argBefore = callArgs[argpos];
|
|
4726
|
-
if ( safe.RegExp_test.call(reCondition, argBefore) ) {
|
|
4727
|
-
callArgs[argpos] = normalValue;
|
|
4728
|
-
safe.uboLog(logPrefix, `Replaced argument:\nBefore: ${JSON.stringify(argBefore)}\nAfter: ${normalValue}`);
|
|
4729
|
-
}
|
|
4730
|
-
}
|
|
4731
|
-
return context.reflect();
|
|
4732
|
-
});
|
|
4733
|
-
}
|
|
4734
|
-
|
|
4735
|
-
/******************************************************************************/
|
|
4736
|
-
|
|
4737
3227
|
builtinScriptlets.push({
|
|
4738
3228
|
name: 'trusted-replace-outbound-text.js',
|
|
4739
3229
|
requiresTrust: true,
|
|
@@ -4805,6 +3295,7 @@ builtinScriptlets.push({
|
|
|
4805
3295
|
requiresTrust: true,
|
|
4806
3296
|
fn: trustedSuppressNativeMethod,
|
|
4807
3297
|
dependencies: [
|
|
3298
|
+
'matches-stack-trace.fn',
|
|
4808
3299
|
'proxy-apply.fn',
|
|
4809
3300
|
'safe-self.fn',
|
|
4810
3301
|
],
|
|
@@ -4816,13 +3307,15 @@ function trustedSuppressNativeMethod(
|
|
|
4816
3307
|
stack = ''
|
|
4817
3308
|
) {
|
|
4818
3309
|
if ( methodPath === '' ) { return; }
|
|
4819
|
-
if ( stack !== '' ) { return; }
|
|
4820
3310
|
const safe = safeSelf();
|
|
4821
|
-
const logPrefix = safe.makeLogPrefix('trusted-suppress-native-method', methodPath, signature, how);
|
|
4822
|
-
const signatureArgs =
|
|
3311
|
+
const logPrefix = safe.makeLogPrefix('trusted-suppress-native-method', methodPath, signature, how, stack);
|
|
3312
|
+
const signatureArgs = safe.String_split.call(signature, /\s*\|\s*/).map(v => {
|
|
4823
3313
|
if ( /^".*"$/.test(v) ) {
|
|
4824
3314
|
return { type: 'pattern', re: safe.patternToRegex(v.slice(1, -1)) };
|
|
4825
3315
|
}
|
|
3316
|
+
if ( /^\/.+\/$/.test(v) ) {
|
|
3317
|
+
return { type: 'pattern', re: safe.patternToRegex(v) };
|
|
3318
|
+
}
|
|
4826
3319
|
if ( v === 'false' ) {
|
|
4827
3320
|
return { type: 'exact', value: false };
|
|
4828
3321
|
}
|
|
@@ -4836,19 +3329,17 @@ function trustedSuppressNativeMethod(
|
|
|
4836
3329
|
return { type: 'exact', value: undefined };
|
|
4837
3330
|
}
|
|
4838
3331
|
});
|
|
3332
|
+
const stackNeedle = safe.initPattern(stack, { canNegate: true });
|
|
4839
3333
|
proxyApplyFn(methodPath, function(context) {
|
|
4840
3334
|
const { callArgs } = context;
|
|
4841
3335
|
if ( signature === '' ) {
|
|
4842
3336
|
safe.uboLog(logPrefix, `Arguments:\n${callArgs.join('\n')}`);
|
|
4843
3337
|
return context.reflect();
|
|
4844
3338
|
}
|
|
4845
|
-
if ( callArgs.length < signatureArgs.length ) {
|
|
4846
|
-
return context.reflect();
|
|
4847
|
-
}
|
|
4848
3339
|
for ( let i = 0; i < signatureArgs.length; i++ ) {
|
|
4849
3340
|
const signatureArg = signatureArgs[i];
|
|
4850
3341
|
if ( signatureArg === undefined ) { continue; }
|
|
4851
|
-
const targetArg = callArgs[i];
|
|
3342
|
+
const targetArg = i < callArgs.length ? callArgs[i] : undefined;
|
|
4852
3343
|
if ( signatureArg.type === 'exact' ) {
|
|
4853
3344
|
if ( targetArg !== signatureArg.value ) {
|
|
4854
3345
|
return context.reflect();
|
|
@@ -4860,6 +3351,16 @@ function trustedSuppressNativeMethod(
|
|
|
4860
3351
|
}
|
|
4861
3352
|
}
|
|
4862
3353
|
}
|
|
3354
|
+
if ( stackNeedle.matchAll !== true ) {
|
|
3355
|
+
const logLevel = safe.logLevel > 1 ? 'all' : '';
|
|
3356
|
+
if ( matchesStackTraceFn(stackNeedle, logLevel) === false ) {
|
|
3357
|
+
return context.reflect();
|
|
3358
|
+
}
|
|
3359
|
+
}
|
|
3360
|
+
if ( how === 'debug' ) {
|
|
3361
|
+
debugger; // eslint-disable-line no-debugger
|
|
3362
|
+
return context.reflect();
|
|
3363
|
+
}
|
|
4863
3364
|
safe.uboLog(logPrefix, `Suppressed:\n${callArgs.join('\n')}`);
|
|
4864
3365
|
if ( how === 'abort' ) {
|
|
4865
3366
|
throw new ReferenceError();
|