@browserless.io/browserless 2.24.1 → 2.24.3

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.
Files changed (83) hide show
  1. package/CHANGELOG.md +19 -4
  2. package/README.md +3 -4
  3. package/build/browsers/browsers.playwright.js +6 -1
  4. package/build/browsers/index.js +1 -1
  5. package/build/routes/chrome/http/content.post.body.json +8 -8
  6. package/build/routes/chrome/http/pdf.post.body.json +8 -8
  7. package/build/routes/chrome/http/scrape.post.body.json +8 -8
  8. package/build/routes/chrome/http/screenshot.post.body.json +8 -8
  9. package/build/routes/chromium/http/content.post.body.json +8 -8
  10. package/build/routes/chromium/http/pdf.post.body.json +8 -8
  11. package/build/routes/chromium/http/scrape.post.body.json +8 -8
  12. package/build/routes/chromium/http/screenshot.post.body.json +8 -8
  13. package/build/routes/management/http/meta.get.js +3 -1
  14. package/extensions/ublock/_locales/ar/messages.json +3 -3
  15. package/extensions/ublock/_locales/bg/messages.json +1 -1
  16. package/extensions/ublock/_locales/br_FR/messages.json +2 -2
  17. package/extensions/ublock/_locales/cy/messages.json +11 -11
  18. package/extensions/ublock/_locales/el/messages.json +2 -2
  19. package/extensions/ublock/_locales/hu/messages.json +1 -1
  20. package/extensions/ublock/_locales/id/messages.json +1 -1
  21. package/extensions/ublock/_locales/lv/messages.json +4 -4
  22. package/extensions/ublock/_locales/mk/messages.json +130 -130
  23. package/extensions/ublock/_locales/oc/messages.json +1 -1
  24. package/extensions/ublock/_locales/pt_BR/messages.json +1 -1
  25. package/extensions/ublock/_locales/pt_PT/messages.json +2 -2
  26. package/extensions/ublock/_locales/si/messages.json +100 -100
  27. package/extensions/ublock/_locales/sr/messages.json +4 -4
  28. package/extensions/ublock/_locales/vi/messages.json +19 -19
  29. package/extensions/ublock/_locales/zh_TW/messages.json +28 -28
  30. package/extensions/ublock/assets/assets.json +33 -29
  31. package/extensions/ublock/assets/thirdparties/easylist/easylist.txt +2984 -3287
  32. package/extensions/ublock/assets/thirdparties/easylist/easyprivacy.txt +150 -171
  33. package/extensions/ublock/assets/thirdparties/pgl.yoyo.org/as/serverlist +37 -27
  34. package/extensions/ublock/assets/thirdparties/publicsuffix.org/list/effective_tld_names.dat +802 -888
  35. package/extensions/ublock/assets/thirdparties/urlhaus-filter/urlhaus-filter-online.txt +2355 -2071
  36. package/extensions/ublock/assets/ublock/badlists.txt +9 -1
  37. package/extensions/ublock/assets/ublock/badware.min.txt +354 -243
  38. package/extensions/ublock/assets/ublock/filters.min.txt +5837 -5737
  39. package/extensions/ublock/assets/ublock/privacy.min.txt +151 -38
  40. package/extensions/ublock/assets/ublock/quick-fixes.min.txt +83 -127
  41. package/extensions/ublock/assets/ublock/unbreak.min.txt +66 -50
  42. package/extensions/ublock/css/codemirror.css +4 -0
  43. package/extensions/ublock/document-blocked.html +3 -1
  44. package/extensions/ublock/js/arglist-parser.js +116 -0
  45. package/extensions/ublock/js/background.js +1 -1
  46. package/extensions/ublock/js/logger-ui.js +1 -1
  47. package/extensions/ublock/js/messaging.js +9 -2
  48. package/extensions/ublock/js/pagestore.js +3 -1
  49. package/extensions/ublock/js/redirect-engine.js +3 -1
  50. package/extensions/ublock/{assets/resources/set-attr.js → js/resources/attribute.js} +115 -11
  51. package/extensions/ublock/js/resources/base.js +38 -0
  52. package/extensions/ublock/js/resources/cookie.js +419 -0
  53. package/extensions/ublock/js/resources/href-sanitizer.js +188 -0
  54. package/extensions/ublock/js/resources/localstorage.js +235 -0
  55. package/extensions/ublock/js/resources/parse-replace.js +54 -0
  56. package/extensions/ublock/js/resources/prevent-settimeout.js +236 -0
  57. package/extensions/ublock/js/resources/proxy-apply.js +109 -0
  58. package/extensions/ublock/js/resources/replace-argument.js +120 -0
  59. package/extensions/ublock/{assets → js}/resources/run-at.js +20 -4
  60. package/extensions/ublock/{assets → js}/resources/safe-self.js +5 -4
  61. package/extensions/ublock/{assets → js}/resources/scriptlets.js +90 -1589
  62. package/extensions/ublock/js/resources/set-constant.js +287 -0
  63. package/extensions/ublock/js/resources/shared.js +44 -0
  64. package/extensions/ublock/js/resources/spoof-css.js +163 -0
  65. package/extensions/ublock/js/s14e-serializer.js +2 -1
  66. package/extensions/ublock/js/scriptlet-filtering-core.js +1 -1
  67. package/extensions/ublock/js/scriptlet-filtering.js +1 -31
  68. package/extensions/ublock/js/static-dnr-filtering.js +143 -129
  69. package/extensions/ublock/js/static-filtering-parser.js +27 -117
  70. package/extensions/ublock/js/static-net-filtering.js +53 -141
  71. package/extensions/ublock/js/traffic.js +1 -1
  72. package/extensions/ublock/js/urlskip.js +166 -0
  73. package/extensions/ublock/js/vapi-background-ext.js +38 -14
  74. package/extensions/ublock/manifest.json +1 -1
  75. package/package.json +12 -12
  76. package/src/browsers/browsers.playwright.ts +8 -1
  77. package/src/browsers/index.ts +1 -1
  78. package/src/routes/management/http/meta.get.ts +6 -1
  79. package/src/routes/management/http/static.get.ts +1 -1
  80. package/static/docs/swagger.json +10 -10
  81. package/static/docs/swagger.min.json +9 -9
  82. package/static/function/client.js +66 -186
  83. package/static/function/index.html +66 -186
