@browserless.io/browserless 2.20.2 → 2.21.0

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 (135) hide show
  1. package/CHANGELOG.md +10 -1
  2. package/bin/browserless.js +2 -1
  3. package/build/browserless.js +3 -2
  4. package/build/browsers/index.d.ts +3 -2
  5. package/build/browsers/index.js +3 -1
  6. package/build/routes/chrome/http/content.post.body.json +8 -8
  7. package/build/routes/chrome/http/pdf.post.body.json +8 -8
  8. package/build/routes/chrome/http/scrape.post.body.json +8 -8
  9. package/build/routes/chrome/http/screenshot.post.body.json +8 -8
  10. package/build/routes/chrome/tests/pdf.spec.js +24 -12
  11. package/build/routes/chromium/http/content.post.body.json +8 -8
  12. package/build/routes/chromium/http/pdf.post.body.json +8 -8
  13. package/build/routes/chromium/http/scrape.post.body.json +8 -8
  14. package/build/routes/chromium/http/screenshot.post.body.json +8 -8
  15. package/build/routes/chromium/tests/pdf.spec.js +24 -12
  16. package/build/shared/pdf.http.js +10 -5
  17. package/extensions/ublock/_locales/ar/messages.json +5 -1
  18. package/extensions/ublock/_locales/az/messages.json +4 -0
  19. package/extensions/ublock/_locales/be/messages.json +4 -0
  20. package/extensions/ublock/_locales/bg/messages.json +4 -0
  21. package/extensions/ublock/_locales/bn/messages.json +4 -0
  22. package/extensions/ublock/_locales/br_FR/messages.json +9 -5
  23. package/extensions/ublock/_locales/bs/messages.json +4 -0
  24. package/extensions/ublock/_locales/ca/messages.json +5 -1
  25. package/extensions/ublock/_locales/cs/messages.json +4 -0
  26. package/extensions/ublock/_locales/cv/messages.json +4 -0
  27. package/extensions/ublock/_locales/cy/messages.json +4 -0
  28. package/extensions/ublock/_locales/da/messages.json +4 -0
  29. package/extensions/ublock/_locales/de/messages.json +4 -0
  30. package/extensions/ublock/_locales/el/messages.json +8 -4
  31. package/extensions/ublock/_locales/en/messages.json +4 -0
  32. package/extensions/ublock/_locales/en_GB/messages.json +4 -0
  33. package/extensions/ublock/_locales/eo/messages.json +9 -5
  34. package/extensions/ublock/_locales/es/messages.json +4 -0
  35. package/extensions/ublock/_locales/et/messages.json +4 -0
  36. package/extensions/ublock/_locales/eu/messages.json +4 -0
  37. package/extensions/ublock/_locales/fa/messages.json +4 -0
  38. package/extensions/ublock/_locales/fi/messages.json +6 -2
  39. package/extensions/ublock/_locales/fil/messages.json +4 -0
  40. package/extensions/ublock/_locales/fr/messages.json +4 -0
  41. package/extensions/ublock/_locales/fy/messages.json +4 -0
  42. package/extensions/ublock/_locales/gl/messages.json +12 -8
  43. package/extensions/ublock/_locales/gu/messages.json +4 -0
  44. package/extensions/ublock/_locales/he/messages.json +4 -0
  45. package/extensions/ublock/_locales/hi/messages.json +4 -0
  46. package/extensions/ublock/_locales/hr/messages.json +4 -0
  47. package/extensions/ublock/_locales/hu/messages.json +68 -64
  48. package/extensions/ublock/_locales/hy/messages.json +4 -0
  49. package/extensions/ublock/_locales/id/messages.json +6 -2
  50. package/extensions/ublock/_locales/it/messages.json +4 -0
  51. package/extensions/ublock/_locales/ja/messages.json +4 -0
  52. package/extensions/ublock/_locales/ka/messages.json +4 -0
  53. package/extensions/ublock/_locales/kk/messages.json +4 -0
  54. package/extensions/ublock/_locales/kn/messages.json +4 -0
  55. package/extensions/ublock/_locales/ko/messages.json +4 -0
  56. package/extensions/ublock/_locales/lt/messages.json +4 -0
  57. package/extensions/ublock/_locales/lv/messages.json +4 -0
  58. package/extensions/ublock/_locales/mk/messages.json +4 -0
  59. package/extensions/ublock/_locales/ml/messages.json +4 -0
  60. package/extensions/ublock/_locales/mr/messages.json +4 -0
  61. package/extensions/ublock/_locales/ms/messages.json +4 -0
  62. package/extensions/ublock/_locales/nb/messages.json +4 -0
  63. package/extensions/ublock/_locales/nl/messages.json +4 -0
  64. package/extensions/ublock/_locales/no/messages.json +4 -0
  65. package/extensions/ublock/_locales/oc/messages.json +4 -0
  66. package/extensions/ublock/_locales/pa/messages.json +8 -4
  67. package/extensions/ublock/_locales/pl/messages.json +4 -0
  68. package/extensions/ublock/_locales/pt_BR/messages.json +4 -0
  69. package/extensions/ublock/_locales/pt_PT/messages.json +4 -0
  70. package/extensions/ublock/_locales/ro/messages.json +4 -0
  71. package/extensions/ublock/_locales/ru/messages.json +4 -0
  72. package/extensions/ublock/_locales/si/messages.json +4 -0
  73. package/extensions/ublock/_locales/sk/messages.json +4 -0
  74. package/extensions/ublock/_locales/sl/messages.json +4 -0
  75. package/extensions/ublock/_locales/so/messages.json +4 -0
  76. package/extensions/ublock/_locales/sq/messages.json +4 -0
  77. package/extensions/ublock/_locales/sr/messages.json +6 -2
  78. package/extensions/ublock/_locales/sv/messages.json +4 -0
  79. package/extensions/ublock/_locales/sw/messages.json +4 -0
  80. package/extensions/ublock/_locales/ta/messages.json +4 -0
  81. package/extensions/ublock/_locales/te/messages.json +4 -0
  82. package/extensions/ublock/_locales/th/messages.json +4 -0
  83. package/extensions/ublock/_locales/tr/messages.json +4 -0
  84. package/extensions/ublock/_locales/uk/messages.json +4 -0
  85. package/extensions/ublock/_locales/ur/messages.json +4 -0
  86. package/extensions/ublock/_locales/vi/messages.json +4 -0
  87. package/extensions/ublock/_locales/zh_CN/messages.json +4 -0
  88. package/extensions/ublock/_locales/zh_TW/messages.json +5 -1
  89. package/extensions/ublock/assets/assets.json +8 -6
  90. package/extensions/ublock/assets/resources/scriptlets.js +455 -301
  91. package/extensions/ublock/assets/thirdparties/easylist/easylist.txt +4441 -6643
  92. package/extensions/ublock/assets/thirdparties/easylist/easyprivacy.txt +401 -307
  93. package/extensions/ublock/assets/thirdparties/pgl.yoyo.org/as/serverlist +34 -29
  94. package/extensions/ublock/assets/thirdparties/publicsuffix.org/list/effective_tld_names.dat +682 -709
  95. package/extensions/ublock/assets/thirdparties/urlhaus-filter/urlhaus-filter-online.txt +2439 -1999
  96. package/extensions/ublock/assets/ublock/badlists.txt +7 -0
  97. package/extensions/ublock/assets/ublock/badware.min.txt +992 -552
  98. package/extensions/ublock/assets/ublock/filters.min.txt +1253 -864
  99. package/extensions/ublock/assets/ublock/privacy.min.txt +58 -31
  100. package/extensions/ublock/assets/ublock/quick-fixes.min.txt +118 -126
  101. package/extensions/ublock/assets/ublock/unbreak.min.txt +129 -109
  102. package/extensions/ublock/js/background.js +4 -3
  103. package/extensions/ublock/js/benchmarks.js +29 -29
  104. package/extensions/ublock/js/codemirror/ubo-static-filtering.js +1 -0
  105. package/extensions/ublock/js/contextmenu.js +20 -21
  106. package/extensions/ublock/js/devtools.js +137 -3
  107. package/extensions/ublock/js/filtering-context.js +98 -43
  108. package/extensions/ublock/js/logger-ui.js +1 -1
  109. package/extensions/ublock/js/messaging.js +57 -135
  110. package/extensions/ublock/js/pagestore.js +44 -31
  111. package/extensions/ublock/js/redirect-resources.js +14 -4
  112. package/extensions/ublock/js/scriptlet-filtering.js +4 -1
  113. package/extensions/ublock/js/static-filtering-parser.js +107 -37
  114. package/extensions/ublock/js/static-net-filtering.js +514 -250
  115. package/extensions/ublock/js/storage.js +2 -1
  116. package/extensions/ublock/js/traffic.js +8 -4
  117. package/extensions/ublock/js/vapi-background.js +1 -0
  118. package/extensions/ublock/js/vapi-common.js +1 -0
  119. package/extensions/ublock/logger-ui.html +2 -3
  120. package/extensions/ublock/manifest.json +2 -2
  121. package/extensions/ublock/support.html +1 -0
  122. package/extensions/ublock/web_accessible_resources/noop-vast2.xml +1 -0
  123. package/extensions/ublock/web_accessible_resources/noop-vast3.xml +1 -0
  124. package/extensions/ublock/web_accessible_resources/noop-vast4.xml +1 -0
  125. package/package.json +16 -16
  126. package/src/browserless.ts +3 -2
  127. package/src/browsers/index.ts +2 -0
  128. package/src/routes/chrome/tests/pdf.spec.ts +24 -12
  129. package/src/routes/chromium/tests/pdf.spec.ts +24 -12
  130. package/src/shared/pdf.http.ts +10 -6
  131. package/static/docs/swagger.json +10 -10
  132. package/static/docs/swagger.min.json +9 -9
  133. package/static/function/client.js +9 -9
  134. package/static/function/index.html +9 -9
  135. /package/extensions/ublock/web_accessible_resources/{noop-vmap1.0.xml → noop-vmap1.xml} +0 -0