@@ -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 { setAttr, setAttrFn, trustedSetAttr } from './set-attr.js';
26
- import { runAt } from './run-at.js';
27
- import { safeSelf } from './safe-self.js';
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
- /* eslint no-prototype-builtins: 0 */
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
- export const builtinScriptlets = [];
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
- registerScriptlet(safeSelf);
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 = target.split('.');
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
- ? rawPrunePaths.split(/ +/)
429
+ ? safe.String_split.call(rawPrunePaths, / +/)
693
430
  : [];
694
431
  const needlePaths = prunePaths.length !== 0 && rawNeedlePaths !== ''
695
- ? rawNeedlePaths.split(/ +/)
432
+ ? safe.String_split.call(rawNeedlePaths, / +/)
696
433
  : [];
697
434
  if ( stackNeedleDetails.matchAll !== true ) {
698
- if ( matchesStackTrace(stackNeedleDetails, extraArgs.logstack) === false ) {
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: matchesStackTrace,
535
+ fn: matchesStackTraceFn,
1019
536
  dependencies: [
1020
537
  'get-exception-token.fn',
1021
538
  'safe-self.fn',
1022
539
  ],
1023
540
  });
1024
- function matchesStackTrace(
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.split(/[\n\r]+/) ) {
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 propsToMatch.split(/\s+/) ) {
1084
- const [ prop, pattern ] = needle.split(':');
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 ( matchesStackTrace(needleDetails, extraArgs.log) ) {
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 ( matchesStackTrace(needleDetails, extraArgs.log) ) {
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 propsToMatch.split(/\s+/) ) {
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-attr.js',
1787
+ name: 'remove-class.js',
2352
1788
  aliases: [
2353
- 'ra.js',
1789
+ 'rc.js',
2354
1790
  ],
2355
- fn: removeAttr,
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 removeAttr(
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-attr', rawToken, rawSelector, behavior);
2370
- const tokens = rawToken.split(/\s*\|\s*/);
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}[${CSS.escape(a)}]`)
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 asap = /\basap\b/.test(behavior);
2378
- let timerId;
2379
- const rmattrAsync = ( ) => {
2380
- if ( timerId !== undefined ) { return; }
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
- for ( const attr of tokens ) {
2395
- if ( node.hasAttribute(attr) === false ) { continue; }
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 ( timerId !== undefined ) { return; }
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
- asap ? rmattr() : rmattrAsync();
1844
+ timer = safe.onIdle(rmclass, { timeout: 67 });
2418
1845
  };
1846
+ const observer = new MutationObserver(mutationHandler);
2419
1847
  const start = ( ) => {
2420
- rmattr();
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: tokens,
1851
+ attributeFilter: [ 'class' ],
2426
1852
  childList: true,
2427
1853
  subtree: true,
2428
1854
  });
2429
1855
  };
2430
- runAt(( ) => { start(); }, behavior.split(/\s+/));
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', ...discard.split(/\n+/).map(s => `\t${s}`));
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 = text.split(/\n\r|\n|\r/);
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 parts = chain.split('.');
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 = extraMatch.split(',').map(s => {
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 = selectors.split(/\s*,\s*/)
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(...rawPrunePaths.split(/ +/));
3147
+ needlePaths.push(...safe.String_split.call(rawPrunePaths, / +/));
4614
3148
  }
4615
3149
  if ( rawNeedlePaths !== '' ) {
4616
- needlePaths.push(...rawNeedlePaths.split(/ +/));
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 = signature.split(/\s*\|\s*/).map(v => {
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();