@@ -97,7 +97,7 @@ function safeSelf() {
97
97
  },
98
98
  initPattern(pattern, options = {}) {
99
99
  if ( pattern === '' ) {
100
- return { matchAll: true };
100
+ return { matchAll: true, expect: true };
101
101
  }
102
102
  const expect = (options.canNegate !== true || pattern.startsWith('!') === false);
103
103
  if ( expect === false ) {
@@ -163,6 +163,12 @@ function safeSelf() {
163
163
  }
164
164
  return self.requestAnimationFrame(fn);
165
165
  },
166
+ offIdle(id) {
167
+ if ( self.requestIdleCallback ) {
168
+ return self.cancelIdleCallback(id);
169
+ }
170
+ return self.cancelAnimationFrame(id);
171
+ }
166
172
  };
167
173
  scriptletGlobals.safeSelf = safe;
168
174
  if ( scriptletGlobals.bcSecret === undefined ) { return safe; }
@@ -170,9 +176,18 @@ function safeSelf() {
170
176
  const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret);
171
177
  let bcBuffer = [];
172
178
  safe.logLevel = scriptletGlobals.logLevel || 1;
179
+ let lastLogType = '';
180
+ let lastLogText = '';
181
+ let lastLogTime = 0;
173
182
  safe.sendToLogger = (type, ...args) => {
174
183
  if ( args.length === 0 ) { return; }
175
184
  const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`;
185
+ if ( text === lastLogText && type === lastLogType ) {
186
+ if ( (Date.now() - lastLogTime) < 5000 ) { return; }
187
+ }
188
+ lastLogType = type;
189
+ lastLogText = text;
190
+ lastLogTime = Date.now();
176
191
  if ( bcBuffer === undefined ) {
177
192
  return bc.postMessage({ what: 'messageToLogger', type, text });
178
193
  }
@@ -203,17 +218,28 @@ function safeSelf() {
203
218
  /******************************************************************************/
204
219
 
205
220
  builtinScriptlets.push({
206
- name: 'get-exception-token.fn',
207
- fn: getExceptionToken,
221
+ name: 'get-random-token.fn',
222
+ fn: getRandomToken,
208
223
  dependencies: [
209
224
  'safe-self.fn',
210
225
  ],
211
226
  });
212
- function getExceptionToken() {
227
+ function getRandomToken() {
213
228
  const safe = safeSelf();
214
- const token =
215
- safe.String_fromCharCode(Date.now() % 26 + 97) +
229
+ return safe.String_fromCharCode(Date.now() % 26 + 97) +
216
230
  safe.Math_floor(safe.Math_random() * 982451653 + 982451653).toString(36);
231
+ }
232
+ /******************************************************************************/
233
+
234
+ builtinScriptlets.push({
235
+ name: 'get-exception-token.fn',
236
+ fn: getExceptionToken,
237
+ dependencies: [
238
+ 'get-random-token.fn',
239
+ ],
240
+ });
241
+ function getExceptionToken() {
242
+ const token = getRandomToken();
217
243
  const oe = self.onerror;
218
244
  self.onerror = function(msg, ...args) {
219
245
  if ( typeof msg === 'string' && msg.includes(token) ) { return true; }
@@ -247,7 +273,7 @@ builtinScriptlets.push({
247
273
  function runAt(fn, when) {
248
274
  const intFromReadyState = state => {
249
275
  const targets = {
250
- 'loading': 1,
276
+ 'loading': 1, 'asap': 1,
251
277
  'interactive': 2, 'end': 2, '2': 2,
252
278
  'complete': 3, 'idle': 3, '3': 3,
253
279
  };
@@ -509,12 +535,16 @@ function validateConstantFn(trusted, raw, extraArgs = {}) {
509
535
  value = function(){ return true; };
510
536
  } else if ( raw === 'falseFunc' ) {
511
537
  value = function(){ return false; };
538
+ } else if ( raw === 'throwFunc' ) {
539
+ value = function(){ throw ''; };
512
540
  } else if ( /^-?\d+$/.test(raw) ) {
513
541
  value = parseInt(raw);
514
542
  if ( isNaN(raw) ) { return; }
515
543
  if ( Math.abs(raw) > 0x7FFF ) { return; }
516
544
  } else if ( trusted ) {
517
- if ( raw.startsWith('{') && raw.endsWith('}') ) {
545
+ if ( raw.startsWith('json:') ) {
546
+ try { value = safe.JSON_parse(raw.slice(5)); } catch(ex) { return; }
547
+ } else if ( raw.startsWith('{') && raw.endsWith('}') ) {
518
548
  try { value = safe.JSON_parse(raw).value; } catch(ex) { return; }
519
549
  }
520
550
  } else {
@@ -701,6 +731,7 @@ builtinScriptlets.push({
701
731
  name: 'replace-node-text.fn',
702
732
  fn: replaceNodeTextFn,
703
733
  dependencies: [
734
+ 'get-random-token.fn',
704
735
  'run-at.fn',
705
736
  'safe-self.fn',
706
737
  ],
@@ -730,6 +761,18 @@ function replaceNodeTextFn(
730
761
  safe.uboLog(logPrefix, 'Quitting');
731
762
  }
732
763
  };
764
+ const textContentFactory = (( ) => {
765
+ const out = { createScript: s => s };
766
+ const { trustedTypes: tt } = self;
767
+ if ( tt instanceof Object ) {
768
+ if ( typeof tt.getPropertyType === 'function' ) {
769
+ if ( tt.getPropertyType('script', 'textContent') === 'TrustedScript' ) {
770
+ return tt.createPolicy(getRandomToken(), out);
771
+ }
772
+ }
773
+ }
774
+ return out;
775
+ })();
733
776
  let sedCount = extraArgs.sedCount || 0;
734
777
  const handleNode = node => {
735
778
  const before = node.textContent;
@@ -747,7 +790,9 @@ function replaceNodeTextFn(
747
790
  const after = pattern !== ''
748
791
  ? before.replace(rePattern, replacement)
749
792
  : replacement;
750
- node.textContent = after;
793
+ node.textContent = node.nodeName === 'SCRIPT'
794
+ ? textContentFactory.createScript(after)
795
+ : after;
751
796
  if ( safe.logLevel > 1 ) {
752
797
  safe.uboLog(logPrefix, `Text before:\n${before.trim()}`);
753
798
  }
@@ -922,6 +967,33 @@ function objectFindOwnerFn(
922
967
 
923
968
  /******************************************************************************/
924
969
 
970
+ builtinScriptlets.push({
971
+ name: 'get-safe-cookie-values.fn',
972
+ fn: getSafeCookieValuesFn,
973
+ });
974
+ function getSafeCookieValuesFn() {
975
+ return [
976
+ 'accept', 'reject',
977
+ 'accepted', 'rejected', 'notaccepted',
978
+ 'allow', 'disallow', 'deny',
979
+ 'allowed', 'denied',
980
+ 'approved', 'disapproved',
981
+ 'checked', 'unchecked',
982
+ 'dismiss', 'dismissed',
983
+ 'enable', 'disable',
984
+ 'enabled', 'disabled',
985
+ 'essential', 'nonessential',
986
+ 'hide', 'hidden',
987
+ 'necessary', 'required',
988
+ 'ok',
989
+ 'on', 'off',
990
+ 'true', 't', 'false', 'f',
991
+ 'yes', 'y', 'no', 'n',
992
+ ];
993
+ }
994
+
995
+ /******************************************************************************/
996
+
925
997
  builtinScriptlets.push({
926
998
  name: 'get-all-cookies.fn',
927
999
  fn: getAllCookiesFn,
@@ -1044,6 +1116,7 @@ builtinScriptlets.push({
1044
1116
  name: 'set-local-storage-item.fn',
1045
1117
  fn: setLocalStorageItemFn,
1046
1118
  dependencies: [
1119
+ 'get-safe-cookie-values.fn',
1047
1120
  'safe-self.fn',
1048
1121
  ],
1049
1122
  });
@@ -1065,13 +1138,9 @@ function setLocalStorageItemFn(
1065
1138
  const trustedValues = [
1066
1139
  '',
1067
1140
  'undefined', 'null',
1068
- 'false', 'true',
1069
- 'on', 'off',
1070
- 'yes', 'no',
1071
- 'accept', 'reject',
1072
- 'accepted', 'rejected',
1073
1141
  '{}', '[]', '""',
1074
1142
  '$remove$',
1143
+ ...getSafeCookieValuesFn(),
1075
1144
  ];
1076
1145
 
1077
1146
  if ( trusted ) {
@@ -1414,9 +1483,6 @@ function replaceFetchResponseFn(
1414
1483
  builtinScriptlets.push({
1415
1484
  name: 'proxy-apply.fn',
1416
1485
  fn: proxyApplyFn,
1417
- dependencies: [
1418
- 'safe-self.fn',
1419
- ],
1420
1486
  });
1421
1487
  function proxyApplyFn(
1422
1488
  target = '',
@@ -1433,12 +1499,70 @@ function proxyApplyFn(
1433
1499
  }
1434
1500
  const fn = context[prop];
1435
1501
  if ( typeof fn !== 'function' ) { return; }
1436
- if ( fn.prototype && fn.prototype.constructor === fn ) {
1437
- context[prop] = new Proxy(fn, { construct: handler });
1438
- return (...args) => { return Reflect.construct(...args); };
1502
+ if ( proxyApplyFn.CtorContext === undefined ) {
1503
+ proxyApplyFn.ctorContexts = [];
1504
+ proxyApplyFn.CtorContext = class {
1505
+ constructor(...args) {
1506
+ this.init(...args);
1507
+ }
1508
+ init(callFn, callArgs) {
1509
+ this.callFn = callFn;
1510
+ this.callArgs = callArgs;
1511
+ return this;
1512
+ }
1513
+ reflect() {
1514
+ const r = Reflect.construct(this.callFn, this.callArgs);
1515
+ this.callFn = this.callArgs = undefined;
1516
+ proxyApplyFn.ctorContexts.push(this);
1517
+ return r;
1518
+ }
1519
+ static factory(...args) {
1520
+ return proxyApplyFn.ctorContexts.length !== 0
1521
+ ? proxyApplyFn.ctorContexts.pop().init(...args)
1522
+ : new proxyApplyFn.CtorContext(...args);
1523
+ }
1524
+ };
1525
+ proxyApplyFn.applyContexts = [];
1526
+ proxyApplyFn.ApplyContext = class {
1527
+ constructor(...args) {
1528
+ this.init(...args);
1529
+ }
1530
+ init(callFn, thisArg, callArgs) {
1531
+ this.callFn = callFn;
1532
+ this.thisArg = thisArg;
1533
+ this.callArgs = callArgs;
1534
+ return this;
1535
+ }
1536
+ reflect() {
1537
+ const r = Reflect.apply(this.callFn, this.thisArg, this.callArgs);
1538
+ this.callFn = this.thisArg = this.callArgs = undefined;
1539
+ proxyApplyFn.applyContexts.push(this);
1540
+ return r;
1541
+ }
1542
+ static factory(...args) {
1543
+ return proxyApplyFn.applyContexts.length !== 0
1544
+ ? proxyApplyFn.applyContexts.pop().init(...args)
1545
+ : new proxyApplyFn.ApplyContext(...args);
1546
+ }
1547
+ };
1548
+ }
1549
+ const fnStr = fn.toString();
1550
+ const toString = (function toString() { return fnStr; }).bind(null);
1551
+ const proxyDetails = {
1552
+ apply(target, thisArg, args) {
1553
+ return handler(proxyApplyFn.ApplyContext.factory(target, thisArg, args));
1554
+ },
1555
+ get(target, prop) {
1556
+ if ( prop === 'toString' ) { return toString; }
1557
+ return Reflect.get(target, prop);
1558
+ },
1559
+ };
1560
+ if ( fn.prototype?.constructor === fn ) {
1561
+ proxyDetails.construct = function(target, args) {
1562
+ return handler(proxyApplyFn.CtorContext.factory(target, args));
1563
+ };
1439
1564
  }
1440
- context[prop] = new Proxy(fn, { apply: handler });
1441
- return (...args) => { return Reflect.apply(...args); };
1565
+ context[prop] = new Proxy(fn, proxyDetails);
1442
1566
  }
1443
1567
 
1444
1568
  /*******************************************************************************
@@ -1644,6 +1768,7 @@ builtinScriptlets.push({
1644
1768
  ],
1645
1769
  fn: addEventListenerDefuser,
1646
1770
  dependencies: [
1771
+ 'proxy-apply.fn',
1647
1772
  'run-at.fn',
1648
1773
  'safe-self.fn',
1649
1774
  'should-debug.fn',
@@ -1700,44 +1825,30 @@ function addEventListenerDefuser(
1700
1825
  }
1701
1826
  return matchesBoth;
1702
1827
  };
1703
- const trapEddEventListeners = ( ) => {
1704
- const eventListenerHandler = {
1705
- apply: function(target, thisArg, args) {
1706
- let t, h;
1707
- try {
1708
- t = String(args[0]);
1709
- if ( typeof args[1] === 'function' ) {
1710
- h = String(safe.Function_toString(args[1]));
1711
- } else if ( typeof args[1] === 'object' && args[1] !== null ) {
1712
- if ( typeof args[1].handleEvent === 'function' ) {
1713
- h = String(safe.Function_toString(args[1].handleEvent));
1714
- }
1715
- } else {
1716
- h = String(args[1]);
1828
+ runAt(( ) => {
1829
+ proxyApplyFn('EventTarget.prototype.addEventListener', function(context) {
1830
+ const { callArgs, thisArg } = context;
1831
+ let t, h;
1832
+ try {
1833
+ t = String(callArgs[0]);
1834
+ if ( typeof callArgs[1] === 'function' ) {
1835
+ h = String(safe.Function_toString(callArgs[1]));
1836
+ } else if ( typeof callArgs[1] === 'object' && callArgs[1] !== null ) {
1837
+ if ( typeof callArgs[1].handleEvent === 'function' ) {
1838
+ h = String(safe.Function_toString(callArgs[1].handleEvent));
1717
1839
  }
1718
- } catch(ex) {
1719
- }
1720
- if ( type === '' && pattern === '' ) {
1721
- safe.uboLog(logPrefix, `Called: ${t}\n${h}\n${elementDetails(thisArg)}`);
1722
- } else if ( shouldPrevent(thisArg, t, h) ) {
1723
- return safe.uboLog(logPrefix, `Prevented: ${t}\n${h}\n${elementDetails(thisArg)}`);
1724
- }
1725
- return Reflect.apply(target, thisArg, args);
1726
- },
1727
- get(target, prop, receiver) {
1728
- if ( prop === 'toString' ) {
1729
- return target.toString.bind(target);
1840
+ } else {
1841
+ h = String(callArgs[1]);
1730
1842
  }
1731
- return Reflect.get(target, prop, receiver);
1732
- },
1733
- };
1734
- self.EventTarget.prototype.addEventListener = new Proxy(
1735
- self.EventTarget.prototype.addEventListener,
1736
- eventListenerHandler
1737
- );
1738
- };
1739
- runAt(( ) => {
1740
- trapEddEventListeners();
1843
+ } catch(ex) {
1844
+ }
1845
+ if ( type === '' && pattern === '' ) {
1846
+ safe.uboLog(logPrefix, `Called: ${t}\n${h}\n${elementDetails(thisArg)}`);
1847
+ } else if ( shouldPrevent(thisArg, t, h) ) {
1848
+ return safe.uboLog(logPrefix, `Prevented: ${t}\n${h}\n${elementDetails(thisArg)}`);
1849
+ }
1850
+ return context.reflect();
1851
+ });
1741
1852
  }, extraArgs.runAt);
1742
1853
  }
1743
1854
 
@@ -2074,6 +2185,7 @@ builtinScriptlets.push({
2074
2185
  fn: noFetchIf,
2075
2186
  dependencies: [
2076
2187
  'generate-content.fn',
2188
+ 'proxy-apply.fn',
2077
2189
  'safe-self.fn',
2078
2190
  ],
2079
2191
  });
@@ -2096,7 +2208,7 @@ function noFetchIf(
2096
2208
  key = 'url';
2097
2209
  value = condition;
2098
2210
  }
2099
- needles.push({ key, re: safe.patternToRegex(value) });
2211
+ needles.push({ key, pattern: safe.initPattern(value, { canNegate: true }) });
2100
2212
  }
2101
2213
  const validResponseProps = {
2102
2214
  ok: [ false, true ],
@@ -2120,58 +2232,59 @@ function noFetchIf(
2120
2232
  responseProps.type = { value: responseType };
2121
2233
  }
2122
2234
  }
2123
- self.fetch = new Proxy(self.fetch, {
2124
- apply: function(target, thisArg, args) {
2125
- const details = args[0] instanceof self.Request
2126
- ? args[0]
2127
- : Object.assign({ url: args[0] }, args[1]);
2128
- let proceed = true;
2129
- try {
2130
- const props = new Map();
2131
- for ( const prop in details ) {
2132
- let v = details[prop];
2133
- if ( typeof v !== 'string' ) {
2134
- try { v = safe.JSON_stringify(v); }
2135
- catch(ex) { }
2136
- }
2137
- if ( typeof v !== 'string' ) { continue; }
2138
- props.set(prop, v);
2139
- }
2140
- if ( propsToMatch === '' && responseBody === '' ) {
2141
- const out = Array.from(props).map(a => `${a[0]}:${a[1]}`);
2142
- safe.uboLog(logPrefix, `Called: ${out.join('\n')}`);
2143
- return Reflect.apply(target, thisArg, args);
2144
- }
2145
- proceed = needles.length === 0;
2146
- for ( const { key, re } of needles ) {
2147
- if (
2148
- props.has(key) === false ||
2149
- re.test(props.get(key)) === false
2150
- ) {
2151
- proceed = true;
2152
- break;
2153
- }
2235
+ proxyApplyFn('fetch', function fetch(context) {
2236
+ const { callArgs } = context;
2237
+ const details = callArgs[0] instanceof self.Request
2238
+ ? callArgs[0]
2239
+ : Object.assign({ url: callArgs[0] }, callArgs[1]);
2240
+ let proceed = true;
2241
+ try {
2242
+ const props = new Map();
2243
+ for ( const prop in details ) {
2244
+ let v = details[prop];
2245
+ if ( typeof v !== 'string' ) {
2246
+ try { v = safe.JSON_stringify(v); }
2247
+ catch(ex) { }
2154
2248
  }
2155
- } catch(ex) {
2249
+ if ( typeof v !== 'string' ) { continue; }
2250
+ props.set(prop, v);
2156
2251
  }
2157
- if ( proceed ) {
2158
- return Reflect.apply(target, thisArg, args);
2252
+ if ( safe.logLevel > 1 || propsToMatch === '' && responseBody === '' ) {
2253
+ const out = Array.from(props).map(a => `${a[0]}:${a[1]}`);
2254
+ safe.uboLog(logPrefix, `Called: ${out.join('\n')}`);
2159
2255
  }
2160
- return generateContentFn(responseBody).then(text => {
2161
- safe.uboLog(logPrefix, `Prevented with response "${text}"`);
2162
- const response = new Response(text, {
2163
- headers: {
2164
- 'Content-Length': text.length,
2165
- }
2166
- });
2167
- const props = Object.assign(
2168
- { url: { value: details.url } },
2169
- responseProps
2170
- );
2171
- safe.Object_defineProperties(response, props);
2172
- return response;
2173
- });
2256
+ if ( propsToMatch === '' && responseBody === '' ) {
2257
+ return context.reflect();
2258
+ }
2259
+ proceed = needles.length === 0;
2260
+ for ( const { key, pattern } of needles ) {
2261
+ if (
2262
+ pattern.expect && props.has(key) === false ||
2263
+ safe.testPattern(pattern, props.get(key)) === false
2264
+ ) {
2265
+ proceed = true;
2266
+ break;
2267
+ }
2268
+ }
2269
+ } catch(ex) {
2174
2270
  }
2271
+ if ( proceed ) {
2272
+ return context.reflect();
2273
+ }
2274
+ return generateContentFn(responseBody).then(text => {
2275
+ safe.uboLog(logPrefix, `Prevented with response "${text}"`);
2276
+ const response = new Response(text, {
2277
+ headers: {
2278
+ 'Content-Length': text.length,
2279
+ }
2280
+ });
2281
+ const props = Object.assign(
2282
+ { url: { value: details.url } },
2283
+ responseProps
2284
+ );
2285
+ safe.Object_defineProperties(response, props);
2286
+ return response;
2287
+ });
2175
2288
  });
2176
2289
  }
2177
2290
 
@@ -2240,9 +2353,20 @@ function removeAttr(
2240
2353
  if ( safe.logLevel > 1 ) {
2241
2354
  safe.uboLog(logPrefix, `Target selector:\n\t${selector}`);
2242
2355
  }
2243
- let timer;
2356
+ const asap = /\basap\b/.test(behavior);
2357
+ let timerId;
2358
+ const rmattrAsync = ( ) => {
2359
+ if ( timerId !== undefined ) { return; }
2360
+ timerId = safe.onIdle(( ) => {
2361
+ timerId = undefined;
2362
+ rmattr();
2363
+ }, { timeout: 17 });
2364
+ };
2244
2365
  const rmattr = ( ) => {
2245
- timer = undefined;
2366
+ if ( timerId !== undefined ) {
2367
+ safe.offIdle(timerId);
2368
+ timerId = undefined;
2369
+ }
2246
2370
  try {
2247
2371
  const nodes = document.querySelectorAll(selector);
2248
2372
  for ( const node of nodes ) {
@@ -2256,7 +2380,7 @@ function removeAttr(
2256
2380
  }
2257
2381
  };
2258
2382
  const mutationHandler = mutations => {
2259
- if ( timer !== undefined ) { return; }
2383
+ if ( timerId !== undefined ) { return; }
2260
2384
  let skip = true;
2261
2385
  for ( let i = 0; i < mutations.length && skip; i++ ) {
2262
2386
  const { type, addedNodes, removedNodes } = mutations[i];
@@ -2269,7 +2393,7 @@ function removeAttr(
2269
2393
  }
2270
2394
  }
2271
2395
  if ( skip ) { return; }
2272
- timer = safe.onIdle(rmattr, { timeout: 67 });
2396
+ asap ? rmattr() : rmattrAsync();
2273
2397
  };
2274
2398
  const start = ( ) => {
2275
2399
  rmattr();
@@ -2282,9 +2406,7 @@ function removeAttr(
2282
2406
  subtree: true,
2283
2407
  });
2284
2408
  };
2285
- runAt(( ) => {
2286
- start();
2287
- }, /\bcomplete\b/.test(behavior) ? 'idle' : 'interactive');
2409
+ runAt(( ) => { start(); }, behavior.split(/\s+/));
2288
2410
  }
2289
2411
 
2290
2412
  /******************************************************************************/
@@ -2434,6 +2556,7 @@ builtinScriptlets.push({
2434
2556
  ],
2435
2557
  fn: noSetIntervalIf,
2436
2558
  dependencies: [
2559
+ 'proxy-apply.fn',
2437
2560
  'safe-self.fn',
2438
2561
  ],
2439
2562
  });
@@ -2454,35 +2577,28 @@ function noSetIntervalIf(
2454
2577
  delay = parseInt(delay, 10);
2455
2578
  }
2456
2579
  const reNeedle = safe.patternToRegex(needle);
2457
- self.setInterval = new Proxy(self.setInterval, {
2458
- apply: function(target, thisArg, args) {
2459
- const a = args[0] instanceof Function
2460
- ? String(safe.Function_toString(args[0]))
2461
- : String(args[0]);
2462
- const b = args[1];
2463
- if ( needle === '' && delay === undefined ) {
2464
- safe.uboLog(logPrefix, `Called:\n${a}\n${b}`);
2465
- return Reflect.apply(target, thisArg, args);
2466
- }
2467
- let defuse;
2468
- if ( needle !== '' ) {
2469
- defuse = reNeedle.test(a) !== needleNot;
2470
- }
2471
- if ( defuse !== false && delay !== undefined ) {
2472
- defuse = (b === delay || isNaN(b) && isNaN(delay) ) !== delayNot;
2473
- }
2474
- if ( defuse ) {
2475
- args[0] = function(){};
2476
- safe.uboLog(logPrefix, `Prevented:\n${a}\n${b}`);
2477
- }
2478
- return Reflect.apply(target, thisArg, args);
2479
- },
2480
- get(target, prop, receiver) {
2481
- if ( prop === 'toString' ) {
2482
- return target.toString.bind(target);
2483
- }
2484
- return Reflect.get(target, prop, receiver);
2485
- },
2580
+ proxyApplyFn('setInterval', function setInterval(context) {
2581
+ const { callArgs } = context;
2582
+ const a = callArgs[0] instanceof Function
2583
+ ? String(safe.Function_toString(callArgs[0]))
2584
+ : String(callArgs[0]);
2585
+ const b = callArgs[1];
2586
+ if ( needle === '' && delay === undefined ) {
2587
+ safe.uboLog(logPrefix, `Called:\n${a}\n${b}`);
2588
+ return context.reflect();
2589
+ }
2590
+ let defuse;
2591
+ if ( needle !== '' ) {
2592
+ defuse = reNeedle.test(a) !== needleNot;
2593
+ }
2594
+ if ( defuse !== false && delay !== undefined ) {
2595
+ defuse = (b === delay || isNaN(b) && isNaN(delay) ) !== delayNot;
2596
+ }
2597
+ if ( defuse ) {
2598
+ callArgs[0] = function(){};
2599
+ safe.uboLog(logPrefix, `Prevented:\n${a}\n${b}`);
2600
+ }
2601
+ return context.reflect();
2486
2602
  });
2487
2603
  }
2488
2604
 
@@ -2497,6 +2613,7 @@ builtinScriptlets.push({
2497
2613
  ],
2498
2614
  fn: noSetTimeoutIf,
2499
2615
  dependencies: [
2616
+ 'proxy-apply.fn',
2500
2617
  'safe-self.fn',
2501
2618
  ],
2502
2619
  });
@@ -2517,35 +2634,28 @@ function noSetTimeoutIf(
2517
2634
  delay = parseInt(delay, 10);
2518
2635
  }
2519
2636
  const reNeedle = safe.patternToRegex(needle);
2520
- self.setTimeout = new Proxy(self.setTimeout, {
2521
- apply: function(target, thisArg, args) {
2522
- const a = args[0] instanceof Function
2523
- ? String(safe.Function_toString(args[0]))
2524
- : String(args[0]);
2525
- const b = args[1];
2526
- if ( needle === '' && delay === undefined ) {
2527
- safe.uboLog(logPrefix, `Called:\n${a}\n${b}`);
2528
- return Reflect.apply(target, thisArg, args);
2529
- }
2530
- let defuse;
2531
- if ( needle !== '' ) {
2532
- defuse = reNeedle.test(a) !== needleNot;
2533
- }
2534
- if ( defuse !== false && delay !== undefined ) {
2535
- defuse = (b === delay || isNaN(b) && isNaN(delay) ) !== delayNot;
2536
- }
2537
- if ( defuse ) {
2538
- args[0] = function(){};
2539
- safe.uboLog(logPrefix, `Prevented:\n${a}\n${b}`);
2540
- }
2541
- return Reflect.apply(target, thisArg, args);
2542
- },
2543
- get(target, prop, receiver) {
2544
- if ( prop === 'toString' ) {
2545
- return target.toString.bind(target);
2546
- }
2547
- return Reflect.get(target, prop, receiver);
2548
- },
2637
+ proxyApplyFn('setTimeout', function setTimeout(context) {
2638
+ const { callArgs } = context;
2639
+ const a = callArgs[0] instanceof Function
2640
+ ? String(safe.Function_toString(callArgs[0]))
2641
+ : String(callArgs[0]);
2642
+ const b = callArgs[1];
2643
+ if ( needle === '' && delay === undefined ) {
2644
+ safe.uboLog(logPrefix, `Called:\n${a}\n${b}`);
2645
+ return context.reflect();
2646
+ }
2647
+ let defuse;
2648
+ if ( needle !== '' ) {
2649
+ defuse = reNeedle.test(a) !== needleNot;
2650
+ }
2651
+ if ( defuse !== false && delay !== undefined ) {
2652
+ defuse = (b === delay || isNaN(b) && isNaN(delay) ) !== delayNot;
2653
+ }
2654
+ if ( defuse ) {
2655
+ callArgs[0] = function(){};
2656
+ safe.uboLog(logPrefix, `Prevented:\n${a}\n${b}`);
2657
+ }
2658
+ return context.reflect();
2549
2659
  });
2550
2660
  }
2551
2661
 
@@ -2647,6 +2757,13 @@ function noXhrIf(
2647
2757
  'content-type': '',
2648
2758
  'content-length': '',
2649
2759
  };
2760
+ const safeDispatchEvent = (xhr, type) => {
2761
+ try {
2762
+ xhr.dispatchEvent(new Event(type));
2763
+ } catch(_) {
2764
+ }
2765
+ };
2766
+ const XHRBefore = XMLHttpRequest.prototype;
2650
2767
  self.XMLHttpRequest = class extends self.XMLHttpRequest {
2651
2768
  open(method, url, ...args) {
2652
2769
  xhrInstances.delete(this);
@@ -2673,27 +2790,26 @@ function noXhrIf(
2673
2790
  let promise = Promise.resolve({
2674
2791
  xhr: this,
2675
2792
  directive,
2676
- props: {
2677
- readyState: { value: 4 },
2793
+ response: {
2678
2794
  response: { value: '' },
2679
2795
  responseText: { value: '' },
2680
2796
  responseXML: { value: null },
2681
2797
  responseURL: { value: haystack.url },
2682
- status: { value: 200 },
2683
- statusText: { value: 'OK' },
2684
- },
2798
+ }
2685
2799
  });
2686
2800
  switch ( this.responseType ) {
2687
2801
  case 'arraybuffer':
2688
2802
  promise = promise.then(details => {
2689
- details.props.response.value = new ArrayBuffer(0);
2803
+ const response = details.response;
2804
+ response.response.value = new ArrayBuffer(0);
2690
2805
  return details;
2691
2806
  });
2692
2807
  haystack.headers['content-type'] = 'application/octet-stream';
2693
2808
  break;
2694
2809
  case 'blob':
2695
2810
  promise = promise.then(details => {
2696
- details.props.response.value = new Blob([]);
2811
+ const response = details.response;
2812
+ response.response.value = new Blob([]);
2697
2813
  return details;
2698
2814
  });
2699
2815
  haystack.headers['content-type'] = 'application/octet-stream';
@@ -2702,8 +2818,9 @@ function noXhrIf(
2702
2818
  promise = promise.then(details => {
2703
2819
  const parser = new DOMParser();
2704
2820
  const doc = parser.parseFromString('', 'text/html');
2705
- details.props.response.value = doc;
2706
- details.props.responseXML.value = doc;
2821
+ const response = details.response;
2822
+ response.response.value = doc;
2823
+ response.responseXML.value = doc;
2707
2824
  return details;
2708
2825
  });
2709
2826
  haystack.headers['content-type'] = 'text/html';
@@ -2711,8 +2828,9 @@ function noXhrIf(
2711
2828
  }
2712
2829
  case 'json':
2713
2830
  promise = promise.then(details => {
2714
- details.props.response.value = {};
2715
- details.props.responseText.value = '{}';
2831
+ const response = details.response;
2832
+ response.response.value = {};
2833
+ response.responseText.value = '{}';
2716
2834
  return details;
2717
2835
  });
2718
2836
  haystack.headers['content-type'] = 'application/json';
@@ -2721,8 +2839,9 @@ function noXhrIf(
2721
2839
  if ( directive === '' ) { break; }
2722
2840
  promise = promise.then(details => {
2723
2841
  return generateContentFn(details.directive).then(text => {
2724
- details.props.response.value = text;
2725
- details.props.responseText.value = text;
2842
+ const response = details.response;
2843
+ response.response.value = text;
2844
+ response.responseText.value = text;
2726
2845
  return details;
2727
2846
  });
2728
2847
  });
@@ -2730,11 +2849,35 @@ function noXhrIf(
2730
2849
  break;
2731
2850
  }
2732
2851
  promise.then(details => {
2733
- haystack.headers['content-length'] = `${details.props.response.value}`.length;
2734
- Object.defineProperties(details.xhr, details.props);
2735
- details.xhr.dispatchEvent(new Event('readystatechange'));
2736
- details.xhr.dispatchEvent(new Event('load'));
2737
- details.xhr.dispatchEvent(new Event('loadend'));
2852
+ Object.defineProperties(details.xhr, {
2853
+ readyState: { value: 1, configurable: true },
2854
+ });
2855
+ safeDispatchEvent(details.xhr, 'readystatechange');
2856
+ return details;
2857
+ }).then(details => {
2858
+ const response = details.response;
2859
+ haystack.headers['content-length'] = `${response.response.value}`.length;
2860
+ Object.defineProperties(details.xhr, {
2861
+ readyState: { value: 2, configurable: true },
2862
+ status: { value: 200 },
2863
+ statusText: { value: 'OK' },
2864
+ });
2865
+ safeDispatchEvent(details.xhr, 'readystatechange');
2866
+ return details;
2867
+ }).then(details => {
2868
+ Object.defineProperties(details.xhr, {
2869
+ readyState: { value: 3, configurable: true },
2870
+ });
2871
+ Object.defineProperties(details.xhr, details.response);
2872
+ safeDispatchEvent(details.xhr, 'readystatechange');
2873
+ return details;
2874
+ }).then(details => {
2875
+ Object.defineProperties(details.xhr, {
2876
+ readyState: { value: 4 },
2877
+ });
2878
+ safeDispatchEvent(details.xhr, 'readystatechange');
2879
+ safeDispatchEvent(details.xhr, 'load');
2880
+ safeDispatchEvent(details.xhr, 'loadend');
2738
2881
  safe.uboLog(logPrefix, `Prevented with response:\n${details.xhr.response}`);
2739
2882
  });
2740
2883
  }
@@ -2761,6 +2904,18 @@ function noXhrIf(
2761
2904
  return out.join('\r\n');
2762
2905
  }
2763
2906
  };
2907
+ self.XMLHttpRequest.prototype.open.toString = function() {
2908
+ return XHRBefore.open.toString();
2909
+ };
2910
+ self.XMLHttpRequest.prototype.send.toString = function() {
2911
+ return XHRBefore.send.toString();
2912
+ };
2913
+ self.XMLHttpRequest.prototype.getResponseHeader.toString = function() {
2914
+ return XHRBefore.getResponseHeader.toString();
2915
+ };
2916
+ self.XMLHttpRequest.prototype.getAllResponseHeaders.toString = function() {
2917
+ return XHRBefore.getAllResponseHeaders.toString();
2918
+ };
2764
2919
  }
2765
2920
 
2766
2921
  /******************************************************************************/
@@ -2774,6 +2929,7 @@ builtinScriptlets.push({
2774
2929
  ],
2775
2930
  fn: noWindowOpenIf,
2776
2931
  dependencies: [
2932
+ 'proxy-apply.fn',
2777
2933
  'safe-self.fn',
2778
2934
  ],
2779
2935
  });
@@ -2789,10 +2945,8 @@ function noWindowOpenIf(
2789
2945
  pattern = pattern.slice(1);
2790
2946
  }
2791
2947
  const rePattern = safe.patternToRegex(pattern);
2792
- let autoRemoveAfter = parseInt(delay);
2793
- if ( isNaN(autoRemoveAfter) ) {
2794
- autoRemoveAfter = -1;
2795
- }
2948
+ const autoRemoveAfter = (parseFloat(delay) || 0) * 1000;
2949
+ const setTimeout = self.setTimeout;
2796
2950
  const createDecoy = function(tag, urlProp, url) {
2797
2951
  const decoyElem = document.createElement(tag);
2798
2952
  decoyElem[urlProp] = url;
@@ -2801,54 +2955,63 @@ function noWindowOpenIf(
2801
2955
  decoyElem.style.setProperty('top','-1px', 'important');
2802
2956
  decoyElem.style.setProperty('width','1px', 'important');
2803
2957
  document.body.appendChild(decoyElem);
2804
- setTimeout(( ) => { decoyElem.remove(); }, autoRemoveAfter * 1000);
2958
+ setTimeout(( ) => { decoyElem.remove(); }, autoRemoveAfter);
2805
2959
  return decoyElem;
2806
2960
  };
2807
- window.open = new Proxy(window.open, {
2808
- apply: function(target, thisArg, args) {
2809
- const haystack = args.join(' ');
2810
- if ( rePattern.test(haystack) !== targetMatchResult ) {
2811
- if ( safe.logLevel > 1 ) {
2812
- safe.uboLog(logPrefix, `Allowed (${args.join(', ')})`);
2813
- }
2814
- return Reflect.apply(target, thisArg, args);
2815
- }
2816
- safe.uboLog(logPrefix, `Prevented (${args.join(', ')})`);
2817
- if ( autoRemoveAfter < 0 ) { return null; }
2818
- const decoyElem = decoy === 'obj'
2819
- ? createDecoy('object', 'data', ...args)
2820
- : createDecoy('iframe', 'src', ...args);
2821
- let popup = decoyElem.contentWindow;
2822
- if ( typeof popup === 'object' && popup !== null ) {
2823
- Object.defineProperty(popup, 'closed', { value: false });
2824
- } else {
2825
- const noopFunc = (function(){}).bind(self);
2826
- popup = new Proxy(self, {
2827
- get: function(target, prop) {
2828
- if ( prop === 'closed' ) { return false; }
2829
- const r = Reflect.get(...arguments);
2830
- if ( typeof r === 'function' ) { return noopFunc; }
2831
- return target[prop];
2832
- },
2833
- set: function() {
2834
- return Reflect.set(...arguments);
2835
- },
2836
- });
2837
- }
2838
- if ( safe.logLevel !== 0 ) {
2839
- popup = new Proxy(popup, {
2840
- get: function(target, prop) {
2841
- safe.uboLog(logPrefix, 'window.open / get', prop, '===', target[prop]);
2842
- return Reflect.get(...arguments);
2843
- },
2844
- set: function(target, prop, value) {
2845
- safe.uboLog(logPrefix, 'window.open / set', prop, '=', value);
2846
- return Reflect.set(...arguments);
2847
- },
2848
- });
2961
+ const noopFunc = function(){};
2962
+ proxyApplyFn('open', function open(context) {
2963
+ const { callArgs } = context;
2964
+ const haystack = callArgs.join(' ');
2965
+ if ( rePattern.test(haystack) !== targetMatchResult ) {
2966
+ if ( safe.logLevel > 1 ) {
2967
+ safe.uboLog(logPrefix, `Allowed (${callArgs.join(', ')})`);
2849
2968
  }
2850
- return popup;
2969
+ return context.reflect();
2970
+ }
2971
+ safe.uboLog(logPrefix, `Prevented (${callArgs.join(', ')})`);
2972
+ if ( delay === '' ) { return null; }
2973
+ if ( decoy === 'blank' ) {
2974
+ callArgs[0] = 'about:blank';
2975
+ const r = context.reflect();
2976
+ setTimeout(( ) => { r.close(); }, autoRemoveAfter);
2977
+ return r;
2978
+ }
2979
+ const decoyElem = decoy === 'obj'
2980
+ ? createDecoy('object', 'data', ...callArgs)
2981
+ : createDecoy('iframe', 'src', ...callArgs);
2982
+ let popup = decoyElem.contentWindow;
2983
+ if ( typeof popup === 'object' && popup !== null ) {
2984
+ Object.defineProperty(popup, 'closed', { value: false });
2985
+ } else {
2986
+ popup = new Proxy(self, {
2987
+ get: function(target, prop, ...args) {
2988
+ if ( prop === 'closed' ) { return false; }
2989
+ const r = Reflect.get(target, prop, ...args);
2990
+ if ( typeof r === 'function' ) { return noopFunc; }
2991
+ return r;
2992
+ },
2993
+ set: function(...args) {
2994
+ return Reflect.set(...args);
2995
+ },
2996
+ });
2851
2997
  }
2998
+ if ( safe.logLevel !== 0 ) {
2999
+ popup = new Proxy(popup, {
3000
+ get: function(target, prop, ...args) {
3001
+ const r = Reflect.get(target, prop, ...args);
3002
+ safe.uboLog(logPrefix, `popup / get ${prop} === ${r}`);
3003
+ if ( typeof r === 'function' ) {
3004
+ return (...args) => { return r.call(target, ...args); };
3005
+ }
3006
+ return r;
3007
+ },
3008
+ set: function(target, prop, value, ...args) {
3009
+ safe.uboLog(logPrefix, `popup / set ${prop} = ${value}`);
3010
+ return Reflect.set(target, prop, value, ...args);
3011
+ },
3012
+ });
3013
+ }
3014
+ return popup;
2852
3015
  });
2853
3016
  }
2854
3017
 
@@ -2974,11 +3137,11 @@ function alertBuster() {
2974
3137
  apply: function(a) {
2975
3138
  console.info(a);
2976
3139
  },
2977
- get(target, prop, receiver) {
3140
+ get(target, prop) {
2978
3141
  if ( prop === 'toString' ) {
2979
3142
  return target.toString.bind(target);
2980
3143
  }
2981
- return Reflect.get(target, prop, receiver);
3144
+ return Reflect.get(target, prop);
2982
3145
  },
2983
3146
  });
2984
3147
  }
@@ -3245,6 +3408,9 @@ function xmlPrune(
3245
3408
  const serializer = new XMLSerializer();
3246
3409
  const textout = serializer.serializeToString(thisArg.responseXML);
3247
3410
  Object.defineProperty(thisArg, 'responseText', { value: textout });
3411
+ if ( typeof thisArg.response === 'string' ) {
3412
+ Object.defineProperty(thisArg, 'response', { value: textout });
3413
+ }
3248
3414
  return;
3249
3415
  }
3250
3416
  if (
@@ -3510,9 +3676,12 @@ function hrefSanitizer(
3510
3676
  const end = recursive ? source.indexOf('?', 1) : source.length;
3511
3677
  try {
3512
3678
  const url = new URL(href, document.location);
3513
- const value = url.searchParams.get(source.slice(1, end));
3679
+ let value = url.searchParams.get(source.slice(1, end));
3514
3680
  if ( value === null ) { return href }
3515
3681
  if ( recursive ) { return extractParam(value, source.slice(end)); }
3682
+ if ( value.includes(' ') ) {
3683
+ value = value.replace(/ /g, '%20');
3684
+ }
3516
3685
  return value;
3517
3686
  } catch(x) {
3518
3687
  }
@@ -3695,7 +3864,7 @@ function spoofCSS(
3695
3864
  const targetElements = new WeakSet(document.querySelectorAll(selector));
3696
3865
  if ( targetElements.has(args[0]) === false ) { return style; }
3697
3866
  const proxiedStyle = new Proxy(style, {
3698
- get(target, prop, receiver) {
3867
+ get(target, prop) {
3699
3868
  if ( typeof target[prop] === 'function' ) {
3700
3869
  if ( prop === 'getPropertyValue' ) {
3701
3870
  return cloackFunc(function getPropertyValue(prop) {
@@ -3707,7 +3876,7 @@ function spoofCSS(
3707
3876
  if ( instanceProperties.includes(prop) ) {
3708
3877
  return Reflect.get(target, prop);
3709
3878
  }
3710
- return spoofStyle(prop, Reflect.get(target, prop, receiver));
3879
+ return spoofStyle(prop, Reflect.get(target, prop));
3711
3880
  },
3712
3881
  getOwnPropertyDescriptor(target, prop) {
3713
3882
  if ( propToValueMap.has(prop) ) {
@@ -3723,11 +3892,11 @@ function spoofCSS(
3723
3892
  });
3724
3893
  return proxiedStyle;
3725
3894
  },
3726
- get(target, prop, receiver) {
3895
+ get(target, prop) {
3727
3896
  if ( prop === 'toString' ) {
3728
3897
  return target.toString.bind(target);
3729
3898
  }
3730
- return Reflect.get(target, prop, receiver);
3899
+ return Reflect.get(target, prop);
3731
3900
  },
3732
3901
  });
3733
3902
  Element.prototype.getBoundingClientRect = new Proxy(Element.prototype.getBoundingClientRect, {
@@ -3746,11 +3915,11 @@ function spoofCSS(
3746
3915
  }
3747
3916
  return new self.DOMRect(rect.x, rect.y, width, height);
3748
3917
  },
3749
- get(target, prop, receiver) {
3918
+ get(target, prop) {
3750
3919
  if ( prop === 'toString' ) {
3751
3920
  return target.toString.bind(target);
3752
3921
  }
3753
- return Reflect.get(target, prop, receiver);
3922
+ return Reflect.get(target, prop);
3754
3923
  },
3755
3924
  });
3756
3925
  }
@@ -3792,6 +3961,7 @@ builtinScriptlets.push({
3792
3961
  fn: setCookie,
3793
3962
  world: 'ISOLATED',
3794
3963
  dependencies: [
3964
+ 'get-safe-cookie-values.fn',
3795
3965
  'safe-self.fn',
3796
3966
  'set-cookie.fn',
3797
3967
  ],
@@ -3804,27 +3974,10 @@ function setCookie(
3804
3974
  if ( name === '' ) { return; }
3805
3975
  const safe = safeSelf();
3806
3976
  const logPrefix = safe.makeLogPrefix('set-cookie', name, value, path);
3807
-
3808
- const validValues = [
3809
- 'accept', 'reject',
3810
- 'accepted', 'rejected', 'notaccepted',
3811
- 'allow', 'deny',
3812
- 'allowed', 'disallow',
3813
- 'enable', 'disable',
3814
- 'enabled', 'disabled',
3815
- 'ok',
3816
- 'on', 'off',
3817
- 'true', 't', 'false', 'f',
3818
- 'yes', 'y', 'no', 'n',
3819
- 'necessary', 'required',
3820
- 'approved', 'disapproved',
3821
- 'hide', 'hidden',
3822
- 'essential', 'nonessential',
3823
- 'dismiss', 'dismissed',
3824
- ];
3825
3977
  const normalized = value.toLowerCase();
3826
3978
  const match = /^("?)(.+)\1$/.exec(normalized);
3827
3979
  const unquoted = match && match[2] || normalized;
3980
+ const validValues = getSafeCookieValuesFn();
3828
3981
  if ( validValues.includes(unquoted) === false ) {
3829
3982
  if ( /^\d+$/.test(unquoted) === false ) { return; }
3830
3983
  const n = parseInt(value, 10);
@@ -4757,8 +4910,8 @@ function trustedPruneOutboundObject(
4757
4910
  if ( propChain === '' ) { return; }
4758
4911
  const safe = safeSelf();
4759
4912
  const extraArgs = safe.getExtraArgs(Array.from(arguments), 3);
4760
- const reflector = proxyApplyFn(propChain, function(...args) {
4761
- const objBefore = reflector(...args);
4913
+ proxyApplyFn(propChain, function(context) {
4914
+ const objBefore = context.reflect();
4762
4915
  if ( objBefore instanceof Object === false ) { return objBefore; }
4763
4916
  const objAfter = objectPruneFn(
4764
4917
  objBefore,
@@ -4791,26 +4944,27 @@ function trustedReplaceArgument(
4791
4944
  if ( propChain === '' ) { return; }
4792
4945
  const safe = safeSelf();
4793
4946
  const logPrefix = safe.makeLogPrefix('trusted-replace-argument', propChain, argposRaw, argraw);
4794
- const argpos = parseInt(argposRaw, 10) || 0;
4947
+ const argoffset = parseInt(argposRaw, 10) || 0;
4795
4948
  const extraArgs = safe.getExtraArgs(Array.from(arguments), 3);
4796
4949
  const normalValue = validateConstantFn(true, argraw, extraArgs);
4797
4950
  const reCondition = extraArgs.condition
4798
4951
  ? safe.patternToRegex(extraArgs.condition)
4799
4952
  : /^/;
4800
- const reflector = proxyApplyFn(propChain, function(...args) {
4953
+ proxyApplyFn(propChain, function(context) {
4954
+ const { callArgs } = context;
4801
4955
  if ( argposRaw === '' ) {
4802
- safe.uboLog(logPrefix, `Arguments:\n${args.join('\n')}`);
4803
- return reflector(...args);
4804
- }
4805
- const arglist = args[args.length-1];
4806
- if ( Array.isArray(arglist) === false ) { return reflector(...args); }
4807
- const argBefore = arglist[argpos];
4808
- if ( safe.RegExp_test.call(reCondition, argBefore) === false ) {
4809
- return reflector(...args);
4810
- }
4811
- arglist[argpos] = normalValue;
4812
- safe.uboLog(logPrefix, `Replaced argument:\nBefore: ${JSON.stringify(argBefore)}\nAfter: ${normalValue}`);
4813
- return reflector(...args);
4956
+ safe.uboLog(logPrefix, `Arguments:\n${callArgs.join('\n')}`);
4957
+ return context.reflect();
4958
+ }
4959
+ const argpos = argoffset >= 0 ? argoffset : callArgs.length - argoffset;
4960
+ if ( argpos >= 0 && argpos < callArgs.length ) {
4961
+ const argBefore = callArgs[argpos];
4962
+ if ( safe.RegExp_test.call(reCondition, argBefore) ) {
4963
+ callArgs[argpos] = normalValue;
4964
+ safe.uboLog(logPrefix, `Replaced argument:\nBefore: ${JSON.stringify(argBefore)}\nAfter: ${normalValue}`);
4965
+ }
4966
+ }
4967
+ return context.reflect();
4814
4968
  });
4815
4969
  }
4816
4970
 
@@ -4827,24 +4981,27 @@ builtinScriptlets.push({
4827
4981
  });
4828
4982
  function trustedReplaceOutboundText(
4829
4983
  propChain = '',
4830
- pattern = '',
4831
- replacement = '',
4984
+ rawPattern = '',
4985
+ rawReplacement = '',
4832
4986
  ...args
4833
4987
  ) {
4834
4988
  if ( propChain === '' ) { return; }
4835
4989
  const safe = safeSelf();
4836
- const logPrefix = safe.makeLogPrefix('trusted-replace-outbound-text', propChain, pattern, replacement, ...args);
4837
- const rePattern = safe.patternToRegex(pattern);
4990
+ const logPrefix = safe.makeLogPrefix('trusted-replace-outbound-text', propChain, rawPattern, rawReplacement, ...args);
4991
+ const rePattern = safe.patternToRegex(rawPattern);
4992
+ const replacement = rawReplacement.startsWith('json:')
4993
+ ? safe.JSON_parse(rawReplacement.slice(5))
4994
+ : rawReplacement;
4838
4995
  const extraArgs = safe.getExtraArgs(args);
4839
4996
  const reCondition = safe.patternToRegex(extraArgs.condition || '');
4840
- const reflector = proxyApplyFn(propChain, function(...args) {
4841
- const encodedTextBefore = reflector(...args);
4997
+ proxyApplyFn(propChain, function(context) {
4998
+ const encodedTextBefore = context.reflect();
4842
4999
  let textBefore = encodedTextBefore;
4843
5000
  if ( extraArgs.encoding === 'base64' ) {
4844
5001
  try { textBefore = self.atob(encodedTextBefore); }
4845
5002
  catch(ex) { return encodedTextBefore; }
4846
5003
  }
4847
- if ( pattern === '' ) {
5004
+ if ( rawPattern === '' ) {
4848
5005
  safe.uboLog(logPrefix, 'Decoded outbound text:\n', textBefore);
4849
5006
  return encodedTextBefore;
4850
5007
  }
@@ -4915,34 +5072,31 @@ function trustedSuppressNativeMethod(
4915
5072
  return { type: 'exact', value: undefined };
4916
5073
  }
4917
5074
  });
4918
- const reflector = proxyApplyFn(methodPath, function(...args) {
5075
+ proxyApplyFn(methodPath, function(context) {
5076
+ const { callArgs } = context;
4919
5077
  if ( signature === '' ) {
4920
- safe.uboLog(logPrefix, `Arguments:\n${args.join('\n')}`);
4921
- return reflector(...args);
4922
- }
4923
- const arglist = args[args.length-1];
4924
- if ( Array.isArray(arglist) === false ) {
4925
- return reflector(...args);
5078
+ safe.uboLog(logPrefix, `Arguments:\n${callArgs.join('\n')}`);
5079
+ return context.reflect();
4926
5080
  }
4927
- if ( arglist.length < signatureArgs.length ) {
4928
- return reflector(...args);
5081
+ if ( callArgs.length < signatureArgs.length ) {
5082
+ return context.reflect();
4929
5083
  }
4930
5084
  for ( let i = 0; i < signatureArgs.length; i++ ) {
4931
5085
  const signatureArg = signatureArgs[i];
4932
5086
  if ( signatureArg === undefined ) { continue; }
4933
- const targetArg = arglist[i];
5087
+ const targetArg = callArgs[i];
4934
5088
  if ( signatureArg.type === 'exact' ) {
4935
5089
  if ( targetArg !== signatureArg.value ) {
4936
- return reflector(...args);
5090
+ return context.reflect();
4937
5091
  }
4938
5092
  }
4939
5093
  if ( signatureArg.type === 'pattern' ) {
4940
5094
  if ( safe.RegExp_test.call(signatureArg.re, targetArg) === false ) {
4941
- return reflector(...args);
5095
+ return context.reflect();
4942
5096
  }
4943
5097
  }
4944
5098
  }
4945
- safe.uboLog(logPrefix, `Suppressed:\n${args.join('\n')}`);
5099
+ safe.uboLog(logPrefix, `Suppressed:\n${callArgs.join('\n')}`);
4946
5100
  if ( how === 'abort' ) {
4947
5101
  throw new ReferenceError();
4948
5102
  }