From 642dd55534722a9da5013e150b89fe30353b264d Mon Sep 17 00:00:00 2001 From: Ghostery Adblocker Bot Date: Fri, 19 Dec 2025 20:09:07 +0000 Subject: [PATCH] Update scriptlets --- package.json | 2 +- ubo.js | 4914 ++++++++++++++++++++++++++++---------------------- 2 files changed, 2754 insertions(+), 2162 deletions(-) diff --git a/package.json b/package.json index 9b58e1c..4948bcf 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "main": "index.js", "type": "module", "scripts": { - "build": "deno build.ts --tagName 1.68.0 > ubo.js", + "build": "deno build.ts --tagName 1.68.1b7 > ubo.js", "test": "node --test" }, "author": { diff --git a/ubo.js b/ubo.js index 9bf0a0b..4a81f71 100644 --- a/ubo.js +++ b/ubo.js @@ -145,6 +145,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -462,6 +463,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -670,6 +672,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -968,6 +971,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -1313,6 +1317,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -1637,6 +1642,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -2397,6 +2403,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -3157,6 +3164,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -3917,6 +3925,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -4677,6 +4686,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -5464,6 +5474,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -6271,6 +6282,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -7026,6 +7038,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -7781,6 +7794,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -8519,6 +8533,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -9321,195 +9336,6 @@ function parsePropertiesToMatchFn(propsToMatch, implicit = '') { } return needles; } -function safeSelf() { - if ( scriptletGlobals.safeSelf ) { - return scriptletGlobals.safeSelf; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toStringFn': self.Function.prototype.toString, - 'Function_toString': thisArg => safe.Function_toStringFn.call(thisArg), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'RegExp': self.RegExp, - 'RegExp_test': self.RegExp.prototype.test, - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parseFn': self.JSON.parse, - 'JSON_stringifyFn': self.JSON.stringify, - 'JSON_parse': (...args) => safe.JSON_parseFn.call(safe.JSON, ...args), - 'JSON_stringify': (...args) => safe.JSON_stringifyFn.call(safe.JSON, ...args), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test.call(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - getExtraArgs(args, offset = 0) { - const entries = args.slice(offset).reduce((out, v, i, a) => { - if ( (i & 1) === 0 ) { - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - } - return out; - }, []); - return this.Object_fromEntries(entries); - }, - onIdle(fn, options) { - if ( self.requestIdleCallback ) { - return self.requestIdleCallback(fn, options); - } - return self.requestAnimationFrame(fn); - }, - offIdle(id) { - if ( self.requestIdleCallback ) { - return self.cancelIdleCallback(id); - } - return self.cancelAnimationFrame(id); - } - }; - scriptletGlobals.safeSelf = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} function matchObjectPropertiesFn(propNeedles, ...objs) { const safe = safeSelf(); const matched = []; @@ -9976,6 +9802,230 @@ class JSONPath { } } } +function safeSelf() { + if ( scriptletGlobals.safeSelf ) { + return scriptletGlobals.safeSelf; + } + const self = globalThis; + const safe = { + 'Array_from': Array.from, + 'Error': self.Error, + 'Function_toStringFn': self.Function.prototype.toString, + 'Function_toString': thisArg => safe.Function_toStringFn.call(thisArg), + 'Math_floor': Math.floor, + 'Math_max': Math.max, + 'Math_min': Math.min, + 'Math_random': Math.random, + 'Object': Object, + 'Object_defineProperty': Object.defineProperty.bind(Object), + 'Object_defineProperties': Object.defineProperties.bind(Object), + 'Object_fromEntries': Object.fromEntries.bind(Object), + 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), + 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, + 'RegExp': self.RegExp, + 'RegExp_test': self.RegExp.prototype.test, + 'RegExp_exec': self.RegExp.prototype.exec, + 'Request_clone': self.Request.prototype.clone, + 'String': self.String, + 'String_fromCharCode': String.fromCharCode, + 'String_split': String.prototype.split, + 'XMLHttpRequest': self.XMLHttpRequest, + 'addEventListener': self.EventTarget.prototype.addEventListener, + 'removeEventListener': self.EventTarget.prototype.removeEventListener, + 'fetch': self.fetch, + 'JSON': self.JSON, + 'JSON_parseFn': self.JSON.parse, + 'JSON_stringifyFn': self.JSON.stringify, + 'JSON_parse': (...args) => safe.JSON_parseFn.call(safe.JSON, ...args), + 'JSON_stringify': (...args) => safe.JSON_stringifyFn.call(safe.JSON, ...args), + 'log': console.log.bind(console), + // Properties + logLevel: 0, + // Methods + makeLogPrefix(...args) { + return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; + }, + uboLog(...args) { + if ( this.sendToLogger === undefined ) { return; } + if ( args === undefined || args[0] === '' ) { return; } + return this.sendToLogger('info', ...args); + + }, + uboErr(...args) { + if ( this.sendToLogger === undefined ) { return; } + if ( args === undefined || args[0] === '' ) { return; } + return this.sendToLogger('error', ...args); + }, + escapeRegexChars(s) { + return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + }, + initPattern(pattern, options = {}) { + if ( pattern === '' ) { + return { matchAll: true, expect: true }; + } + const expect = (options.canNegate !== true || pattern.startsWith('!') === false); + if ( expect === false ) { + pattern = pattern.slice(1); + } + const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); + if ( match !== null ) { + return { + re: new this.RegExp( + match[1], + match[2] || options.flags + ), + expect, + }; + } + if ( options.flags !== undefined ) { + return { + re: new this.RegExp(this.escapeRegexChars(pattern), + options.flags + ), + expect, + }; + } + return { pattern, expect }; + }, + testPattern(details, haystack) { + if ( details.matchAll ) { return true; } + if ( details.re ) { + return this.RegExp_test.call(details.re, haystack) === details.expect; + } + return haystack.includes(details.pattern) === details.expect; + }, + patternToRegex(pattern, flags = undefined, verbatim = false) { + if ( pattern === '' ) { return /^/; } + const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); + if ( match === null ) { + const reStr = this.escapeRegexChars(pattern); + return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); + } + try { + return new RegExp(match[1], match[2] || undefined); + } + catch { + } + return /^/; + }, + getExtraArgs(args, offset = 0) { + const entries = args.slice(offset).reduce((out, v, i, a) => { + if ( (i & 1) === 0 ) { + const rawValue = a[i+1]; + const value = /^\d+$/.test(rawValue) + ? parseInt(rawValue, 10) + : rawValue; + out.push([ a[i], value ]); + } + return out; + }, []); + return this.Object_fromEntries(entries); + }, + onIdle(fn, options) { + if ( self.requestIdleCallback ) { + return self.requestIdleCallback(fn, options); + } + return self.requestAnimationFrame(fn); + }, + offIdle(id) { + if ( self.requestIdleCallback ) { + return self.cancelIdleCallback(id); + } + return self.cancelAnimationFrame(id); + } + }; + scriptletGlobals.safeSelf = safe; + if ( scriptletGlobals.bcSecret === undefined ) { return safe; } + // This is executed only when the logger is opened + safe.logLevel = scriptletGlobals.logLevel || 1; + let lastLogType = ''; + let lastLogText = ''; + let lastLogTime = 0; + safe.toLogText = (type, ...args) => { + if ( args.length === 0 ) { return; } + const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; + if ( text === lastLogText && type === lastLogType ) { + if ( (Date.now() - lastLogTime) < 5000 ) { return; } + } + lastLogType = type; + lastLogText = text; + lastLogTime = Date.now(); + return text; + }; + try { + const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); + let bcBuffer = []; + safe.sendToLogger = (type, ...args) => { + const text = safe.toLogText(type, ...args); + if ( text === undefined ) { return; } + if ( bcBuffer === undefined ) { + return bc.postMessage({ what: 'messageToLogger', type, text }); + } + bcBuffer.push({ type, text }); + }; + bc.onmessage = ev => { + const msg = ev.data; + switch ( msg ) { + case 'iamready!': + if ( bcBuffer === undefined ) { break; } + bcBuffer.forEach(({ type, text }) => + bc.postMessage({ what: 'messageToLogger', type, text }) + ); + bcBuffer = undefined; + break; + case 'setScriptletLogLevelToOne': + safe.logLevel = 1; + break; + case 'setScriptletLogLevelToTwo': + safe.logLevel = 2; + break; + } + }; + bc.postMessage('areyouready?'); + } catch { + safe.sendToLogger = (type, ...args) => { + const text = safe.toLogText(type, ...args); + if ( text === undefined ) { return; } + safe.log(`uBO ${text}`); + }; + } + return safe; +} +function collateFetchArgumentsFn(resource, options) { + const safe = safeSelf(); + const props = [ + 'body', 'cache', 'credentials', 'duplex', 'headers', + 'integrity', 'keepalive', 'method', 'mode', 'priority', + 'redirect', 'referrer', 'referrerPolicy', 'url' + ]; + const out = {}; + if ( collateFetchArgumentsFn.collateKnownProps === undefined ) { + collateFetchArgumentsFn.collateKnownProps = (src, out) => { + for ( const prop of props ) { + if ( src[prop] === undefined ) { continue; } + out[prop] = src[prop]; + } + }; + } + if ( + typeof resource !== 'object' || + safe.Object_toString.call(resource) !== '[object Request]' + ) { + out.url = `${resource}`; + } else { + let clone; + try { + clone = safe.Request_clone.call(resource); + } catch { + } + collateFetchArgumentsFn.collateKnownProps(clone || resource, out); + } + if ( typeof options === 'object' && options !== null ) { + collateFetchArgumentsFn.collateKnownProps(options, out); + } + return out; +} function jsonEditFetchResponseFn(trusted, jsonq = '') { const safe = safeSelf(); const logPrefix = safe.makeLogPrefix( @@ -9992,18 +10042,8 @@ function jsonEditFetchResponseFn(trusted, jsonq = '') { const args = context.callArgs; const fetchPromise = context.reflect(); if ( propNeedles.size !== 0 ) { - const objs = [ args[0] instanceof Object ? args[0] : { url: args[0] } ]; - if ( objs[0] instanceof Request ) { - try { - objs[0] = safe.Request_clone.call(objs[0]); - } catch(ex) { - safe.uboErr(logPrefix, 'Error:', ex); - } - } - if ( args[1] instanceof Object ) { - objs.push(args[1]); - } - const matched = matchObjectPropertiesFn(propNeedles, ...objs); + const props = collateFetchArgumentsFn(...args); + const matched = matchObjectPropertiesFn(propNeedles, props); if ( matched === undefined ) { return fetchPromise; } if ( safe.logLevel > 1 ) { safe.uboLog(logPrefix, `Matched "propsToMatch":\n\t${matched.join('\n\t')}`); @@ -10155,195 +10195,6 @@ function parsePropertiesToMatchFn(propsToMatch, implicit = '') { } return needles; } -function safeSelf() { - if ( scriptletGlobals.safeSelf ) { - return scriptletGlobals.safeSelf; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toStringFn': self.Function.prototype.toString, - 'Function_toString': thisArg => safe.Function_toStringFn.call(thisArg), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'RegExp': self.RegExp, - 'RegExp_test': self.RegExp.prototype.test, - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parseFn': self.JSON.parse, - 'JSON_stringifyFn': self.JSON.stringify, - 'JSON_parse': (...args) => safe.JSON_parseFn.call(safe.JSON, ...args), - 'JSON_stringify': (...args) => safe.JSON_stringifyFn.call(safe.JSON, ...args), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test.call(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - getExtraArgs(args, offset = 0) { - const entries = args.slice(offset).reduce((out, v, i, a) => { - if ( (i & 1) === 0 ) { - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - } - return out; - }, []); - return this.Object_fromEntries(entries); - }, - onIdle(fn, options) { - if ( self.requestIdleCallback ) { - return self.requestIdleCallback(fn, options); - } - return self.requestAnimationFrame(fn); - }, - offIdle(id) { - if ( self.requestIdleCallback ) { - return self.cancelIdleCallback(id); - } - return self.cancelAnimationFrame(id); - } - }; - scriptletGlobals.safeSelf = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} function matchObjectPropertiesFn(propNeedles, ...objs) { const safe = safeSelf(); const matched = []; @@ -10810,6 +10661,230 @@ class JSONPath { } } } +function safeSelf() { + if ( scriptletGlobals.safeSelf ) { + return scriptletGlobals.safeSelf; + } + const self = globalThis; + const safe = { + 'Array_from': Array.from, + 'Error': self.Error, + 'Function_toStringFn': self.Function.prototype.toString, + 'Function_toString': thisArg => safe.Function_toStringFn.call(thisArg), + 'Math_floor': Math.floor, + 'Math_max': Math.max, + 'Math_min': Math.min, + 'Math_random': Math.random, + 'Object': Object, + 'Object_defineProperty': Object.defineProperty.bind(Object), + 'Object_defineProperties': Object.defineProperties.bind(Object), + 'Object_fromEntries': Object.fromEntries.bind(Object), + 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), + 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, + 'RegExp': self.RegExp, + 'RegExp_test': self.RegExp.prototype.test, + 'RegExp_exec': self.RegExp.prototype.exec, + 'Request_clone': self.Request.prototype.clone, + 'String': self.String, + 'String_fromCharCode': String.fromCharCode, + 'String_split': String.prototype.split, + 'XMLHttpRequest': self.XMLHttpRequest, + 'addEventListener': self.EventTarget.prototype.addEventListener, + 'removeEventListener': self.EventTarget.prototype.removeEventListener, + 'fetch': self.fetch, + 'JSON': self.JSON, + 'JSON_parseFn': self.JSON.parse, + 'JSON_stringifyFn': self.JSON.stringify, + 'JSON_parse': (...args) => safe.JSON_parseFn.call(safe.JSON, ...args), + 'JSON_stringify': (...args) => safe.JSON_stringifyFn.call(safe.JSON, ...args), + 'log': console.log.bind(console), + // Properties + logLevel: 0, + // Methods + makeLogPrefix(...args) { + return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; + }, + uboLog(...args) { + if ( this.sendToLogger === undefined ) { return; } + if ( args === undefined || args[0] === '' ) { return; } + return this.sendToLogger('info', ...args); + + }, + uboErr(...args) { + if ( this.sendToLogger === undefined ) { return; } + if ( args === undefined || args[0] === '' ) { return; } + return this.sendToLogger('error', ...args); + }, + escapeRegexChars(s) { + return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + }, + initPattern(pattern, options = {}) { + if ( pattern === '' ) { + return { matchAll: true, expect: true }; + } + const expect = (options.canNegate !== true || pattern.startsWith('!') === false); + if ( expect === false ) { + pattern = pattern.slice(1); + } + const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); + if ( match !== null ) { + return { + re: new this.RegExp( + match[1], + match[2] || options.flags + ), + expect, + }; + } + if ( options.flags !== undefined ) { + return { + re: new this.RegExp(this.escapeRegexChars(pattern), + options.flags + ), + expect, + }; + } + return { pattern, expect }; + }, + testPattern(details, haystack) { + if ( details.matchAll ) { return true; } + if ( details.re ) { + return this.RegExp_test.call(details.re, haystack) === details.expect; + } + return haystack.includes(details.pattern) === details.expect; + }, + patternToRegex(pattern, flags = undefined, verbatim = false) { + if ( pattern === '' ) { return /^/; } + const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); + if ( match === null ) { + const reStr = this.escapeRegexChars(pattern); + return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); + } + try { + return new RegExp(match[1], match[2] || undefined); + } + catch { + } + return /^/; + }, + getExtraArgs(args, offset = 0) { + const entries = args.slice(offset).reduce((out, v, i, a) => { + if ( (i & 1) === 0 ) { + const rawValue = a[i+1]; + const value = /^\d+$/.test(rawValue) + ? parseInt(rawValue, 10) + : rawValue; + out.push([ a[i], value ]); + } + return out; + }, []); + return this.Object_fromEntries(entries); + }, + onIdle(fn, options) { + if ( self.requestIdleCallback ) { + return self.requestIdleCallback(fn, options); + } + return self.requestAnimationFrame(fn); + }, + offIdle(id) { + if ( self.requestIdleCallback ) { + return self.cancelIdleCallback(id); + } + return self.cancelAnimationFrame(id); + } + }; + scriptletGlobals.safeSelf = safe; + if ( scriptletGlobals.bcSecret === undefined ) { return safe; } + // This is executed only when the logger is opened + safe.logLevel = scriptletGlobals.logLevel || 1; + let lastLogType = ''; + let lastLogText = ''; + let lastLogTime = 0; + safe.toLogText = (type, ...args) => { + if ( args.length === 0 ) { return; } + const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; + if ( text === lastLogText && type === lastLogType ) { + if ( (Date.now() - lastLogTime) < 5000 ) { return; } + } + lastLogType = type; + lastLogText = text; + lastLogTime = Date.now(); + return text; + }; + try { + const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); + let bcBuffer = []; + safe.sendToLogger = (type, ...args) => { + const text = safe.toLogText(type, ...args); + if ( text === undefined ) { return; } + if ( bcBuffer === undefined ) { + return bc.postMessage({ what: 'messageToLogger', type, text }); + } + bcBuffer.push({ type, text }); + }; + bc.onmessage = ev => { + const msg = ev.data; + switch ( msg ) { + case 'iamready!': + if ( bcBuffer === undefined ) { break; } + bcBuffer.forEach(({ type, text }) => + bc.postMessage({ what: 'messageToLogger', type, text }) + ); + bcBuffer = undefined; + break; + case 'setScriptletLogLevelToOne': + safe.logLevel = 1; + break; + case 'setScriptletLogLevelToTwo': + safe.logLevel = 2; + break; + } + }; + bc.postMessage('areyouready?'); + } catch { + safe.sendToLogger = (type, ...args) => { + const text = safe.toLogText(type, ...args); + if ( text === undefined ) { return; } + safe.log(`uBO ${text}`); + }; + } + return safe; +} +function collateFetchArgumentsFn(resource, options) { + const safe = safeSelf(); + const props = [ + 'body', 'cache', 'credentials', 'duplex', 'headers', + 'integrity', 'keepalive', 'method', 'mode', 'priority', + 'redirect', 'referrer', 'referrerPolicy', 'url' + ]; + const out = {}; + if ( collateFetchArgumentsFn.collateKnownProps === undefined ) { + collateFetchArgumentsFn.collateKnownProps = (src, out) => { + for ( const prop of props ) { + if ( src[prop] === undefined ) { continue; } + out[prop] = src[prop]; + } + }; + } + if ( + typeof resource !== 'object' || + safe.Object_toString.call(resource) !== '[object Request]' + ) { + out.url = `${resource}`; + } else { + let clone; + try { + clone = safe.Request_clone.call(resource); + } catch { + } + collateFetchArgumentsFn.collateKnownProps(clone || resource, out); + } + if ( typeof options === 'object' && options !== null ) { + collateFetchArgumentsFn.collateKnownProps(options, out); + } + return out; +} function jsonEditFetchResponseFn(trusted, jsonq = '') { const safe = safeSelf(); const logPrefix = safe.makeLogPrefix( @@ -10826,18 +10901,8 @@ function jsonEditFetchResponseFn(trusted, jsonq = '') { const args = context.callArgs; const fetchPromise = context.reflect(); if ( propNeedles.size !== 0 ) { - const objs = [ args[0] instanceof Object ? args[0] : { url: args[0] } ]; - if ( objs[0] instanceof Request ) { - try { - objs[0] = safe.Request_clone.call(objs[0]); - } catch(ex) { - safe.uboErr(logPrefix, 'Error:', ex); - } - } - if ( args[1] instanceof Object ) { - objs.push(args[1]); - } - const matched = matchObjectPropertiesFn(propNeedles, ...objs); + const props = collateFetchArgumentsFn(...args); + const matched = matchObjectPropertiesFn(propNeedles, props); if ( matched === undefined ) { return fetchPromise; } if ( safe.logLevel > 1 ) { safe.uboLog(logPrefix, `Matched "propsToMatch":\n\t${matched.join('\n\t')}`); @@ -10989,195 +11054,6 @@ function parsePropertiesToMatchFn(propsToMatch, implicit = '') { } return needles; } -function safeSelf() { - if ( scriptletGlobals.safeSelf ) { - return scriptletGlobals.safeSelf; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toStringFn': self.Function.prototype.toString, - 'Function_toString': thisArg => safe.Function_toStringFn.call(thisArg), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'RegExp': self.RegExp, - 'RegExp_test': self.RegExp.prototype.test, - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parseFn': self.JSON.parse, - 'JSON_stringifyFn': self.JSON.stringify, - 'JSON_parse': (...args) => safe.JSON_parseFn.call(safe.JSON, ...args), - 'JSON_stringify': (...args) => safe.JSON_stringifyFn.call(safe.JSON, ...args), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test.call(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - getExtraArgs(args, offset = 0) { - const entries = args.slice(offset).reduce((out, v, i, a) => { - if ( (i & 1) === 0 ) { - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - } - return out; - }, []); - return this.Object_fromEntries(entries); - }, - onIdle(fn, options) { - if ( self.requestIdleCallback ) { - return self.requestIdleCallback(fn, options); - } - return self.requestAnimationFrame(fn); - }, - offIdle(id) { - if ( self.requestIdleCallback ) { - return self.cancelIdleCallback(id); - } - return self.cancelAnimationFrame(id); - } - }; - scriptletGlobals.safeSelf = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} function matchObjectPropertiesFn(propNeedles, ...objs) { const safe = safeSelf(); const matched = []; @@ -11644,6 +11520,230 @@ class JSONPath { } } } +function safeSelf() { + if ( scriptletGlobals.safeSelf ) { + return scriptletGlobals.safeSelf; + } + const self = globalThis; + const safe = { + 'Array_from': Array.from, + 'Error': self.Error, + 'Function_toStringFn': self.Function.prototype.toString, + 'Function_toString': thisArg => safe.Function_toStringFn.call(thisArg), + 'Math_floor': Math.floor, + 'Math_max': Math.max, + 'Math_min': Math.min, + 'Math_random': Math.random, + 'Object': Object, + 'Object_defineProperty': Object.defineProperty.bind(Object), + 'Object_defineProperties': Object.defineProperties.bind(Object), + 'Object_fromEntries': Object.fromEntries.bind(Object), + 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), + 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, + 'RegExp': self.RegExp, + 'RegExp_test': self.RegExp.prototype.test, + 'RegExp_exec': self.RegExp.prototype.exec, + 'Request_clone': self.Request.prototype.clone, + 'String': self.String, + 'String_fromCharCode': String.fromCharCode, + 'String_split': String.prototype.split, + 'XMLHttpRequest': self.XMLHttpRequest, + 'addEventListener': self.EventTarget.prototype.addEventListener, + 'removeEventListener': self.EventTarget.prototype.removeEventListener, + 'fetch': self.fetch, + 'JSON': self.JSON, + 'JSON_parseFn': self.JSON.parse, + 'JSON_stringifyFn': self.JSON.stringify, + 'JSON_parse': (...args) => safe.JSON_parseFn.call(safe.JSON, ...args), + 'JSON_stringify': (...args) => safe.JSON_stringifyFn.call(safe.JSON, ...args), + 'log': console.log.bind(console), + // Properties + logLevel: 0, + // Methods + makeLogPrefix(...args) { + return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; + }, + uboLog(...args) { + if ( this.sendToLogger === undefined ) { return; } + if ( args === undefined || args[0] === '' ) { return; } + return this.sendToLogger('info', ...args); + + }, + uboErr(...args) { + if ( this.sendToLogger === undefined ) { return; } + if ( args === undefined || args[0] === '' ) { return; } + return this.sendToLogger('error', ...args); + }, + escapeRegexChars(s) { + return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + }, + initPattern(pattern, options = {}) { + if ( pattern === '' ) { + return { matchAll: true, expect: true }; + } + const expect = (options.canNegate !== true || pattern.startsWith('!') === false); + if ( expect === false ) { + pattern = pattern.slice(1); + } + const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); + if ( match !== null ) { + return { + re: new this.RegExp( + match[1], + match[2] || options.flags + ), + expect, + }; + } + if ( options.flags !== undefined ) { + return { + re: new this.RegExp(this.escapeRegexChars(pattern), + options.flags + ), + expect, + }; + } + return { pattern, expect }; + }, + testPattern(details, haystack) { + if ( details.matchAll ) { return true; } + if ( details.re ) { + return this.RegExp_test.call(details.re, haystack) === details.expect; + } + return haystack.includes(details.pattern) === details.expect; + }, + patternToRegex(pattern, flags = undefined, verbatim = false) { + if ( pattern === '' ) { return /^/; } + const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); + if ( match === null ) { + const reStr = this.escapeRegexChars(pattern); + return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); + } + try { + return new RegExp(match[1], match[2] || undefined); + } + catch { + } + return /^/; + }, + getExtraArgs(args, offset = 0) { + const entries = args.slice(offset).reduce((out, v, i, a) => { + if ( (i & 1) === 0 ) { + const rawValue = a[i+1]; + const value = /^\d+$/.test(rawValue) + ? parseInt(rawValue, 10) + : rawValue; + out.push([ a[i], value ]); + } + return out; + }, []); + return this.Object_fromEntries(entries); + }, + onIdle(fn, options) { + if ( self.requestIdleCallback ) { + return self.requestIdleCallback(fn, options); + } + return self.requestAnimationFrame(fn); + }, + offIdle(id) { + if ( self.requestIdleCallback ) { + return self.cancelIdleCallback(id); + } + return self.cancelAnimationFrame(id); + } + }; + scriptletGlobals.safeSelf = safe; + if ( scriptletGlobals.bcSecret === undefined ) { return safe; } + // This is executed only when the logger is opened + safe.logLevel = scriptletGlobals.logLevel || 1; + let lastLogType = ''; + let lastLogText = ''; + let lastLogTime = 0; + safe.toLogText = (type, ...args) => { + if ( args.length === 0 ) { return; } + const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; + if ( text === lastLogText && type === lastLogType ) { + if ( (Date.now() - lastLogTime) < 5000 ) { return; } + } + lastLogType = type; + lastLogText = text; + lastLogTime = Date.now(); + return text; + }; + try { + const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); + let bcBuffer = []; + safe.sendToLogger = (type, ...args) => { + const text = safe.toLogText(type, ...args); + if ( text === undefined ) { return; } + if ( bcBuffer === undefined ) { + return bc.postMessage({ what: 'messageToLogger', type, text }); + } + bcBuffer.push({ type, text }); + }; + bc.onmessage = ev => { + const msg = ev.data; + switch ( msg ) { + case 'iamready!': + if ( bcBuffer === undefined ) { break; } + bcBuffer.forEach(({ type, text }) => + bc.postMessage({ what: 'messageToLogger', type, text }) + ); + bcBuffer = undefined; + break; + case 'setScriptletLogLevelToOne': + safe.logLevel = 1; + break; + case 'setScriptletLogLevelToTwo': + safe.logLevel = 2; + break; + } + }; + bc.postMessage('areyouready?'); + } catch { + safe.sendToLogger = (type, ...args) => { + const text = safe.toLogText(type, ...args); + if ( text === undefined ) { return; } + safe.log(`uBO ${text}`); + }; + } + return safe; +} +function collateFetchArgumentsFn(resource, options) { + const safe = safeSelf(); + const props = [ + 'body', 'cache', 'credentials', 'duplex', 'headers', + 'integrity', 'keepalive', 'method', 'mode', 'priority', + 'redirect', 'referrer', 'referrerPolicy', 'url' + ]; + const out = {}; + if ( collateFetchArgumentsFn.collateKnownProps === undefined ) { + collateFetchArgumentsFn.collateKnownProps = (src, out) => { + for ( const prop of props ) { + if ( src[prop] === undefined ) { continue; } + out[prop] = src[prop]; + } + }; + } + if ( + typeof resource !== 'object' || + safe.Object_toString.call(resource) !== '[object Request]' + ) { + out.url = `${resource}`; + } else { + let clone; + try { + clone = safe.Request_clone.call(resource); + } catch { + } + collateFetchArgumentsFn.collateKnownProps(clone || resource, out); + } + if ( typeof options === 'object' && options !== null ) { + collateFetchArgumentsFn.collateKnownProps(options, out); + } + return out; +} function jsonEditFetchRequestFn(trusted, jsonq = '') { const safe = safeSelf(); const logPrefix = safe.makeLogPrefix( @@ -11676,17 +11776,8 @@ function jsonEditFetchRequestFn(trusted, jsonq = '') { return context.reflect(); } if ( propNeedles.size !== 0 ) { - const objs = [ - resource instanceof Object ? resource : { url: `${resource}` } - ]; - if ( objs[0] instanceof Request ) { - try { - objs[0] = safe.Request_clone.call(objs[0]); - } catch(ex) { - safe.uboErr(logPrefix, 'Error:', ex); - } - } - const matched = matchObjectPropertiesFn(propNeedles, ...objs); + const props = collateFetchArgumentsFn(resource, options); + const matched = matchObjectPropertiesFn(propNeedles, props); if ( matched === undefined ) { return context.reflect(); } if ( safe.logLevel > 1 ) { safe.uboLog(logPrefix, `Matched "propsToMatch":\n\t${matched.join('\n\t')}`); @@ -11819,195 +11910,6 @@ function parsePropertiesToMatchFn(propsToMatch, implicit = '') { } return needles; } -function safeSelf() { - if ( scriptletGlobals.safeSelf ) { - return scriptletGlobals.safeSelf; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toStringFn': self.Function.prototype.toString, - 'Function_toString': thisArg => safe.Function_toStringFn.call(thisArg), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'RegExp': self.RegExp, - 'RegExp_test': self.RegExp.prototype.test, - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parseFn': self.JSON.parse, - 'JSON_stringifyFn': self.JSON.stringify, - 'JSON_parse': (...args) => safe.JSON_parseFn.call(safe.JSON, ...args), - 'JSON_stringify': (...args) => safe.JSON_stringifyFn.call(safe.JSON, ...args), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test.call(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - getExtraArgs(args, offset = 0) { - const entries = args.slice(offset).reduce((out, v, i, a) => { - if ( (i & 1) === 0 ) { - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - } - return out; - }, []); - return this.Object_fromEntries(entries); - }, - onIdle(fn, options) { - if ( self.requestIdleCallback ) { - return self.requestIdleCallback(fn, options); - } - return self.requestAnimationFrame(fn); - }, - offIdle(id) { - if ( self.requestIdleCallback ) { - return self.cancelIdleCallback(id); - } - return self.cancelAnimationFrame(id); - } - }; - scriptletGlobals.safeSelf = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} function matchObjectPropertiesFn(propNeedles, ...objs) { const safe = safeSelf(); const matched = []; @@ -12474,116 +12376,6 @@ class JSONPath { } } } -function jsonEditFetchRequestFn(trusted, jsonq = '') { - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix( - `${trusted ? 'trusted-' : ''}json-edit-fetch-request`, - jsonq - ); - const jsonp = JSONPath.create(jsonq); - if ( jsonp.valid === false || jsonp.value !== undefined && trusted !== true ) { - return safe.uboLog(logPrefix, 'Bad JSONPath query'); - } - const extraArgs = safe.getExtraArgs(Array.from(arguments), 2); - const propNeedles = parsePropertiesToMatchFn(extraArgs.propsToMatch, 'url'); - const filterBody = body => { - if ( typeof body !== 'string' ) { return; } - let data; - try { data = safe.JSON_parse(body); } - catch { } - if ( data instanceof Object === false ) { return; } - const objAfter = jsonp.apply(data); - if ( objAfter === undefined ) { return; } - return safe.JSON_stringify(objAfter); - } - const proxyHandler = context => { - const args = context.callArgs; - const [ resource, options ] = args; - const bodyBefore = options?.body; - if ( Boolean(bodyBefore) === false ) { return context.reflect(); } - const bodyAfter = filterBody(bodyBefore); - if ( bodyAfter === undefined || bodyAfter === bodyBefore ) { - return context.reflect(); - } - if ( propNeedles.size !== 0 ) { - const objs = [ - resource instanceof Object ? resource : { url: `${resource}` } - ]; - if ( objs[0] instanceof Request ) { - try { - objs[0] = safe.Request_clone.call(objs[0]); - } catch(ex) { - safe.uboErr(logPrefix, 'Error:', ex); - } - } - const matched = matchObjectPropertiesFn(propNeedles, ...objs); - if ( matched === undefined ) { return context.reflect(); } - if ( safe.logLevel > 1 ) { - safe.uboLog(logPrefix, `Matched "propsToMatch":\n\t${matched.join('\n\t')}`); - } - } - safe.uboLog(logPrefix, 'Edited'); - if ( safe.logLevel > 1 ) { - safe.uboLog(logPrefix, `After edit:\n${bodyAfter}`); - } - options.body = bodyAfter; - return context.reflect(); - }; - proxyApplyFn('fetch', proxyHandler); - proxyApplyFn('Request', proxyHandler); -} -function trustedJsonEditFetchRequest(jsonq = '', ...args) { - jsonEditFetchRequestFn(true, jsonq, ...args); -}; -trustedJsonEditFetchRequest(...args); -}, -}; - - -scriptlets['jsonl-edit-xhr-response.js'] = { -aliases: [], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function parsePropertiesToMatchFn(propsToMatch, implicit = '') { - const safe = safeSelf(); - const needles = new Map(); - if ( propsToMatch === undefined || propsToMatch === '' ) { return needles; } - const options = { canNegate: true }; - for ( const needle of safe.String_split.call(propsToMatch, /\s+/) ) { - let [ prop, pattern ] = safe.String_split.call(needle, ':'); - if ( prop === '' ) { continue; } - if ( pattern !== undefined && /[^$\w -]/.test(prop) ) { - prop = `${prop}:${pattern}`; - pattern = undefined; - } - if ( pattern !== undefined ) { - needles.set(prop, safe.initPattern(pattern, options)); - } else if ( implicit !== '' ) { - needles.set(implicit, safe.initPattern(prop, options)); - } - } - return needles; -} -function matchObjectPropertiesFn(propNeedles, ...objs) { - const safe = safeSelf(); - const matched = []; - for ( const obj of objs ) { - if ( obj instanceof Object === false ) { continue; } - for ( const [ prop, details ] of propNeedles ) { - let value = obj[prop]; - if ( value === undefined ) { continue; } - if ( typeof value !== 'string' ) { - try { value = safe.JSON_stringify(value); } - catch { } - if ( typeof value !== 'string' ) { continue; } - } - if ( safe.testPattern(details, value) === false ) { return; } - matched.push(`${prop}: ${value}`); - } - } - return matched; -} function safeSelf() { if ( scriptletGlobals.safeSelf ) { return scriptletGlobals.safeSelf; @@ -12604,6 +12396,332 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, + 'RegExp': self.RegExp, + 'RegExp_test': self.RegExp.prototype.test, + 'RegExp_exec': self.RegExp.prototype.exec, + 'Request_clone': self.Request.prototype.clone, + 'String': self.String, + 'String_fromCharCode': String.fromCharCode, + 'String_split': String.prototype.split, + 'XMLHttpRequest': self.XMLHttpRequest, + 'addEventListener': self.EventTarget.prototype.addEventListener, + 'removeEventListener': self.EventTarget.prototype.removeEventListener, + 'fetch': self.fetch, + 'JSON': self.JSON, + 'JSON_parseFn': self.JSON.parse, + 'JSON_stringifyFn': self.JSON.stringify, + 'JSON_parse': (...args) => safe.JSON_parseFn.call(safe.JSON, ...args), + 'JSON_stringify': (...args) => safe.JSON_stringifyFn.call(safe.JSON, ...args), + 'log': console.log.bind(console), + // Properties + logLevel: 0, + // Methods + makeLogPrefix(...args) { + return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; + }, + uboLog(...args) { + if ( this.sendToLogger === undefined ) { return; } + if ( args === undefined || args[0] === '' ) { return; } + return this.sendToLogger('info', ...args); + + }, + uboErr(...args) { + if ( this.sendToLogger === undefined ) { return; } + if ( args === undefined || args[0] === '' ) { return; } + return this.sendToLogger('error', ...args); + }, + escapeRegexChars(s) { + return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + }, + initPattern(pattern, options = {}) { + if ( pattern === '' ) { + return { matchAll: true, expect: true }; + } + const expect = (options.canNegate !== true || pattern.startsWith('!') === false); + if ( expect === false ) { + pattern = pattern.slice(1); + } + const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); + if ( match !== null ) { + return { + re: new this.RegExp( + match[1], + match[2] || options.flags + ), + expect, + }; + } + if ( options.flags !== undefined ) { + return { + re: new this.RegExp(this.escapeRegexChars(pattern), + options.flags + ), + expect, + }; + } + return { pattern, expect }; + }, + testPattern(details, haystack) { + if ( details.matchAll ) { return true; } + if ( details.re ) { + return this.RegExp_test.call(details.re, haystack) === details.expect; + } + return haystack.includes(details.pattern) === details.expect; + }, + patternToRegex(pattern, flags = undefined, verbatim = false) { + if ( pattern === '' ) { return /^/; } + const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); + if ( match === null ) { + const reStr = this.escapeRegexChars(pattern); + return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); + } + try { + return new RegExp(match[1], match[2] || undefined); + } + catch { + } + return /^/; + }, + getExtraArgs(args, offset = 0) { + const entries = args.slice(offset).reduce((out, v, i, a) => { + if ( (i & 1) === 0 ) { + const rawValue = a[i+1]; + const value = /^\d+$/.test(rawValue) + ? parseInt(rawValue, 10) + : rawValue; + out.push([ a[i], value ]); + } + return out; + }, []); + return this.Object_fromEntries(entries); + }, + onIdle(fn, options) { + if ( self.requestIdleCallback ) { + return self.requestIdleCallback(fn, options); + } + return self.requestAnimationFrame(fn); + }, + offIdle(id) { + if ( self.requestIdleCallback ) { + return self.cancelIdleCallback(id); + } + return self.cancelAnimationFrame(id); + } + }; + scriptletGlobals.safeSelf = safe; + if ( scriptletGlobals.bcSecret === undefined ) { return safe; } + // This is executed only when the logger is opened + safe.logLevel = scriptletGlobals.logLevel || 1; + let lastLogType = ''; + let lastLogText = ''; + let lastLogTime = 0; + safe.toLogText = (type, ...args) => { + if ( args.length === 0 ) { return; } + const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; + if ( text === lastLogText && type === lastLogType ) { + if ( (Date.now() - lastLogTime) < 5000 ) { return; } + } + lastLogType = type; + lastLogText = text; + lastLogTime = Date.now(); + return text; + }; + try { + const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); + let bcBuffer = []; + safe.sendToLogger = (type, ...args) => { + const text = safe.toLogText(type, ...args); + if ( text === undefined ) { return; } + if ( bcBuffer === undefined ) { + return bc.postMessage({ what: 'messageToLogger', type, text }); + } + bcBuffer.push({ type, text }); + }; + bc.onmessage = ev => { + const msg = ev.data; + switch ( msg ) { + case 'iamready!': + if ( bcBuffer === undefined ) { break; } + bcBuffer.forEach(({ type, text }) => + bc.postMessage({ what: 'messageToLogger', type, text }) + ); + bcBuffer = undefined; + break; + case 'setScriptletLogLevelToOne': + safe.logLevel = 1; + break; + case 'setScriptletLogLevelToTwo': + safe.logLevel = 2; + break; + } + }; + bc.postMessage('areyouready?'); + } catch { + safe.sendToLogger = (type, ...args) => { + const text = safe.toLogText(type, ...args); + if ( text === undefined ) { return; } + safe.log(`uBO ${text}`); + }; + } + return safe; +} +function collateFetchArgumentsFn(resource, options) { + const safe = safeSelf(); + const props = [ + 'body', 'cache', 'credentials', 'duplex', 'headers', + 'integrity', 'keepalive', 'method', 'mode', 'priority', + 'redirect', 'referrer', 'referrerPolicy', 'url' + ]; + const out = {}; + if ( collateFetchArgumentsFn.collateKnownProps === undefined ) { + collateFetchArgumentsFn.collateKnownProps = (src, out) => { + for ( const prop of props ) { + if ( src[prop] === undefined ) { continue; } + out[prop] = src[prop]; + } + }; + } + if ( + typeof resource !== 'object' || + safe.Object_toString.call(resource) !== '[object Request]' + ) { + out.url = `${resource}`; + } else { + let clone; + try { + clone = safe.Request_clone.call(resource); + } catch { + } + collateFetchArgumentsFn.collateKnownProps(clone || resource, out); + } + if ( typeof options === 'object' && options !== null ) { + collateFetchArgumentsFn.collateKnownProps(options, out); + } + return out; +} +function jsonEditFetchRequestFn(trusted, jsonq = '') { + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix( + `${trusted ? 'trusted-' : ''}json-edit-fetch-request`, + jsonq + ); + const jsonp = JSONPath.create(jsonq); + if ( jsonp.valid === false || jsonp.value !== undefined && trusted !== true ) { + return safe.uboLog(logPrefix, 'Bad JSONPath query'); + } + const extraArgs = safe.getExtraArgs(Array.from(arguments), 2); + const propNeedles = parsePropertiesToMatchFn(extraArgs.propsToMatch, 'url'); + const filterBody = body => { + if ( typeof body !== 'string' ) { return; } + let data; + try { data = safe.JSON_parse(body); } + catch { } + if ( data instanceof Object === false ) { return; } + const objAfter = jsonp.apply(data); + if ( objAfter === undefined ) { return; } + return safe.JSON_stringify(objAfter); + } + const proxyHandler = context => { + const args = context.callArgs; + const [ resource, options ] = args; + const bodyBefore = options?.body; + if ( Boolean(bodyBefore) === false ) { return context.reflect(); } + const bodyAfter = filterBody(bodyBefore); + if ( bodyAfter === undefined || bodyAfter === bodyBefore ) { + return context.reflect(); + } + if ( propNeedles.size !== 0 ) { + const props = collateFetchArgumentsFn(resource, options); + const matched = matchObjectPropertiesFn(propNeedles, props); + if ( matched === undefined ) { return context.reflect(); } + if ( safe.logLevel > 1 ) { + safe.uboLog(logPrefix, `Matched "propsToMatch":\n\t${matched.join('\n\t')}`); + } + } + safe.uboLog(logPrefix, 'Edited'); + if ( safe.logLevel > 1 ) { + safe.uboLog(logPrefix, `After edit:\n${bodyAfter}`); + } + options.body = bodyAfter; + return context.reflect(); + }; + proxyApplyFn('fetch', proxyHandler); + proxyApplyFn('Request', proxyHandler); +} +function trustedJsonEditFetchRequest(jsonq = '', ...args) { + jsonEditFetchRequestFn(true, jsonq, ...args); +}; +trustedJsonEditFetchRequest(...args); +}, +}; + + +scriptlets['jsonl-edit-xhr-response.js'] = { +aliases: [], + +requiresTrust: false, +func: function (scriptletGlobals = {}, ...args) { +function parsePropertiesToMatchFn(propsToMatch, implicit = '') { + const safe = safeSelf(); + const needles = new Map(); + if ( propsToMatch === undefined || propsToMatch === '' ) { return needles; } + const options = { canNegate: true }; + for ( const needle of safe.String_split.call(propsToMatch, /\s+/) ) { + let [ prop, pattern ] = safe.String_split.call(needle, ':'); + if ( prop === '' ) { continue; } + if ( pattern !== undefined && /[^$\w -]/.test(prop) ) { + prop = `${prop}:${pattern}`; + pattern = undefined; + } + if ( pattern !== undefined ) { + needles.set(prop, safe.initPattern(pattern, options)); + } else if ( implicit !== '' ) { + needles.set(implicit, safe.initPattern(prop, options)); + } + } + return needles; +} +function matchObjectPropertiesFn(propNeedles, ...objs) { + const safe = safeSelf(); + const matched = []; + for ( const obj of objs ) { + if ( obj instanceof Object === false ) { continue; } + for ( const [ prop, details ] of propNeedles ) { + let value = obj[prop]; + if ( value === undefined ) { continue; } + if ( typeof value !== 'string' ) { + try { value = safe.JSON_stringify(value); } + catch { } + if ( typeof value !== 'string' ) { continue; } + } + if ( safe.testPattern(details, value) === false ) { return; } + matched.push(`${prop}: ${value}`); + } + } + return matched; +} +function safeSelf() { + if ( scriptletGlobals.safeSelf ) { + return scriptletGlobals.safeSelf; + } + const self = globalThis; + const safe = { + 'Array_from': Array.from, + 'Error': self.Error, + 'Function_toStringFn': self.Function.prototype.toString, + 'Function_toString': thisArg => safe.Function_toStringFn.call(thisArg), + 'Math_floor': Math.floor, + 'Math_max': Math.max, + 'Math_min': Math.min, + 'Math_random': Math.random, + 'Object': Object, + 'Object_defineProperty': Object.defineProperty.bind(Object), + 'Object_defineProperties': Object.defineProperties.bind(Object), + 'Object_fromEntries': Object.fromEntries.bind(Object), + 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), + 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -13373,6 +13491,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -14206,195 +14325,6 @@ function matchObjectPropertiesFn(propNeedles, ...objs) { } return matched; } -function safeSelf() { - if ( scriptletGlobals.safeSelf ) { - return scriptletGlobals.safeSelf; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toStringFn': self.Function.prototype.toString, - 'Function_toString': thisArg => safe.Function_toStringFn.call(thisArg), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'RegExp': self.RegExp, - 'RegExp_test': self.RegExp.prototype.test, - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parseFn': self.JSON.parse, - 'JSON_stringifyFn': self.JSON.stringify, - 'JSON_parse': (...args) => safe.JSON_parseFn.call(safe.JSON, ...args), - 'JSON_stringify': (...args) => safe.JSON_stringifyFn.call(safe.JSON, ...args), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test.call(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - getExtraArgs(args, offset = 0) { - const entries = args.slice(offset).reduce((out, v, i, a) => { - if ( (i & 1) === 0 ) { - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - } - return out; - }, []); - return this.Object_fromEntries(entries); - }, - onIdle(fn, options) { - if ( self.requestIdleCallback ) { - return self.requestIdleCallback(fn, options); - } - return self.requestAnimationFrame(fn); - }, - offIdle(id) { - if ( self.requestIdleCallback ) { - return self.cancelIdleCallback(id); - } - return self.cancelAnimationFrame(id); - } - }; - scriptletGlobals.safeSelf = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} function jsonlEditFn(jsonp, text = '') { const safe = safeSelf(); const lineSeparator = /\r?\n/.exec(text)?.[0] || '\n'; @@ -14864,6 +14794,230 @@ class JSONPath { } } } +function safeSelf() { + if ( scriptletGlobals.safeSelf ) { + return scriptletGlobals.safeSelf; + } + const self = globalThis; + const safe = { + 'Array_from': Array.from, + 'Error': self.Error, + 'Function_toStringFn': self.Function.prototype.toString, + 'Function_toString': thisArg => safe.Function_toStringFn.call(thisArg), + 'Math_floor': Math.floor, + 'Math_max': Math.max, + 'Math_min': Math.min, + 'Math_random': Math.random, + 'Object': Object, + 'Object_defineProperty': Object.defineProperty.bind(Object), + 'Object_defineProperties': Object.defineProperties.bind(Object), + 'Object_fromEntries': Object.fromEntries.bind(Object), + 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), + 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, + 'RegExp': self.RegExp, + 'RegExp_test': self.RegExp.prototype.test, + 'RegExp_exec': self.RegExp.prototype.exec, + 'Request_clone': self.Request.prototype.clone, + 'String': self.String, + 'String_fromCharCode': String.fromCharCode, + 'String_split': String.prototype.split, + 'XMLHttpRequest': self.XMLHttpRequest, + 'addEventListener': self.EventTarget.prototype.addEventListener, + 'removeEventListener': self.EventTarget.prototype.removeEventListener, + 'fetch': self.fetch, + 'JSON': self.JSON, + 'JSON_parseFn': self.JSON.parse, + 'JSON_stringifyFn': self.JSON.stringify, + 'JSON_parse': (...args) => safe.JSON_parseFn.call(safe.JSON, ...args), + 'JSON_stringify': (...args) => safe.JSON_stringifyFn.call(safe.JSON, ...args), + 'log': console.log.bind(console), + // Properties + logLevel: 0, + // Methods + makeLogPrefix(...args) { + return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; + }, + uboLog(...args) { + if ( this.sendToLogger === undefined ) { return; } + if ( args === undefined || args[0] === '' ) { return; } + return this.sendToLogger('info', ...args); + + }, + uboErr(...args) { + if ( this.sendToLogger === undefined ) { return; } + if ( args === undefined || args[0] === '' ) { return; } + return this.sendToLogger('error', ...args); + }, + escapeRegexChars(s) { + return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + }, + initPattern(pattern, options = {}) { + if ( pattern === '' ) { + return { matchAll: true, expect: true }; + } + const expect = (options.canNegate !== true || pattern.startsWith('!') === false); + if ( expect === false ) { + pattern = pattern.slice(1); + } + const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); + if ( match !== null ) { + return { + re: new this.RegExp( + match[1], + match[2] || options.flags + ), + expect, + }; + } + if ( options.flags !== undefined ) { + return { + re: new this.RegExp(this.escapeRegexChars(pattern), + options.flags + ), + expect, + }; + } + return { pattern, expect }; + }, + testPattern(details, haystack) { + if ( details.matchAll ) { return true; } + if ( details.re ) { + return this.RegExp_test.call(details.re, haystack) === details.expect; + } + return haystack.includes(details.pattern) === details.expect; + }, + patternToRegex(pattern, flags = undefined, verbatim = false) { + if ( pattern === '' ) { return /^/; } + const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); + if ( match === null ) { + const reStr = this.escapeRegexChars(pattern); + return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); + } + try { + return new RegExp(match[1], match[2] || undefined); + } + catch { + } + return /^/; + }, + getExtraArgs(args, offset = 0) { + const entries = args.slice(offset).reduce((out, v, i, a) => { + if ( (i & 1) === 0 ) { + const rawValue = a[i+1]; + const value = /^\d+$/.test(rawValue) + ? parseInt(rawValue, 10) + : rawValue; + out.push([ a[i], value ]); + } + return out; + }, []); + return this.Object_fromEntries(entries); + }, + onIdle(fn, options) { + if ( self.requestIdleCallback ) { + return self.requestIdleCallback(fn, options); + } + return self.requestAnimationFrame(fn); + }, + offIdle(id) { + if ( self.requestIdleCallback ) { + return self.cancelIdleCallback(id); + } + return self.cancelAnimationFrame(id); + } + }; + scriptletGlobals.safeSelf = safe; + if ( scriptletGlobals.bcSecret === undefined ) { return safe; } + // This is executed only when the logger is opened + safe.logLevel = scriptletGlobals.logLevel || 1; + let lastLogType = ''; + let lastLogText = ''; + let lastLogTime = 0; + safe.toLogText = (type, ...args) => { + if ( args.length === 0 ) { return; } + const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; + if ( text === lastLogText && type === lastLogType ) { + if ( (Date.now() - lastLogTime) < 5000 ) { return; } + } + lastLogType = type; + lastLogText = text; + lastLogTime = Date.now(); + return text; + }; + try { + const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); + let bcBuffer = []; + safe.sendToLogger = (type, ...args) => { + const text = safe.toLogText(type, ...args); + if ( text === undefined ) { return; } + if ( bcBuffer === undefined ) { + return bc.postMessage({ what: 'messageToLogger', type, text }); + } + bcBuffer.push({ type, text }); + }; + bc.onmessage = ev => { + const msg = ev.data; + switch ( msg ) { + case 'iamready!': + if ( bcBuffer === undefined ) { break; } + bcBuffer.forEach(({ type, text }) => + bc.postMessage({ what: 'messageToLogger', type, text }) + ); + bcBuffer = undefined; + break; + case 'setScriptletLogLevelToOne': + safe.logLevel = 1; + break; + case 'setScriptletLogLevelToTwo': + safe.logLevel = 2; + break; + } + }; + bc.postMessage('areyouready?'); + } catch { + safe.sendToLogger = (type, ...args) => { + const text = safe.toLogText(type, ...args); + if ( text === undefined ) { return; } + safe.log(`uBO ${text}`); + }; + } + return safe; +} +function collateFetchArgumentsFn(resource, options) { + const safe = safeSelf(); + const props = [ + 'body', 'cache', 'credentials', 'duplex', 'headers', + 'integrity', 'keepalive', 'method', 'mode', 'priority', + 'redirect', 'referrer', 'referrerPolicy', 'url' + ]; + const out = {}; + if ( collateFetchArgumentsFn.collateKnownProps === undefined ) { + collateFetchArgumentsFn.collateKnownProps = (src, out) => { + for ( const prop of props ) { + if ( src[prop] === undefined ) { continue; } + out[prop] = src[prop]; + } + }; + } + if ( + typeof resource !== 'object' || + safe.Object_toString.call(resource) !== '[object Request]' + ) { + out.url = `${resource}`; + } else { + let clone; + try { + clone = safe.Request_clone.call(resource); + } catch { + } + collateFetchArgumentsFn.collateKnownProps(clone || resource, out); + } + if ( typeof options === 'object' && options !== null ) { + collateFetchArgumentsFn.collateKnownProps(options, out); + } + return out; +} function jsonlEditFetchResponseFn(trusted, jsonq = '') { const safe = safeSelf(); const logPrefix = safe.makeLogPrefix( @@ -14881,18 +15035,8 @@ function jsonlEditFetchResponseFn(trusted, jsonq = '') { const args = context.callArgs; const fetchPromise = context.reflect(); if ( propNeedles.size !== 0 ) { - const objs = [ args[0] instanceof Object ? args[0] : { url: args[0] } ]; - if ( objs[0] instanceof Request ) { - try { - objs[0] = safe.Request_clone.call(objs[0]); - } catch(ex) { - safe.uboErr(logPrefix, 'Error:', ex); - } - } - if ( args[1] instanceof Object ) { - objs.push(args[1]); - } - const matched = matchObjectPropertiesFn(propNeedles, ...objs); + const props = collateFetchArgumentsFn(...args); + const matched = matchObjectPropertiesFn(propNeedles, props); if ( matched === undefined ) { return fetchPromise; } if ( safe.logLevel > 1 ) { safe.uboLog(logPrefix, `Matched "propsToMatch":\n\t${matched.join('\n\t')}`); @@ -15067,195 +15211,6 @@ function matchObjectPropertiesFn(propNeedles, ...objs) { } return matched; } -function safeSelf() { - if ( scriptletGlobals.safeSelf ) { - return scriptletGlobals.safeSelf; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toStringFn': self.Function.prototype.toString, - 'Function_toString': thisArg => safe.Function_toStringFn.call(thisArg), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'RegExp': self.RegExp, - 'RegExp_test': self.RegExp.prototype.test, - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parseFn': self.JSON.parse, - 'JSON_stringifyFn': self.JSON.stringify, - 'JSON_parse': (...args) => safe.JSON_parseFn.call(safe.JSON, ...args), - 'JSON_stringify': (...args) => safe.JSON_stringifyFn.call(safe.JSON, ...args), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test.call(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - getExtraArgs(args, offset = 0) { - const entries = args.slice(offset).reduce((out, v, i, a) => { - if ( (i & 1) === 0 ) { - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - } - return out; - }, []); - return this.Object_fromEntries(entries); - }, - onIdle(fn, options) { - if ( self.requestIdleCallback ) { - return self.requestIdleCallback(fn, options); - } - return self.requestAnimationFrame(fn); - }, - offIdle(id) { - if ( self.requestIdleCallback ) { - return self.cancelIdleCallback(id); - } - return self.cancelAnimationFrame(id); - } - }; - scriptletGlobals.safeSelf = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} function jsonlEditFn(jsonp, text = '') { const safe = safeSelf(); const lineSeparator = /\r?\n/.exec(text)?.[0] || '\n'; @@ -15725,130 +15680,6 @@ class JSONPath { } } } -function jsonlEditFetchResponseFn(trusted, jsonq = '') { - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix( - `${trusted ? 'trusted-' : ''}jsonl-edit-fetch-response`, - jsonq - ); - const jsonp = JSONPath.create(jsonq); - if ( jsonp.valid === false || jsonp.value !== undefined && trusted !== true ) { - return safe.uboLog(logPrefix, 'Bad JSONPath query'); - } - const extraArgs = safe.getExtraArgs(Array.from(arguments), 2); - const propNeedles = parsePropertiesToMatchFn(extraArgs.propsToMatch, 'url'); - const logall = jsonq === ''; - proxyApplyFn('fetch', function(context) { - const args = context.callArgs; - const fetchPromise = context.reflect(); - if ( propNeedles.size !== 0 ) { - const objs = [ args[0] instanceof Object ? args[0] : { url: args[0] } ]; - if ( objs[0] instanceof Request ) { - try { - objs[0] = safe.Request_clone.call(objs[0]); - } catch(ex) { - safe.uboErr(logPrefix, 'Error:', ex); - } - } - if ( args[1] instanceof Object ) { - objs.push(args[1]); - } - const matched = matchObjectPropertiesFn(propNeedles, ...objs); - if ( matched === undefined ) { return fetchPromise; } - if ( safe.logLevel > 1 ) { - safe.uboLog(logPrefix, `Matched "propsToMatch":\n\t${matched.join('\n\t')}`); - } - } - return fetchPromise.then(responseBefore => { - const response = responseBefore.clone(); - return response.text().then(textBefore => { - if ( typeof textBefore !== 'string' ) { return textBefore; } - if ( logall ) { - safe.uboLog(logPrefix, textBefore); - return responseBefore; - } - const textAfter = jsonlEditFn(jsonp, textBefore); - if ( textAfter === textBefore ) { return responseBefore; } - safe.uboLog(logPrefix, 'Pruned'); - const responseAfter = new Response(textAfter, { - status: responseBefore.status, - statusText: responseBefore.statusText, - headers: responseBefore.headers, - }); - Object.defineProperties(responseAfter, { - ok: { value: responseBefore.ok }, - redirected: { value: responseBefore.redirected }, - type: { value: responseBefore.type }, - url: { value: responseBefore.url }, - }); - return responseAfter; - }).catch(reason => { - safe.uboErr(logPrefix, 'Error:', reason); - return responseBefore; - }); - }).catch(reason => { - safe.uboErr(logPrefix, 'Error:', reason); - return fetchPromise; - }); - }); -} -function trustedJsonlEditFetchResponse(jsonq = '', ...args) { - jsonlEditFetchResponseFn(true, jsonq, ...args); -}; -trustedJsonlEditFetchResponse(...args); -}, -}; - - -scriptlets['abort-on-stack-trace.js'] = { -aliases: ["aost.js"], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function matchesStackTraceFn( - needleDetails, - logLevel = '' -) { - const safe = safeSelf(); - const exceptionToken = getExceptionTokenFn(); - const error = new safe.Error(exceptionToken); - const docURL = new URL(self.location.href); - docURL.hash = ''; - // Normalize stack trace - const reLine = /(.*?@)?(\S+)(:\d+):\d+\)?$/; - const lines = []; - for ( let line of safe.String_split.call(error.stack, /[\n\r]+/) ) { - if ( line.includes(exceptionToken) ) { continue; } - line = line.trim(); - const match = safe.RegExp_exec.call(reLine, line); - if ( match === null ) { continue; } - let url = match[2]; - if ( url.startsWith('(') ) { url = url.slice(1); } - if ( url === docURL.href ) { - url = 'inlineScript'; - } else if ( url.startsWith('') ) { - url = 'injectedScript'; - } - let fn = match[1] !== undefined - ? match[1].slice(0, -1) - : line.slice(0, match.index).trim(); - if ( fn.startsWith('at') ) { fn = fn.slice(2).trim(); } - let rowcol = match[3]; - lines.push(' ' + `${fn} ${url}${rowcol}:1`.trim()); - } - lines[0] = `stackDepth:${lines.length-1}`; - const stack = lines.join('\t'); - const r = needleDetails.matchAll !== true && - safe.testPattern(needleDetails, stack); - if ( - logLevel === 'all' || - logLevel === 'match' && r || - logLevel === 'nomatch' && !r - ) { - safe.uboLog(stack.replace(/\t/g, '\n')); - } - return r; -} function safeSelf() { if ( scriptletGlobals.safeSelf ) { return scriptletGlobals.safeSelf; @@ -15869,6 +15700,345 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, + 'RegExp': self.RegExp, + 'RegExp_test': self.RegExp.prototype.test, + 'RegExp_exec': self.RegExp.prototype.exec, + 'Request_clone': self.Request.prototype.clone, + 'String': self.String, + 'String_fromCharCode': String.fromCharCode, + 'String_split': String.prototype.split, + 'XMLHttpRequest': self.XMLHttpRequest, + 'addEventListener': self.EventTarget.prototype.addEventListener, + 'removeEventListener': self.EventTarget.prototype.removeEventListener, + 'fetch': self.fetch, + 'JSON': self.JSON, + 'JSON_parseFn': self.JSON.parse, + 'JSON_stringifyFn': self.JSON.stringify, + 'JSON_parse': (...args) => safe.JSON_parseFn.call(safe.JSON, ...args), + 'JSON_stringify': (...args) => safe.JSON_stringifyFn.call(safe.JSON, ...args), + 'log': console.log.bind(console), + // Properties + logLevel: 0, + // Methods + makeLogPrefix(...args) { + return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; + }, + uboLog(...args) { + if ( this.sendToLogger === undefined ) { return; } + if ( args === undefined || args[0] === '' ) { return; } + return this.sendToLogger('info', ...args); + + }, + uboErr(...args) { + if ( this.sendToLogger === undefined ) { return; } + if ( args === undefined || args[0] === '' ) { return; } + return this.sendToLogger('error', ...args); + }, + escapeRegexChars(s) { + return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + }, + initPattern(pattern, options = {}) { + if ( pattern === '' ) { + return { matchAll: true, expect: true }; + } + const expect = (options.canNegate !== true || pattern.startsWith('!') === false); + if ( expect === false ) { + pattern = pattern.slice(1); + } + const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); + if ( match !== null ) { + return { + re: new this.RegExp( + match[1], + match[2] || options.flags + ), + expect, + }; + } + if ( options.flags !== undefined ) { + return { + re: new this.RegExp(this.escapeRegexChars(pattern), + options.flags + ), + expect, + }; + } + return { pattern, expect }; + }, + testPattern(details, haystack) { + if ( details.matchAll ) { return true; } + if ( details.re ) { + return this.RegExp_test.call(details.re, haystack) === details.expect; + } + return haystack.includes(details.pattern) === details.expect; + }, + patternToRegex(pattern, flags = undefined, verbatim = false) { + if ( pattern === '' ) { return /^/; } + const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); + if ( match === null ) { + const reStr = this.escapeRegexChars(pattern); + return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); + } + try { + return new RegExp(match[1], match[2] || undefined); + } + catch { + } + return /^/; + }, + getExtraArgs(args, offset = 0) { + const entries = args.slice(offset).reduce((out, v, i, a) => { + if ( (i & 1) === 0 ) { + const rawValue = a[i+1]; + const value = /^\d+$/.test(rawValue) + ? parseInt(rawValue, 10) + : rawValue; + out.push([ a[i], value ]); + } + return out; + }, []); + return this.Object_fromEntries(entries); + }, + onIdle(fn, options) { + if ( self.requestIdleCallback ) { + return self.requestIdleCallback(fn, options); + } + return self.requestAnimationFrame(fn); + }, + offIdle(id) { + if ( self.requestIdleCallback ) { + return self.cancelIdleCallback(id); + } + return self.cancelAnimationFrame(id); + } + }; + scriptletGlobals.safeSelf = safe; + if ( scriptletGlobals.bcSecret === undefined ) { return safe; } + // This is executed only when the logger is opened + safe.logLevel = scriptletGlobals.logLevel || 1; + let lastLogType = ''; + let lastLogText = ''; + let lastLogTime = 0; + safe.toLogText = (type, ...args) => { + if ( args.length === 0 ) { return; } + const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; + if ( text === lastLogText && type === lastLogType ) { + if ( (Date.now() - lastLogTime) < 5000 ) { return; } + } + lastLogType = type; + lastLogText = text; + lastLogTime = Date.now(); + return text; + }; + try { + const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); + let bcBuffer = []; + safe.sendToLogger = (type, ...args) => { + const text = safe.toLogText(type, ...args); + if ( text === undefined ) { return; } + if ( bcBuffer === undefined ) { + return bc.postMessage({ what: 'messageToLogger', type, text }); + } + bcBuffer.push({ type, text }); + }; + bc.onmessage = ev => { + const msg = ev.data; + switch ( msg ) { + case 'iamready!': + if ( bcBuffer === undefined ) { break; } + bcBuffer.forEach(({ type, text }) => + bc.postMessage({ what: 'messageToLogger', type, text }) + ); + bcBuffer = undefined; + break; + case 'setScriptletLogLevelToOne': + safe.logLevel = 1; + break; + case 'setScriptletLogLevelToTwo': + safe.logLevel = 2; + break; + } + }; + bc.postMessage('areyouready?'); + } catch { + safe.sendToLogger = (type, ...args) => { + const text = safe.toLogText(type, ...args); + if ( text === undefined ) { return; } + safe.log(`uBO ${text}`); + }; + } + return safe; +} +function collateFetchArgumentsFn(resource, options) { + const safe = safeSelf(); + const props = [ + 'body', 'cache', 'credentials', 'duplex', 'headers', + 'integrity', 'keepalive', 'method', 'mode', 'priority', + 'redirect', 'referrer', 'referrerPolicy', 'url' + ]; + const out = {}; + if ( collateFetchArgumentsFn.collateKnownProps === undefined ) { + collateFetchArgumentsFn.collateKnownProps = (src, out) => { + for ( const prop of props ) { + if ( src[prop] === undefined ) { continue; } + out[prop] = src[prop]; + } + }; + } + if ( + typeof resource !== 'object' || + safe.Object_toString.call(resource) !== '[object Request]' + ) { + out.url = `${resource}`; + } else { + let clone; + try { + clone = safe.Request_clone.call(resource); + } catch { + } + collateFetchArgumentsFn.collateKnownProps(clone || resource, out); + } + if ( typeof options === 'object' && options !== null ) { + collateFetchArgumentsFn.collateKnownProps(options, out); + } + return out; +} +function jsonlEditFetchResponseFn(trusted, jsonq = '') { + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix( + `${trusted ? 'trusted-' : ''}jsonl-edit-fetch-response`, + jsonq + ); + const jsonp = JSONPath.create(jsonq); + if ( jsonp.valid === false || jsonp.value !== undefined && trusted !== true ) { + return safe.uboLog(logPrefix, 'Bad JSONPath query'); + } + const extraArgs = safe.getExtraArgs(Array.from(arguments), 2); + const propNeedles = parsePropertiesToMatchFn(extraArgs.propsToMatch, 'url'); + const logall = jsonq === ''; + proxyApplyFn('fetch', function(context) { + const args = context.callArgs; + const fetchPromise = context.reflect(); + if ( propNeedles.size !== 0 ) { + const props = collateFetchArgumentsFn(...args); + const matched = matchObjectPropertiesFn(propNeedles, props); + if ( matched === undefined ) { return fetchPromise; } + if ( safe.logLevel > 1 ) { + safe.uboLog(logPrefix, `Matched "propsToMatch":\n\t${matched.join('\n\t')}`); + } + } + return fetchPromise.then(responseBefore => { + const response = responseBefore.clone(); + return response.text().then(textBefore => { + if ( typeof textBefore !== 'string' ) { return textBefore; } + if ( logall ) { + safe.uboLog(logPrefix, textBefore); + return responseBefore; + } + const textAfter = jsonlEditFn(jsonp, textBefore); + if ( textAfter === textBefore ) { return responseBefore; } + safe.uboLog(logPrefix, 'Pruned'); + const responseAfter = new Response(textAfter, { + status: responseBefore.status, + statusText: responseBefore.statusText, + headers: responseBefore.headers, + }); + Object.defineProperties(responseAfter, { + ok: { value: responseBefore.ok }, + redirected: { value: responseBefore.redirected }, + type: { value: responseBefore.type }, + url: { value: responseBefore.url }, + }); + return responseAfter; + }).catch(reason => { + safe.uboErr(logPrefix, 'Error:', reason); + return responseBefore; + }); + }).catch(reason => { + safe.uboErr(logPrefix, 'Error:', reason); + return fetchPromise; + }); + }); +} +function trustedJsonlEditFetchResponse(jsonq = '', ...args) { + jsonlEditFetchResponseFn(true, jsonq, ...args); +}; +trustedJsonlEditFetchResponse(...args); +}, +}; + + +scriptlets['abort-on-stack-trace.js'] = { +aliases: ["aost.js"], + +requiresTrust: false, +func: function (scriptletGlobals = {}, ...args) { +function matchesStackTraceFn( + needleDetails, + logLevel = '' +) { + const safe = safeSelf(); + const exceptionToken = getExceptionTokenFn(); + const error = new safe.Error(exceptionToken); + const docURL = new URL(self.location.href); + docURL.hash = ''; + // Normalize stack trace + const reLine = /(.*?@)?(\S+)(:\d+):\d+\)?$/; + const lines = []; + for ( let line of safe.String_split.call(error.stack, /[\n\r]+/) ) { + if ( line.includes(exceptionToken) ) { continue; } + line = line.trim(); + const match = safe.RegExp_exec.call(reLine, line); + if ( match === null ) { continue; } + let url = match[2]; + if ( url.startsWith('(') ) { url = url.slice(1); } + if ( url === docURL.href ) { + url = 'inlineScript'; + } else if ( url.startsWith('') ) { + url = 'injectedScript'; + } + let fn = match[1] !== undefined + ? match[1].slice(0, -1) + : line.slice(0, match.index).trim(); + if ( fn.startsWith('at') ) { fn = fn.slice(2).trim(); } + let rowcol = match[3]; + lines.push(' ' + `${fn} ${url}${rowcol}:1`.trim()); + } + lines[0] = `stackDepth:${lines.length-1}`; + const stack = lines.join('\t'); + const r = needleDetails.matchAll !== true && + safe.testPattern(needleDetails, stack); + if ( + logLevel === 'all' || + logLevel === 'match' && r || + logLevel === 'nomatch' && !r + ) { + safe.uboLog(stack.replace(/\t/g, '\n')); + } + return r; +} +function safeSelf() { + if ( scriptletGlobals.safeSelf ) { + return scriptletGlobals.safeSelf; + } + const self = globalThis; + const safe = { + 'Array_from': Array.from, + 'Error': self.Error, + 'Function_toStringFn': self.Function.prototype.toString, + 'Function_toString': thisArg => safe.Function_toStringFn.call(thisArg), + 'Math_floor': Math.floor, + 'Math_max': Math.max, + 'Math_min': Math.min, + 'Math_random': Math.random, + 'Object': Object, + 'Object_defineProperty': Object.defineProperty.bind(Object), + 'Object_defineProperties': Object.defineProperties.bind(Object), + 'Object_fromEntries': Object.fromEntries.bind(Object), + 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), + 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -16238,6 +16408,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -16716,6 +16887,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -17101,6 +17273,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -17597,6 +17770,25 @@ function objectPruneFn( } if ( outcome === 'match' ) { return obj; } } +function matchObjectPropertiesFn(propNeedles, ...objs) { + const safe = safeSelf(); + const matched = []; + for ( const obj of objs ) { + if ( obj instanceof Object === false ) { continue; } + for ( const [ prop, details ] of propNeedles ) { + let value = obj[prop]; + if ( value === undefined ) { continue; } + if ( typeof value !== 'string' ) { + try { value = safe.JSON_stringify(value); } + catch { } + if ( typeof value !== 'string' ) { continue; } + } + if ( safe.testPattern(details, value) === false ) { return; } + matched.push(`${prop}: ${value}`); + } + } + return matched; +} function safeSelf() { if ( scriptletGlobals.safeSelf ) { return scriptletGlobals.safeSelf; @@ -17617,6 +17809,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -17786,24 +17979,39 @@ function safeSelf() { } return safe; } -function matchObjectPropertiesFn(propNeedles, ...objs) { +function collateFetchArgumentsFn(resource, options) { const safe = safeSelf(); - const matched = []; - for ( const obj of objs ) { - if ( obj instanceof Object === false ) { continue; } - for ( const [ prop, details ] of propNeedles ) { - let value = obj[prop]; - if ( value === undefined ) { continue; } - if ( typeof value !== 'string' ) { - try { value = safe.JSON_stringify(value); } - catch { } - if ( typeof value !== 'string' ) { continue; } + const props = [ + 'body', 'cache', 'credentials', 'duplex', 'headers', + 'integrity', 'keepalive', 'method', 'mode', 'priority', + 'redirect', 'referrer', 'referrerPolicy', 'url' + ]; + const out = {}; + if ( collateFetchArgumentsFn.collateKnownProps === undefined ) { + collateFetchArgumentsFn.collateKnownProps = (src, out) => { + for ( const prop of props ) { + if ( src[prop] === undefined ) { continue; } + out[prop] = src[prop]; } - if ( safe.testPattern(details, value) === false ) { return; } - matched.push(`${prop}: ${value}`); + }; + } + if ( + typeof resource !== 'object' || + safe.Object_toString.call(resource) !== '[object Request]' + ) { + out.url = `${resource}`; + } else { + let clone; + try { + clone = safe.Request_clone.call(resource); + } catch { } + collateFetchArgumentsFn.collateKnownProps(clone || resource, out); } - return matched; + if ( typeof options === 'object' && options !== null ) { + collateFetchArgumentsFn.collateKnownProps(options, out); + } + return out; } function jsonPruneFetchResponse( rawPrunePaths = '', @@ -17818,18 +18026,8 @@ function jsonPruneFetchResponse( const applyHandler = function(target, thisArg, args) { const fetchPromise = Reflect.apply(target, thisArg, args); if ( propNeedles.size !== 0 ) { - const objs = [ args[0] instanceof Object ? args[0] : { url: args[0] } ]; - if ( objs[0] instanceof Request ) { - try { - objs[0] = safe.Request_clone.call(objs[0]); - } catch(ex) { - safe.uboErr(logPrefix, 'Error:', ex); - } - } - if ( args[1] instanceof Object ) { - objs.push(args[1]); - } - const matched = matchObjectPropertiesFn(propNeedles, ...objs); + const props = collateFetchArgumentsFn(...args); + const matched = matchObjectPropertiesFn(propNeedles, props); if ( matched === undefined ) { return fetchPromise; } if ( safe.logLevel > 1 ) { safe.uboLog(logPrefix, `Matched "propsToMatch":\n\t${matched.join('\n\t')}`); @@ -18092,6 +18290,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -18539,6 +18738,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -18851,6 +19051,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -19129,11 +19330,229 @@ noEvalIf(...args); }; -scriptlets['prevent-fetch.js'] = { -aliases: ["no-fetch-if.js"], +scriptlets['prevent-addEventListener.js'] = { +aliases: ["addEventListener-defuser.js","aeld.js"], requiresTrust: false, func: function (scriptletGlobals = {}, ...args) { +function safeSelf() { + if ( scriptletGlobals.safeSelf ) { + return scriptletGlobals.safeSelf; + } + const self = globalThis; + const safe = { + 'Array_from': Array.from, + 'Error': self.Error, + 'Function_toStringFn': self.Function.prototype.toString, + 'Function_toString': thisArg => safe.Function_toStringFn.call(thisArg), + 'Math_floor': Math.floor, + 'Math_max': Math.max, + 'Math_min': Math.min, + 'Math_random': Math.random, + 'Object': Object, + 'Object_defineProperty': Object.defineProperty.bind(Object), + 'Object_defineProperties': Object.defineProperties.bind(Object), + 'Object_fromEntries': Object.fromEntries.bind(Object), + 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), + 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, + 'RegExp': self.RegExp, + 'RegExp_test': self.RegExp.prototype.test, + 'RegExp_exec': self.RegExp.prototype.exec, + 'Request_clone': self.Request.prototype.clone, + 'String': self.String, + 'String_fromCharCode': String.fromCharCode, + 'String_split': String.prototype.split, + 'XMLHttpRequest': self.XMLHttpRequest, + 'addEventListener': self.EventTarget.prototype.addEventListener, + 'removeEventListener': self.EventTarget.prototype.removeEventListener, + 'fetch': self.fetch, + 'JSON': self.JSON, + 'JSON_parseFn': self.JSON.parse, + 'JSON_stringifyFn': self.JSON.stringify, + 'JSON_parse': (...args) => safe.JSON_parseFn.call(safe.JSON, ...args), + 'JSON_stringify': (...args) => safe.JSON_stringifyFn.call(safe.JSON, ...args), + 'log': console.log.bind(console), + // Properties + logLevel: 0, + // Methods + makeLogPrefix(...args) { + return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; + }, + uboLog(...args) { + if ( this.sendToLogger === undefined ) { return; } + if ( args === undefined || args[0] === '' ) { return; } + return this.sendToLogger('info', ...args); + + }, + uboErr(...args) { + if ( this.sendToLogger === undefined ) { return; } + if ( args === undefined || args[0] === '' ) { return; } + return this.sendToLogger('error', ...args); + }, + escapeRegexChars(s) { + return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + }, + initPattern(pattern, options = {}) { + if ( pattern === '' ) { + return { matchAll: true, expect: true }; + } + const expect = (options.canNegate !== true || pattern.startsWith('!') === false); + if ( expect === false ) { + pattern = pattern.slice(1); + } + const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); + if ( match !== null ) { + return { + re: new this.RegExp( + match[1], + match[2] || options.flags + ), + expect, + }; + } + if ( options.flags !== undefined ) { + return { + re: new this.RegExp(this.escapeRegexChars(pattern), + options.flags + ), + expect, + }; + } + return { pattern, expect }; + }, + testPattern(details, haystack) { + if ( details.matchAll ) { return true; } + if ( details.re ) { + return this.RegExp_test.call(details.re, haystack) === details.expect; + } + return haystack.includes(details.pattern) === details.expect; + }, + patternToRegex(pattern, flags = undefined, verbatim = false) { + if ( pattern === '' ) { return /^/; } + const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); + if ( match === null ) { + const reStr = this.escapeRegexChars(pattern); + return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); + } + try { + return new RegExp(match[1], match[2] || undefined); + } + catch { + } + return /^/; + }, + getExtraArgs(args, offset = 0) { + const entries = args.slice(offset).reduce((out, v, i, a) => { + if ( (i & 1) === 0 ) { + const rawValue = a[i+1]; + const value = /^\d+$/.test(rawValue) + ? parseInt(rawValue, 10) + : rawValue; + out.push([ a[i], value ]); + } + return out; + }, []); + return this.Object_fromEntries(entries); + }, + onIdle(fn, options) { + if ( self.requestIdleCallback ) { + return self.requestIdleCallback(fn, options); + } + return self.requestAnimationFrame(fn); + }, + offIdle(id) { + if ( self.requestIdleCallback ) { + return self.cancelIdleCallback(id); + } + return self.cancelAnimationFrame(id); + } + }; + scriptletGlobals.safeSelf = safe; + if ( scriptletGlobals.bcSecret === undefined ) { return safe; } + // This is executed only when the logger is opened + safe.logLevel = scriptletGlobals.logLevel || 1; + let lastLogType = ''; + let lastLogText = ''; + let lastLogTime = 0; + safe.toLogText = (type, ...args) => { + if ( args.length === 0 ) { return; } + const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; + if ( text === lastLogText && type === lastLogType ) { + if ( (Date.now() - lastLogTime) < 5000 ) { return; } + } + lastLogType = type; + lastLogText = text; + lastLogTime = Date.now(); + return text; + }; + try { + const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); + let bcBuffer = []; + safe.sendToLogger = (type, ...args) => { + const text = safe.toLogText(type, ...args); + if ( text === undefined ) { return; } + if ( bcBuffer === undefined ) { + return bc.postMessage({ what: 'messageToLogger', type, text }); + } + bcBuffer.push({ type, text }); + }; + bc.onmessage = ev => { + const msg = ev.data; + switch ( msg ) { + case 'iamready!': + if ( bcBuffer === undefined ) { break; } + bcBuffer.forEach(({ type, text }) => + bc.postMessage({ what: 'messageToLogger', type, text }) + ); + bcBuffer = undefined; + break; + case 'setScriptletLogLevelToOne': + safe.logLevel = 1; + break; + case 'setScriptletLogLevelToTwo': + safe.logLevel = 2; + break; + } + }; + bc.postMessage('areyouready?'); + } catch { + safe.sendToLogger = (type, ...args) => { + const text = safe.toLogText(type, ...args); + if ( text === undefined ) { return; } + safe.log(`uBO ${text}`); + }; + } + return safe; +} +function runAt(fn, when) { + const intFromReadyState = state => { + const targets = { + 'loading': 1, 'asap': 1, + 'interactive': 2, 'end': 2, '2': 2, + 'complete': 3, 'idle': 3, '3': 3, + }; + const tokens = Array.isArray(state) ? state : [ state ]; + for ( const token of tokens ) { + const prop = `${token}`; + if ( Object.hasOwn(targets, prop) === false ) { continue; } + return targets[prop]; + } + return 0; + }; + const runAt = intFromReadyState(when); + if ( intFromReadyState(document.readyState) >= runAt ) { + fn(); return; + } + const onStateChange = ( ) => { + if ( intFromReadyState(document.readyState) < runAt ) { return; } + fn(); + safe.removeEventListener.apply(document, args); + }; + const safe = safeSelf(); + const args = [ 'readystatechange', onStateChange, { capture: true } ]; + safe.addEventListener.apply(document, args); +} function proxyApplyFn( target = '', handler = '' @@ -19218,45 +19637,107 @@ function proxyApplyFn( } context[prop] = new Proxy(fn, proxyDetails); } -function parsePropertiesToMatchFn(propsToMatch, implicit = '') { +function preventAddEventListener( + type = '', + pattern = '' +) { const safe = safeSelf(); - const needles = new Map(); - if ( propsToMatch === undefined || propsToMatch === '' ) { return needles; } - const options = { canNegate: true }; - for ( const needle of safe.String_split.call(propsToMatch, /\s+/) ) { - let [ prop, pattern ] = safe.String_split.call(needle, ':'); - if ( prop === '' ) { continue; } - if ( pattern !== undefined && /[^$\w -]/.test(prop) ) { - prop = `${prop}:${pattern}`; - pattern = undefined; + const extraArgs = safe.getExtraArgs(Array.from(arguments), 2); + const logPrefix = safe.makeLogPrefix('prevent-addEventListener', type, pattern); + const reType = safe.patternToRegex(type, undefined, true); + const rePattern = safe.patternToRegex(pattern); + const targetSelector = extraArgs.elements || undefined; + const elementMatches = elem => { + if ( targetSelector === 'window' ) { return elem === window; } + if ( targetSelector === 'document' ) { return elem === document; } + if ( elem && elem.matches && elem.matches(targetSelector) ) { return true; } + const elems = Array.from(document.querySelectorAll(targetSelector)); + return elems.includes(elem); + }; + const elementDetails = elem => { + if ( elem instanceof Window ) { return 'window'; } + if ( elem instanceof Document ) { return 'document'; } + if ( elem instanceof Element === false ) { return '?'; } + const parts = []; + // https://github.com/uBlockOrigin/uAssets/discussions/17907#discussioncomment-9871079 + const id = String(elem.id); + if ( id !== '' ) { parts.push(`#${CSS.escape(id)}`); } + for ( let i = 0; i < elem.classList.length; i++ ) { + parts.push(`.${CSS.escape(elem.classList.item(i))}`); } - if ( pattern !== undefined ) { - needles.set(prop, safe.initPattern(pattern, options)); - } else if ( implicit !== '' ) { - needles.set(implicit, safe.initPattern(prop, options)); + for ( let i = 0; i < elem.attributes.length; i++ ) { + const attr = elem.attributes.item(i); + if ( attr.name === 'id' ) { continue; } + if ( attr.name === 'class' ) { continue; } + parts.push(`[${CSS.escape(attr.name)}="${attr.value}"]`); } - } - return needles; -} -function matchObjectPropertiesFn(propNeedles, ...objs) { - const safe = safeSelf(); - const matched = []; - for ( const obj of objs ) { - if ( obj instanceof Object === false ) { continue; } - for ( const [ prop, details ] of propNeedles ) { - let value = obj[prop]; - if ( value === undefined ) { continue; } - if ( typeof value !== 'string' ) { - try { value = safe.JSON_stringify(value); } - catch { } - if ( typeof value !== 'string' ) { continue; } + return parts.join(''); + }; + const shouldPrevent = (thisArg, type, handler) => { + const matchesType = safe.RegExp_test.call(reType, type); + const matchesHandler = safe.RegExp_test.call(rePattern, handler); + const matchesEither = matchesType || matchesHandler; + const matchesBoth = matchesType && matchesHandler; + if ( safe.logLevel > 1 && matchesEither ) { + debugger; // eslint-disable-line no-debugger + } + if ( matchesBoth && targetSelector !== undefined ) { + if ( elementMatches(thisArg) === false ) { return false; } + } + return matchesBoth; + }; + const proxyFn = function(context) { + const { callArgs, thisArg } = context; + let t, h; + try { + t = String(callArgs[0]); + if ( typeof callArgs[1] === 'function' ) { + h = String(safe.Function_toString(callArgs[1])); + } else if ( typeof callArgs[1] === 'object' && callArgs[1] !== null ) { + if ( typeof callArgs[1].handleEvent === 'function' ) { + h = String(safe.Function_toString(callArgs[1].handleEvent)); + } + } else { + h = String(callArgs[1]); } - if ( safe.testPattern(details, value) === false ) { return; } - matched.push(`${prop}: ${value}`); + } catch { } - } - return matched; -} + if ( type === '' && pattern === '' ) { + safe.uboLog(logPrefix, `Called: ${t}\n${h}\n${elementDetails(thisArg)}`); + } else if ( shouldPrevent(thisArg, t, h) ) { + return safe.uboLog(logPrefix, `Prevented: ${t}\n${h}\n${elementDetails(thisArg)}`); + } + return context.reflect(); + }; + runAt(( ) => { + proxyApplyFn('EventTarget.prototype.addEventListener', proxyFn); + if ( extraArgs.protect ) { + const { addEventListener } = EventTarget.prototype; + Object.defineProperty(EventTarget.prototype, 'addEventListener', { + set() { }, + get() { return addEventListener; } + }); + } + proxyApplyFn('document.addEventListener', proxyFn); + if ( extraArgs.protect ) { + const { addEventListener } = document; + Object.defineProperty(document, 'addEventListener', { + set() { }, + get() { return addEventListener; } + }); + } + }, extraArgs.runAt); +}; +preventAddEventListener(...args); +}, +}; + + +scriptlets['prevent-dialog.js'] = { +aliases: [], + +requiresTrust: false, +func: function (scriptletGlobals = {}, ...args) { function safeSelf() { if ( scriptletGlobals.safeSelf ) { return scriptletGlobals.safeSelf; @@ -19277,6 +19758,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -19446,6 +19928,165 @@ function safeSelf() { } return safe; } +function preventDialog( + selector = '', +) { + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('prevent-dialog', selector); + const prevent = ( ) => { + debouncer = undefined; + const elems = document.querySelectorAll(`dialog${selector}`); + for ( const elem of elems ) { + if ( typeof elem.close !== 'function' ) { continue; } + if ( elem.open === false ) { continue; } + elem.close(); + safe.uboLog(logPrefix, 'Closed'); + } + }; + let debouncer; + const observer = new MutationObserver(( ) => { + if ( debouncer !== undefined ) { return; } + debouncer = requestAnimationFrame(prevent); + }); + observer.observe(document, { + attributes: true, + childList: true, + subtree: true, + }); +}; +preventDialog(...args); +}, +}; + + +scriptlets['prevent-fetch.js'] = { +aliases: ["no-fetch-if.js"], + +requiresTrust: false, +func: function (scriptletGlobals = {}, ...args) { +function proxyApplyFn( + target = '', + handler = '' +) { + let context = globalThis; + let prop = target; + for (;;) { + const pos = prop.indexOf('.'); + if ( pos === -1 ) { break; } + context = context[prop.slice(0, pos)]; + if ( context instanceof Object === false ) { return; } + prop = prop.slice(pos+1); + } + const fn = context[prop]; + if ( typeof fn !== 'function' ) { return; } + if ( proxyApplyFn.CtorContext === undefined ) { + proxyApplyFn.ctorContexts = []; + proxyApplyFn.CtorContext = class { + constructor(...args) { + this.init(...args); + } + init(callFn, callArgs) { + this.callFn = callFn; + this.callArgs = callArgs; + return this; + } + reflect() { + const r = Reflect.construct(this.callFn, this.callArgs); + this.callFn = this.callArgs = this.private = undefined; + proxyApplyFn.ctorContexts.push(this); + return r; + } + static factory(...args) { + return proxyApplyFn.ctorContexts.length !== 0 + ? proxyApplyFn.ctorContexts.pop().init(...args) + : new proxyApplyFn.CtorContext(...args); + } + }; + proxyApplyFn.applyContexts = []; + proxyApplyFn.ApplyContext = class { + constructor(...args) { + this.init(...args); + } + init(callFn, thisArg, callArgs) { + this.callFn = callFn; + this.thisArg = thisArg; + this.callArgs = callArgs; + return this; + } + reflect() { + const r = Reflect.apply(this.callFn, this.thisArg, this.callArgs); + this.callFn = this.thisArg = this.callArgs = this.private = undefined; + proxyApplyFn.applyContexts.push(this); + return r; + } + static factory(...args) { + return proxyApplyFn.applyContexts.length !== 0 + ? proxyApplyFn.applyContexts.pop().init(...args) + : new proxyApplyFn.ApplyContext(...args); + } + }; + proxyApplyFn.isCtor = new Map(); + } + if ( proxyApplyFn.isCtor.has(target) === false ) { + proxyApplyFn.isCtor.set(target, fn.prototype?.constructor === fn); + } + const fnStr = fn.toString(); + const toString = (function toString() { return fnStr; }).bind(null); + const proxyDetails = { + apply(target, thisArg, args) { + return handler(proxyApplyFn.ApplyContext.factory(target, thisArg, args)); + }, + get(target, prop) { + if ( prop === 'toString' ) { return toString; } + return Reflect.get(target, prop); + }, + }; + if ( proxyApplyFn.isCtor.get(target) ) { + proxyDetails.construct = function(target, args) { + return handler(proxyApplyFn.CtorContext.factory(target, args)); + }; + } + context[prop] = new Proxy(fn, proxyDetails); +} +function parsePropertiesToMatchFn(propsToMatch, implicit = '') { + const safe = safeSelf(); + const needles = new Map(); + if ( propsToMatch === undefined || propsToMatch === '' ) { return needles; } + const options = { canNegate: true }; + for ( const needle of safe.String_split.call(propsToMatch, /\s+/) ) { + let [ prop, pattern ] = safe.String_split.call(needle, ':'); + if ( prop === '' ) { continue; } + if ( pattern !== undefined && /[^$\w -]/.test(prop) ) { + prop = `${prop}:${pattern}`; + pattern = undefined; + } + if ( pattern !== undefined ) { + needles.set(prop, safe.initPattern(pattern, options)); + } else if ( implicit !== '' ) { + needles.set(implicit, safe.initPattern(prop, options)); + } + } + return needles; +} +function matchObjectPropertiesFn(propNeedles, ...objs) { + const safe = safeSelf(); + const matched = []; + for ( const obj of objs ) { + if ( obj instanceof Object === false ) { continue; } + for ( const [ prop, details ] of propNeedles ) { + let value = obj[prop]; + if ( value === undefined ) { continue; } + if ( typeof value !== 'string' ) { + try { value = safe.JSON_stringify(value); } + catch { } + if ( typeof value !== 'string' ) { continue; } + } + if ( safe.testPattern(details, value) === false ) { return; } + matched.push(`${prop}: ${value}`); + } + } + return matched; +} function generateContentFn(trusted, directive) { const safe = safeSelf(); const randomize = len => { @@ -19503,6 +20144,230 @@ function generateContentFn(trusted, directive) { } return ''; } +function safeSelf() { + if ( scriptletGlobals.safeSelf ) { + return scriptletGlobals.safeSelf; + } + const self = globalThis; + const safe = { + 'Array_from': Array.from, + 'Error': self.Error, + 'Function_toStringFn': self.Function.prototype.toString, + 'Function_toString': thisArg => safe.Function_toStringFn.call(thisArg), + 'Math_floor': Math.floor, + 'Math_max': Math.max, + 'Math_min': Math.min, + 'Math_random': Math.random, + 'Object': Object, + 'Object_defineProperty': Object.defineProperty.bind(Object), + 'Object_defineProperties': Object.defineProperties.bind(Object), + 'Object_fromEntries': Object.fromEntries.bind(Object), + 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), + 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, + 'RegExp': self.RegExp, + 'RegExp_test': self.RegExp.prototype.test, + 'RegExp_exec': self.RegExp.prototype.exec, + 'Request_clone': self.Request.prototype.clone, + 'String': self.String, + 'String_fromCharCode': String.fromCharCode, + 'String_split': String.prototype.split, + 'XMLHttpRequest': self.XMLHttpRequest, + 'addEventListener': self.EventTarget.prototype.addEventListener, + 'removeEventListener': self.EventTarget.prototype.removeEventListener, + 'fetch': self.fetch, + 'JSON': self.JSON, + 'JSON_parseFn': self.JSON.parse, + 'JSON_stringifyFn': self.JSON.stringify, + 'JSON_parse': (...args) => safe.JSON_parseFn.call(safe.JSON, ...args), + 'JSON_stringify': (...args) => safe.JSON_stringifyFn.call(safe.JSON, ...args), + 'log': console.log.bind(console), + // Properties + logLevel: 0, + // Methods + makeLogPrefix(...args) { + return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; + }, + uboLog(...args) { + if ( this.sendToLogger === undefined ) { return; } + if ( args === undefined || args[0] === '' ) { return; } + return this.sendToLogger('info', ...args); + + }, + uboErr(...args) { + if ( this.sendToLogger === undefined ) { return; } + if ( args === undefined || args[0] === '' ) { return; } + return this.sendToLogger('error', ...args); + }, + escapeRegexChars(s) { + return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + }, + initPattern(pattern, options = {}) { + if ( pattern === '' ) { + return { matchAll: true, expect: true }; + } + const expect = (options.canNegate !== true || pattern.startsWith('!') === false); + if ( expect === false ) { + pattern = pattern.slice(1); + } + const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); + if ( match !== null ) { + return { + re: new this.RegExp( + match[1], + match[2] || options.flags + ), + expect, + }; + } + if ( options.flags !== undefined ) { + return { + re: new this.RegExp(this.escapeRegexChars(pattern), + options.flags + ), + expect, + }; + } + return { pattern, expect }; + }, + testPattern(details, haystack) { + if ( details.matchAll ) { return true; } + if ( details.re ) { + return this.RegExp_test.call(details.re, haystack) === details.expect; + } + return haystack.includes(details.pattern) === details.expect; + }, + patternToRegex(pattern, flags = undefined, verbatim = false) { + if ( pattern === '' ) { return /^/; } + const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); + if ( match === null ) { + const reStr = this.escapeRegexChars(pattern); + return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); + } + try { + return new RegExp(match[1], match[2] || undefined); + } + catch { + } + return /^/; + }, + getExtraArgs(args, offset = 0) { + const entries = args.slice(offset).reduce((out, v, i, a) => { + if ( (i & 1) === 0 ) { + const rawValue = a[i+1]; + const value = /^\d+$/.test(rawValue) + ? parseInt(rawValue, 10) + : rawValue; + out.push([ a[i], value ]); + } + return out; + }, []); + return this.Object_fromEntries(entries); + }, + onIdle(fn, options) { + if ( self.requestIdleCallback ) { + return self.requestIdleCallback(fn, options); + } + return self.requestAnimationFrame(fn); + }, + offIdle(id) { + if ( self.requestIdleCallback ) { + return self.cancelIdleCallback(id); + } + return self.cancelAnimationFrame(id); + } + }; + scriptletGlobals.safeSelf = safe; + if ( scriptletGlobals.bcSecret === undefined ) { return safe; } + // This is executed only when the logger is opened + safe.logLevel = scriptletGlobals.logLevel || 1; + let lastLogType = ''; + let lastLogText = ''; + let lastLogTime = 0; + safe.toLogText = (type, ...args) => { + if ( args.length === 0 ) { return; } + const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; + if ( text === lastLogText && type === lastLogType ) { + if ( (Date.now() - lastLogTime) < 5000 ) { return; } + } + lastLogType = type; + lastLogText = text; + lastLogTime = Date.now(); + return text; + }; + try { + const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); + let bcBuffer = []; + safe.sendToLogger = (type, ...args) => { + const text = safe.toLogText(type, ...args); + if ( text === undefined ) { return; } + if ( bcBuffer === undefined ) { + return bc.postMessage({ what: 'messageToLogger', type, text }); + } + bcBuffer.push({ type, text }); + }; + bc.onmessage = ev => { + const msg = ev.data; + switch ( msg ) { + case 'iamready!': + if ( bcBuffer === undefined ) { break; } + bcBuffer.forEach(({ type, text }) => + bc.postMessage({ what: 'messageToLogger', type, text }) + ); + bcBuffer = undefined; + break; + case 'setScriptletLogLevelToOne': + safe.logLevel = 1; + break; + case 'setScriptletLogLevelToTwo': + safe.logLevel = 2; + break; + } + }; + bc.postMessage('areyouready?'); + } catch { + safe.sendToLogger = (type, ...args) => { + const text = safe.toLogText(type, ...args); + if ( text === undefined ) { return; } + safe.log(`uBO ${text}`); + }; + } + return safe; +} +function collateFetchArgumentsFn(resource, options) { + const safe = safeSelf(); + const props = [ + 'body', 'cache', 'credentials', 'duplex', 'headers', + 'integrity', 'keepalive', 'method', 'mode', 'priority', + 'redirect', 'referrer', 'referrerPolicy', 'url' + ]; + const out = {}; + if ( collateFetchArgumentsFn.collateKnownProps === undefined ) { + collateFetchArgumentsFn.collateKnownProps = (src, out) => { + for ( const prop of props ) { + if ( src[prop] === undefined ) { continue; } + out[prop] = src[prop]; + } + }; + } + if ( + typeof resource !== 'object' || + safe.Object_toString.call(resource) !== '[object Request]' + ) { + out.url = `${resource}`; + } else { + let clone; + try { + clone = safe.Request_clone.call(resource); + } catch { + } + collateFetchArgumentsFn.collateKnownProps(clone || resource, out); + } + if ( typeof options === 'object' && options !== null ) { + collateFetchArgumentsFn.collateKnownProps(options, out); + } + return out; +} function preventFetchFn( trusted = false, propsToMatch = '', @@ -19549,31 +20414,9 @@ function preventFetchFn( } proxyApplyFn('fetch', function fetch(context) { const { callArgs } = context; - const details = (( ) => { - const fetchProps = (src, out) => { - if ( typeof src !== 'object' || src === null ) { return; } - const props = [ - 'body', 'cache', 'credentials', 'duplex', 'headers', - 'integrity', 'keepalive', 'method', 'mode', 'priority', - 'redirect', 'referrer', 'referrerPolicy', 'signal', - ]; - for ( const prop of props ) { - if ( src[prop] === undefined ) { continue; } - out[prop] = src[prop]; - } - }; - const out = {}; - if ( callArgs[0] instanceof self.Request ) { - out.url = `${callArgs[0].url}`; - fetchProps(callArgs[0], out); - } else { - out.url = `${callArgs[0]}`; - } - fetchProps(callArgs[1], out); - return out; - })(); + const details = collateFetchArgumentsFn(...callArgs); if ( safe.logLevel > 1 || propsToMatch === '' && responseBody === '' ) { - const out = Array.from(details).map(a => `${a[0]}:${a[1]}`); + const out = Array.from(Object.entries(details)).map(a => `${a[0]}:${a[1]}`); safe.uboLog(logPrefix, `Called: ${out.join('\n')}`); } if ( propsToMatch === '' && responseBody === '' ) { @@ -19740,6 +20583,63 @@ function matchObjectPropertiesFn(propNeedles, ...objs) { } return matched; } +function generateContentFn(trusted, directive) { + const safe = safeSelf(); + const randomize = len => { + const chunks = []; + let textSize = 0; + do { + const s = safe.Math_random().toString(36).slice(2); + chunks.push(s); + textSize += s.length; + } + while ( textSize < len ); + return chunks.join(' ').slice(0, len); + }; + if ( directive === 'true' ) { + return randomize(10); + } + if ( directive === 'emptyObj' ) { + return '{}'; + } + if ( directive === 'emptyArr' ) { + return '[]'; + } + if ( directive === 'emptyStr' ) { + return ''; + } + if ( directive.startsWith('length:') ) { + const match = /^length:(\d+)(?:-(\d+))?$/.exec(directive); + if ( match === null ) { return ''; } + const min = parseInt(match[1], 10); + const extent = safe.Math_max(parseInt(match[2], 10) || 0, min) - min; + const len = safe.Math_min(min + extent * safe.Math_random(), 500000); + return randomize(len | 0); + } + if ( directive.startsWith('war:') ) { + if ( scriptletGlobals.warOrigin === undefined ) { return ''; } + return new Promise(resolve => { + const warOrigin = scriptletGlobals.warOrigin; + const warName = directive.slice(4); + const fullpath = [ warOrigin, '/', warName ]; + const warSecret = scriptletGlobals.warSecret; + if ( warSecret !== undefined ) { + fullpath.push('?secret=', warSecret); + } + const warXHR = new safe.XMLHttpRequest(); + warXHR.responseType = 'text'; + warXHR.onloadend = ev => { + resolve(ev.target.responseText || ''); + }; + warXHR.open('GET', fullpath.join('')); + warXHR.send(); + }).catch(( ) => ''); + } + if ( trusted ) { + return directive; + } + return ''; +} function safeSelf() { if ( scriptletGlobals.safeSelf ) { return scriptletGlobals.safeSelf; @@ -19760,6 +20660,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -19929,62 +20830,39 @@ function safeSelf() { } return safe; } -function generateContentFn(trusted, directive) { +function collateFetchArgumentsFn(resource, options) { const safe = safeSelf(); - const randomize = len => { - const chunks = []; - let textSize = 0; - do { - const s = safe.Math_random().toString(36).slice(2); - chunks.push(s); - textSize += s.length; - } - while ( textSize < len ); - return chunks.join(' ').slice(0, len); - }; - if ( directive === 'true' ) { - return randomize(10); - } - if ( directive === 'emptyObj' ) { - return '{}'; - } - if ( directive === 'emptyArr' ) { - return '[]'; - } - if ( directive === 'emptyStr' ) { - return ''; - } - if ( directive.startsWith('length:') ) { - const match = /^length:(\d+)(?:-(\d+))?$/.exec(directive); - if ( match === null ) { return ''; } - const min = parseInt(match[1], 10); - const extent = safe.Math_max(parseInt(match[2], 10) || 0, min) - min; - const len = safe.Math_min(min + extent * safe.Math_random(), 500000); - return randomize(len | 0); - } - if ( directive.startsWith('war:') ) { - if ( scriptletGlobals.warOrigin === undefined ) { return ''; } - return new Promise(resolve => { - const warOrigin = scriptletGlobals.warOrigin; - const warName = directive.slice(4); - const fullpath = [ warOrigin, '/', warName ]; - const warSecret = scriptletGlobals.warSecret; - if ( warSecret !== undefined ) { - fullpath.push('?secret=', warSecret); + const props = [ + 'body', 'cache', 'credentials', 'duplex', 'headers', + 'integrity', 'keepalive', 'method', 'mode', 'priority', + 'redirect', 'referrer', 'referrerPolicy', 'url' + ]; + const out = {}; + if ( collateFetchArgumentsFn.collateKnownProps === undefined ) { + collateFetchArgumentsFn.collateKnownProps = (src, out) => { + for ( const prop of props ) { + if ( src[prop] === undefined ) { continue; } + out[prop] = src[prop]; } - const warXHR = new safe.XMLHttpRequest(); - warXHR.responseType = 'text'; - warXHR.onloadend = ev => { - resolve(ev.target.responseText || ''); - }; - warXHR.open('GET', fullpath.join('')); - warXHR.send(); - }).catch(( ) => ''); + }; } - if ( trusted ) { - return directive; + if ( + typeof resource !== 'object' || + safe.Object_toString.call(resource) !== '[object Request]' + ) { + out.url = `${resource}`; + } else { + let clone; + try { + clone = safe.Request_clone.call(resource); + } catch { + } + collateFetchArgumentsFn.collateKnownProps(clone || resource, out); } - return ''; + if ( typeof options === 'object' && options !== null ) { + collateFetchArgumentsFn.collateKnownProps(options, out); + } + return out; } function preventFetchFn( trusted = false, @@ -20032,31 +20910,9 @@ function preventFetchFn( } proxyApplyFn('fetch', function fetch(context) { const { callArgs } = context; - const details = (( ) => { - const fetchProps = (src, out) => { - if ( typeof src !== 'object' || src === null ) { return; } - const props = [ - 'body', 'cache', 'credentials', 'duplex', 'headers', - 'integrity', 'keepalive', 'method', 'mode', 'priority', - 'redirect', 'referrer', 'referrerPolicy', 'signal', - ]; - for ( const prop of props ) { - if ( src[prop] === undefined ) { continue; } - out[prop] = src[prop]; - } - }; - const out = {}; - if ( callArgs[0] instanceof self.Request ) { - out.url = `${callArgs[0].url}`; - fetchProps(callArgs[0], out); - } else { - out.url = `${callArgs[0]}`; - } - fetchProps(callArgs[1], out); - return out; - })(); + const details = collateFetchArgumentsFn(...callArgs); if ( safe.logLevel > 1 || propsToMatch === '' && responseBody === '' ) { - const out = Array.from(details).map(a => `${a[0]}:${a[1]}`); + const out = Array.from(Object.entries(details)).map(a => `${a[0]}:${a[1]}`); safe.uboLog(logPrefix, `Called: ${out.join('\n')}`); } if ( propsToMatch === '' && responseBody === '' ) { @@ -20120,6 +20976,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -20354,6 +21211,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -20693,6 +21551,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -21032,6 +21891,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -21387,6 +22247,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -21819,6 +22680,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -22251,6 +23113,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -22711,6 +23574,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -23104,6 +23968,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -23437,6 +24302,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -23773,6 +24639,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -24097,6 +24964,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -24347,6 +25215,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -24597,6 +25466,109 @@ aliases: [], world: 'ISOLATED', requiresTrust: false, func: function (scriptletGlobals = {}, ...args) { +function getSafeCookieValuesFn() { + return [ + 'accept', 'reject', + 'accepted', 'rejected', 'notaccepted', + 'allow', 'disallow', 'deny', + 'allowed', 'denied', + 'approved', 'disapproved', + 'checked', 'unchecked', + 'dismiss', 'dismissed', + 'enable', 'disable', + 'enabled', 'disabled', + 'essential', 'nonessential', + 'forbidden', 'forever', + 'hide', 'hidden', + 'necessary', 'required', + 'ok', + 'on', 'off', + 'true', 't', 'false', 'f', + 'yes', 'y', 'no', 'n', + 'all', 'none', 'functional', + 'granted', 'done', + 'decline', 'declined', + 'closed', 'next', 'mandatory', + 'disagree', 'agree', + ]; +} +function setLocalStorageItemFn( + which = 'local', + trusted = false, + key = '', + value = '', + options = {} +) { + if ( key === '' ) { return; } + + // For increased compatibility with AdGuard + if ( value === 'emptyArr' ) { + value = '[]'; + } else if ( value === 'emptyObj' ) { + value = '{}'; + } + + const trustedValues = [ + '', + 'undefined', 'null', + '{}', '[]', '""', + '$remove$', + ...getSafeCookieValuesFn(), + ]; + + if ( trusted ) { + if ( value.includes('$now$') ) { + value = value.replaceAll('$now$', Date.now()); + } + if ( value.includes('$currentDate$') ) { + value = value.replaceAll('$currentDate$', `${Date()}`); + } + if ( value.includes('$currentISODate$') ) { + value = value.replaceAll('$currentISODate$', (new Date()).toISOString()); + } + } else { + const normalized = value.toLowerCase(); + const match = /^("?)(.+)\1$/.exec(normalized); + const unquoted = match && match[2] || normalized; + if ( trustedValues.includes(unquoted) === false ) { + if ( /^-?\d+$/.test(unquoted) === false ) { return; } + const n = parseInt(unquoted, 10) || 0; + if ( n < -32767 || n > 32767 ) { return; } + } + } + + let modified = false; + + try { + const storage = self[`${which}Storage`]; + if ( value === '$remove$' ) { + const safe = safeSelf(); + const pattern = safe.patternToRegex(key, undefined, true ); + const toRemove = []; + for ( let i = 0, n = storage.length; i < n; i++ ) { + const key = storage.key(i); + if ( pattern.test(key) ) { toRemove.push(key); } + } + modified = toRemove.length !== 0; + for ( const key of toRemove ) { + storage.removeItem(key); + } + } else { + + const before = storage.getItem(key); + const after = `${value}`; + modified = after !== before; + if ( modified ) { + storage.setItem(key, after); + } + } + } catch { + } + + if ( modified && typeof options.reload === 'number' ) { + setTimeout(( ) => { window.location.reload(); }, options.reload); + } +} function safeSelf() { if ( scriptletGlobals.safeSelf ) { return scriptletGlobals.safeSelf; @@ -24617,6 +25589,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -24786,6 +25759,21 @@ function safeSelf() { } return safe; } +function setLocalStorageItem(key = '', value = '') { + const safe = safeSelf(); + const options = safe.getExtraArgs(Array.from(arguments), 2) + setLocalStorageItemFn('local', false, key, value, options); +}; +setLocalStorageItem(...args); +}, +}; + + +scriptlets['set-session-storage-item.js'] = { +aliases: [], +world: 'ISOLATED', +requiresTrust: false, +func: function (scriptletGlobals = {}, ...args) { function getSafeCookieValuesFn() { return [ 'accept', 'reject', @@ -24817,6 +25805,7 @@ function setLocalStorageItemFn( trusted = false, key = '', value = '', + options = {} ) { if ( key === '' ) { return; } @@ -24856,6 +25845,8 @@ function setLocalStorageItemFn( } } + let modified = false; + try { const storage = self[`${which}Storage`]; if ( value === '$remove$' ) { @@ -24866,28 +25857,26 @@ function setLocalStorageItemFn( const key = storage.key(i); if ( pattern.test(key) ) { toRemove.push(key); } } + modified = toRemove.length !== 0; for ( const key of toRemove ) { storage.removeItem(key); } } else { - storage.setItem(key, `${value}`); + + const before = storage.getItem(key); + const after = `${value}`; + modified = after !== before; + if ( modified ) { + storage.setItem(key, after); + } } } catch { } -} -function setLocalStorageItem(key = '', value = '') { - setLocalStorageItemFn('local', false, key, value); -}; -setLocalStorageItem(...args); -}, -}; - -scriptlets['set-session-storage-item.js'] = { -aliases: [], -world: 'ISOLATED', -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { + if ( modified && typeof options.reload === 'number' ) { + setTimeout(( ) => { window.location.reload(); }, options.reload); + } +} function safeSelf() { if ( scriptletGlobals.safeSelf ) { return scriptletGlobals.safeSelf; @@ -24908,6 +25897,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -25077,6 +26067,21 @@ function safeSelf() { } return safe; } +function setSessionStorageItem(key = '', value = '') { + const safe = safeSelf(); + const options = safe.getExtraArgs(Array.from(arguments), 2) + setLocalStorageItemFn('session', false, key, value, options); +}; +setSessionStorageItem(...args); +}, +}; + + +scriptlets['trusted-set-local-storage-item.js'] = { +aliases: [], +world: 'ISOLATED', +requiresTrust: true, +func: function (scriptletGlobals = {}, ...args) { function getSafeCookieValuesFn() { return [ 'accept', 'reject', @@ -25108,6 +26113,7 @@ function setLocalStorageItemFn( trusted = false, key = '', value = '', + options = {} ) { if ( key === '' ) { return; } @@ -25147,6 +26153,8 @@ function setLocalStorageItemFn( } } + let modified = false; + try { const storage = self[`${which}Storage`]; if ( value === '$remove$' ) { @@ -25157,28 +26165,26 @@ function setLocalStorageItemFn( const key = storage.key(i); if ( pattern.test(key) ) { toRemove.push(key); } } + modified = toRemove.length !== 0; for ( const key of toRemove ) { storage.removeItem(key); } } else { - storage.setItem(key, `${value}`); + + const before = storage.getItem(key); + const after = `${value}`; + modified = after !== before; + if ( modified ) { + storage.setItem(key, after); + } } } catch { } -} -function setSessionStorageItem(key = '', value = '') { - setLocalStorageItemFn('session', false, key, value); -}; -setSessionStorageItem(...args); -}, -}; - -scriptlets['trusted-set-local-storage-item.js'] = { -aliases: [], -world: 'ISOLATED', -requiresTrust: true, -func: function (scriptletGlobals = {}, ...args) { + if ( modified && typeof options.reload === 'number' ) { + setTimeout(( ) => { window.location.reload(); }, options.reload); + } +} function safeSelf() { if ( scriptletGlobals.safeSelf ) { return scriptletGlobals.safeSelf; @@ -25199,6 +26205,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -25368,6 +26375,21 @@ function safeSelf() { } return safe; } +function trustedSetLocalStorageItem(key = '', value = '') { + const safe = safeSelf(); + const options = safe.getExtraArgs(Array.from(arguments), 2) + setLocalStorageItemFn('local', true, key, value, options); +}; +trustedSetLocalStorageItem(...args); +}, +}; + + +scriptlets['trusted-set-session-storage-item.js'] = { +aliases: [], +world: 'ISOLATED', +requiresTrust: true, +func: function (scriptletGlobals = {}, ...args) { function getSafeCookieValuesFn() { return [ 'accept', 'reject', @@ -25399,6 +26421,7 @@ function setLocalStorageItemFn( trusted = false, key = '', value = '', + options = {} ) { if ( key === '' ) { return; } @@ -25438,6 +26461,8 @@ function setLocalStorageItemFn( } } + let modified = false; + try { const storage = self[`${which}Storage`]; if ( value === '$remove$' ) { @@ -25448,28 +26473,26 @@ function setLocalStorageItemFn( const key = storage.key(i); if ( pattern.test(key) ) { toRemove.push(key); } } + modified = toRemove.length !== 0; for ( const key of toRemove ) { storage.removeItem(key); } } else { - storage.setItem(key, `${value}`); + + const before = storage.getItem(key); + const after = `${value}`; + modified = after !== before; + if ( modified ) { + storage.setItem(key, after); + } } } catch { } -} -function trustedSetLocalStorageItem(key = '', value = '') { - setLocalStorageItemFn('local', true, key, value); -}; -trustedSetLocalStorageItem(...args); -}, -}; - -scriptlets['trusted-set-session-storage-item.js'] = { -aliases: [], -world: 'ISOLATED', -requiresTrust: true, -func: function (scriptletGlobals = {}, ...args) { + if ( modified && typeof options.reload === 'number' ) { + setTimeout(( ) => { window.location.reload(); }, options.reload); + } +} function safeSelf() { if ( scriptletGlobals.safeSelf ) { return scriptletGlobals.safeSelf; @@ -25490,6 +26513,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -25659,97 +26683,10 @@ function safeSelf() { } return safe; } -function getSafeCookieValuesFn() { - return [ - 'accept', 'reject', - 'accepted', 'rejected', 'notaccepted', - 'allow', 'disallow', 'deny', - 'allowed', 'denied', - 'approved', 'disapproved', - 'checked', 'unchecked', - 'dismiss', 'dismissed', - 'enable', 'disable', - 'enabled', 'disabled', - 'essential', 'nonessential', - 'forbidden', 'forever', - 'hide', 'hidden', - 'necessary', 'required', - 'ok', - 'on', 'off', - 'true', 't', 'false', 'f', - 'yes', 'y', 'no', 'n', - 'all', 'none', 'functional', - 'granted', 'done', - 'decline', 'declined', - 'closed', 'next', 'mandatory', - 'disagree', 'agree', - ]; -} -function setLocalStorageItemFn( - which = 'local', - trusted = false, - key = '', - value = '', -) { - if ( key === '' ) { return; } - - // For increased compatibility with AdGuard - if ( value === 'emptyArr' ) { - value = '[]'; - } else if ( value === 'emptyObj' ) { - value = '{}'; - } - - const trustedValues = [ - '', - 'undefined', 'null', - '{}', '[]', '""', - '$remove$', - ...getSafeCookieValuesFn(), - ]; - - if ( trusted ) { - if ( value.includes('$now$') ) { - value = value.replaceAll('$now$', Date.now()); - } - if ( value.includes('$currentDate$') ) { - value = value.replaceAll('$currentDate$', `${Date()}`); - } - if ( value.includes('$currentISODate$') ) { - value = value.replaceAll('$currentISODate$', (new Date()).toISOString()); - } - } else { - const normalized = value.toLowerCase(); - const match = /^("?)(.+)\1$/.exec(normalized); - const unquoted = match && match[2] || normalized; - if ( trustedValues.includes(unquoted) === false ) { - if ( /^-?\d+$/.test(unquoted) === false ) { return; } - const n = parseInt(unquoted, 10) || 0; - if ( n < -32767 || n > 32767 ) { return; } - } - } - - try { - const storage = self[`${which}Storage`]; - if ( value === '$remove$' ) { - const safe = safeSelf(); - const pattern = safe.patternToRegex(key, undefined, true ); - const toRemove = []; - for ( let i = 0, n = storage.length; i < n; i++ ) { - const key = storage.key(i); - if ( pattern.test(key) ) { toRemove.push(key); } - } - for ( const key of toRemove ) { - storage.removeItem(key); - } - } else { - storage.setItem(key, `${value}`); - } - } catch { - } -} function trustedSetSessionStorageItem(key = '', value = '') { - setLocalStorageItemFn('session', true, key, value); + const safe = safeSelf(); + const options = safe.getExtraArgs(Array.from(arguments), 2) + setLocalStorageItemFn('session', true, key, value, options); }; trustedSetSessionStorageItem(...args); }, @@ -25796,6 +26733,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -26122,6 +27060,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -26383,6 +27322,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -26552,440 +27492,47 @@ function safeSelf() { } return safe; } -function getRandomTokenFn() { - const safe = safeSelf(); - return safe.String_fromCharCode(Date.now() % 26 + 97) + - safe.Math_floor(safe.Math_random() * 982451653 + 982451653).toString(36); -} -function getExceptionTokenFn() { - const token = getRandomTokenFn(); - const oe = self.onerror; - self.onerror = function(msg, ...args) { - if ( typeof msg === 'string' && msg.includes(token) ) { return true; } - if ( oe instanceof Function ) { - return oe.call(this, msg, ...args); - } - }.bind(); - return token; -} -function abortOnPropertyWrite( - prop = '' -) { - if ( typeof prop !== 'string' ) { return; } - if ( prop === '' ) { return; } - const safe = safeSelf(); - const logPrefix = safe.makeLogPrefix('abort-on-property-write', prop); - const exceptionToken = getExceptionTokenFn(); - let owner = window; - for (;;) { - const pos = prop.indexOf('.'); - if ( pos === -1 ) { break; } - owner = owner[prop.slice(0, pos)]; - if ( owner instanceof Object === false ) { return; } - prop = prop.slice(pos + 1); - } - delete owner[prop]; - Object.defineProperty(owner, prop, { - set: function() { - safe.uboLog(logPrefix, 'Aborted'); - throw new ReferenceError(exceptionToken); - } - }); -}; -abortOnPropertyWrite(...args); -}, -}; - - -scriptlets['addEventListener-defuser.js'] = { -aliases: ["aeld.js","prevent-addEventListener.js"], - -requiresTrust: false, -func: function (scriptletGlobals = {}, ...args) { -function shouldDebug(details) { - if ( details instanceof Object === false ) { return false; } - return scriptletGlobals.canDebug && details.debug; -} -function safeSelf() { - if ( scriptletGlobals.safeSelf ) { - return scriptletGlobals.safeSelf; - } - const self = globalThis; - const safe = { - 'Array_from': Array.from, - 'Error': self.Error, - 'Function_toStringFn': self.Function.prototype.toString, - 'Function_toString': thisArg => safe.Function_toStringFn.call(thisArg), - 'Math_floor': Math.floor, - 'Math_max': Math.max, - 'Math_min': Math.min, - 'Math_random': Math.random, - 'Object': Object, - 'Object_defineProperty': Object.defineProperty.bind(Object), - 'Object_defineProperties': Object.defineProperties.bind(Object), - 'Object_fromEntries': Object.fromEntries.bind(Object), - 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), - 'Object_hasOwn': Object.hasOwn.bind(Object), - 'RegExp': self.RegExp, - 'RegExp_test': self.RegExp.prototype.test, - 'RegExp_exec': self.RegExp.prototype.exec, - 'Request_clone': self.Request.prototype.clone, - 'String': self.String, - 'String_fromCharCode': String.fromCharCode, - 'String_split': String.prototype.split, - 'XMLHttpRequest': self.XMLHttpRequest, - 'addEventListener': self.EventTarget.prototype.addEventListener, - 'removeEventListener': self.EventTarget.prototype.removeEventListener, - 'fetch': self.fetch, - 'JSON': self.JSON, - 'JSON_parseFn': self.JSON.parse, - 'JSON_stringifyFn': self.JSON.stringify, - 'JSON_parse': (...args) => safe.JSON_parseFn.call(safe.JSON, ...args), - 'JSON_stringify': (...args) => safe.JSON_stringifyFn.call(safe.JSON, ...args), - 'log': console.log.bind(console), - // Properties - logLevel: 0, - // Methods - makeLogPrefix(...args) { - return this.sendToLogger && `[${args.join(' \u205D ')}]` || ''; - }, - uboLog(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('info', ...args); - - }, - uboErr(...args) { - if ( this.sendToLogger === undefined ) { return; } - if ( args === undefined || args[0] === '' ) { return; } - return this.sendToLogger('error', ...args); - }, - escapeRegexChars(s) { - return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); - }, - initPattern(pattern, options = {}) { - if ( pattern === '' ) { - return { matchAll: true, expect: true }; - } - const expect = (options.canNegate !== true || pattern.startsWith('!') === false); - if ( expect === false ) { - pattern = pattern.slice(1); - } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match !== null ) { - return { - re: new this.RegExp( - match[1], - match[2] || options.flags - ), - expect, - }; - } - if ( options.flags !== undefined ) { - return { - re: new this.RegExp(this.escapeRegexChars(pattern), - options.flags - ), - expect, - }; - } - return { pattern, expect }; - }, - testPattern(details, haystack) { - if ( details.matchAll ) { return true; } - if ( details.re ) { - return this.RegExp_test.call(details.re, haystack) === details.expect; - } - return haystack.includes(details.pattern) === details.expect; - }, - patternToRegex(pattern, flags = undefined, verbatim = false) { - if ( pattern === '' ) { return /^/; } - const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern); - if ( match === null ) { - const reStr = this.escapeRegexChars(pattern); - return new RegExp(verbatim ? `^${reStr}$` : reStr, flags); - } - try { - return new RegExp(match[1], match[2] || undefined); - } - catch { - } - return /^/; - }, - getExtraArgs(args, offset = 0) { - const entries = args.slice(offset).reduce((out, v, i, a) => { - if ( (i & 1) === 0 ) { - const rawValue = a[i+1]; - const value = /^\d+$/.test(rawValue) - ? parseInt(rawValue, 10) - : rawValue; - out.push([ a[i], value ]); - } - return out; - }, []); - return this.Object_fromEntries(entries); - }, - onIdle(fn, options) { - if ( self.requestIdleCallback ) { - return self.requestIdleCallback(fn, options); - } - return self.requestAnimationFrame(fn); - }, - offIdle(id) { - if ( self.requestIdleCallback ) { - return self.cancelIdleCallback(id); - } - return self.cancelAnimationFrame(id); - } - }; - scriptletGlobals.safeSelf = safe; - if ( scriptletGlobals.bcSecret === undefined ) { return safe; } - // This is executed only when the logger is opened - safe.logLevel = scriptletGlobals.logLevel || 1; - let lastLogType = ''; - let lastLogText = ''; - let lastLogTime = 0; - safe.toLogText = (type, ...args) => { - if ( args.length === 0 ) { return; } - const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; - if ( text === lastLogText && type === lastLogType ) { - if ( (Date.now() - lastLogTime) < 5000 ) { return; } - } - lastLogType = type; - lastLogText = text; - lastLogTime = Date.now(); - return text; - }; - try { - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); - }; - bc.onmessage = ev => { - const msg = ev.data; - switch ( msg ) { - case 'iamready!': - if ( bcBuffer === undefined ) { break; } - bcBuffer.forEach(({ type, text }) => - bc.postMessage({ what: 'messageToLogger', type, text }) - ); - bcBuffer = undefined; - break; - case 'setScriptletLogLevelToOne': - safe.logLevel = 1; - break; - case 'setScriptletLogLevelToTwo': - safe.logLevel = 2; - break; - } - }; - bc.postMessage('areyouready?'); - } catch { - safe.sendToLogger = (type, ...args) => { - const text = safe.toLogText(type, ...args); - if ( text === undefined ) { return; } - safe.log(`uBO ${text}`); - }; - } - return safe; -} -function runAt(fn, when) { - const intFromReadyState = state => { - const targets = { - 'loading': 1, 'asap': 1, - 'interactive': 2, 'end': 2, '2': 2, - 'complete': 3, 'idle': 3, '3': 3, - }; - const tokens = Array.isArray(state) ? state : [ state ]; - for ( const token of tokens ) { - const prop = `${token}`; - if ( Object.hasOwn(targets, prop) === false ) { continue; } - return targets[prop]; - } - return 0; - }; - const runAt = intFromReadyState(when); - if ( intFromReadyState(document.readyState) >= runAt ) { - fn(); return; - } - const onStateChange = ( ) => { - if ( intFromReadyState(document.readyState) < runAt ) { return; } - fn(); - safe.removeEventListener.apply(document, args); - }; +function getRandomTokenFn() { const safe = safeSelf(); - const args = [ 'readystatechange', onStateChange, { capture: true } ]; - safe.addEventListener.apply(document, args); + return safe.String_fromCharCode(Date.now() % 26 + 97) + + safe.Math_floor(safe.Math_random() * 982451653 + 982451653).toString(36); } -function proxyApplyFn( - target = '', - handler = '' +function getExceptionTokenFn() { + const token = getRandomTokenFn(); + const oe = self.onerror; + self.onerror = function(msg, ...args) { + if ( typeof msg === 'string' && msg.includes(token) ) { return true; } + if ( oe instanceof Function ) { + return oe.call(this, msg, ...args); + } + }.bind(); + return token; +} +function abortOnPropertyWrite( + prop = '' ) { - let context = globalThis; - let prop = target; + if ( typeof prop !== 'string' ) { return; } + if ( prop === '' ) { return; } + const safe = safeSelf(); + const logPrefix = safe.makeLogPrefix('abort-on-property-write', prop); + const exceptionToken = getExceptionTokenFn(); + let owner = window; for (;;) { const pos = prop.indexOf('.'); if ( pos === -1 ) { break; } - context = context[prop.slice(0, pos)]; - if ( context instanceof Object === false ) { return; } - prop = prop.slice(pos+1); - } - const fn = context[prop]; - if ( typeof fn !== 'function' ) { return; } - if ( proxyApplyFn.CtorContext === undefined ) { - proxyApplyFn.ctorContexts = []; - proxyApplyFn.CtorContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, callArgs) { - this.callFn = callFn; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.construct(this.callFn, this.callArgs); - this.callFn = this.callArgs = this.private = undefined; - proxyApplyFn.ctorContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.ctorContexts.length !== 0 - ? proxyApplyFn.ctorContexts.pop().init(...args) - : new proxyApplyFn.CtorContext(...args); - } - }; - proxyApplyFn.applyContexts = []; - proxyApplyFn.ApplyContext = class { - constructor(...args) { - this.init(...args); - } - init(callFn, thisArg, callArgs) { - this.callFn = callFn; - this.thisArg = thisArg; - this.callArgs = callArgs; - return this; - } - reflect() { - const r = Reflect.apply(this.callFn, this.thisArg, this.callArgs); - this.callFn = this.thisArg = this.callArgs = this.private = undefined; - proxyApplyFn.applyContexts.push(this); - return r; - } - static factory(...args) { - return proxyApplyFn.applyContexts.length !== 0 - ? proxyApplyFn.applyContexts.pop().init(...args) - : new proxyApplyFn.ApplyContext(...args); - } - }; - proxyApplyFn.isCtor = new Map(); - } - if ( proxyApplyFn.isCtor.has(target) === false ) { - proxyApplyFn.isCtor.set(target, fn.prototype?.constructor === fn); - } - const fnStr = fn.toString(); - const toString = (function toString() { return fnStr; }).bind(null); - const proxyDetails = { - apply(target, thisArg, args) { - return handler(proxyApplyFn.ApplyContext.factory(target, thisArg, args)); - }, - get(target, prop) { - if ( prop === 'toString' ) { return toString; } - return Reflect.get(target, prop); - }, - }; - if ( proxyApplyFn.isCtor.get(target) ) { - proxyDetails.construct = function(target, args) { - return handler(proxyApplyFn.CtorContext.factory(target, args)); - }; + owner = owner[prop.slice(0, pos)]; + if ( owner instanceof Object === false ) { return; } + prop = prop.slice(pos + 1); } - context[prop] = new Proxy(fn, proxyDetails); -} -function addEventListenerDefuser( - type = '', - pattern = '' -) { - const safe = safeSelf(); - const extraArgs = safe.getExtraArgs(Array.from(arguments), 2); - const logPrefix = safe.makeLogPrefix('prevent-addEventListener', type, pattern); - const reType = safe.patternToRegex(type, undefined, true); - const rePattern = safe.patternToRegex(pattern); - const debug = shouldDebug(extraArgs); - const targetSelector = extraArgs.elements || undefined; - const elementMatches = elem => { - if ( targetSelector === 'window' ) { return elem === window; } - if ( targetSelector === 'document' ) { return elem === document; } - if ( elem && elem.matches && elem.matches(targetSelector) ) { return true; } - const elems = Array.from(document.querySelectorAll(targetSelector)); - return elems.includes(elem); - }; - const elementDetails = elem => { - if ( elem instanceof Window ) { return 'window'; } - if ( elem instanceof Document ) { return 'document'; } - if ( elem instanceof Element === false ) { return '?'; } - const parts = []; - // https://github.com/uBlockOrigin/uAssets/discussions/17907#discussioncomment-9871079 - const id = String(elem.id); - if ( id !== '' ) { parts.push(`#${CSS.escape(id)}`); } - for ( let i = 0; i < elem.classList.length; i++ ) { - parts.push(`.${CSS.escape(elem.classList.item(i))}`); - } - for ( let i = 0; i < elem.attributes.length; i++ ) { - const attr = elem.attributes.item(i); - if ( attr.name === 'id' ) { continue; } - if ( attr.name === 'class' ) { continue; } - parts.push(`[${CSS.escape(attr.name)}="${attr.value}"]`); - } - return parts.join(''); - }; - const shouldPrevent = (thisArg, type, handler) => { - const matchesType = safe.RegExp_test.call(reType, type); - const matchesHandler = safe.RegExp_test.call(rePattern, handler); - const matchesEither = matchesType || matchesHandler; - const matchesBoth = matchesType && matchesHandler; - if ( debug === 1 && matchesBoth || debug === 2 && matchesEither ) { - debugger; // eslint-disable-line no-debugger - } - if ( matchesBoth && targetSelector !== undefined ) { - if ( elementMatches(thisArg) === false ) { return false; } - } - return matchesBoth; - }; - const proxyFn = function(context) { - const { callArgs, thisArg } = context; - let t, h; - try { - t = String(callArgs[0]); - if ( typeof callArgs[1] === 'function' ) { - h = String(safe.Function_toString(callArgs[1])); - } else if ( typeof callArgs[1] === 'object' && callArgs[1] !== null ) { - if ( typeof callArgs[1].handleEvent === 'function' ) { - h = String(safe.Function_toString(callArgs[1].handleEvent)); - } - } else { - h = String(callArgs[1]); - } - } catch { - } - if ( type === '' && pattern === '' ) { - safe.uboLog(logPrefix, `Called: ${t}\n${h}\n${elementDetails(thisArg)}`); - } else if ( shouldPrevent(thisArg, t, h) ) { - return safe.uboLog(logPrefix, `Prevented: ${t}\n${h}\n${elementDetails(thisArg)}`); + delete owner[prop]; + Object.defineProperty(owner, prop, { + set: function() { + safe.uboLog(logPrefix, 'Aborted'); + throw new ReferenceError(exceptionToken); } - return context.reflect(); - }; - runAt(( ) => { - proxyApplyFn('EventTarget.prototype.addEventListener', proxyFn); - proxyApplyFn('document.addEventListener', proxyFn); - }, extraArgs.runAt); + }); }; -addEventListenerDefuser(...args); +abortOnPropertyWrite(...args); }, }; @@ -27015,6 +27562,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -27241,6 +27789,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -27467,6 +28016,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -27691,6 +28241,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -27980,6 +28531,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -28276,6 +28828,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -28711,6 +29264,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -29079,6 +29633,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -29465,6 +30020,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -29802,6 +30358,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -30243,6 +30800,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -30498,6 +31056,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -30810,6 +31369,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -31076,6 +31636,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -31389,6 +31950,25 @@ function parsePropertiesToMatchFn(propsToMatch, implicit = '') { } return needles; } +function matchObjectPropertiesFn(propNeedles, ...objs) { + const safe = safeSelf(); + const matched = []; + for ( const obj of objs ) { + if ( obj instanceof Object === false ) { continue; } + for ( const [ prop, details ] of propNeedles ) { + let value = obj[prop]; + if ( value === undefined ) { continue; } + if ( typeof value !== 'string' ) { + try { value = safe.JSON_stringify(value); } + catch { } + if ( typeof value !== 'string' ) { continue; } + } + if ( safe.testPattern(details, value) === false ) { return; } + matched.push(`${prop}: ${value}`); + } + } + return matched; +} function safeSelf() { if ( scriptletGlobals.safeSelf ) { return scriptletGlobals.safeSelf; @@ -31409,6 +31989,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -31578,24 +32159,39 @@ function safeSelf() { } return safe; } -function matchObjectPropertiesFn(propNeedles, ...objs) { +function collateFetchArgumentsFn(resource, options) { const safe = safeSelf(); - const matched = []; - for ( const obj of objs ) { - if ( obj instanceof Object === false ) { continue; } - for ( const [ prop, details ] of propNeedles ) { - let value = obj[prop]; - if ( value === undefined ) { continue; } - if ( typeof value !== 'string' ) { - try { value = safe.JSON_stringify(value); } - catch { } - if ( typeof value !== 'string' ) { continue; } + const props = [ + 'body', 'cache', 'credentials', 'duplex', 'headers', + 'integrity', 'keepalive', 'method', 'mode', 'priority', + 'redirect', 'referrer', 'referrerPolicy', 'url' + ]; + const out = {}; + if ( collateFetchArgumentsFn.collateKnownProps === undefined ) { + collateFetchArgumentsFn.collateKnownProps = (src, out) => { + for ( const prop of props ) { + if ( src[prop] === undefined ) { continue; } + out[prop] = src[prop]; } - if ( safe.testPattern(details, value) === false ) { return; } - matched.push(`${prop}: ${value}`); + }; + } + if ( + typeof resource !== 'object' || + safe.Object_toString.call(resource) !== '[object Request]' + ) { + out.url = `${resource}`; + } else { + let clone; + try { + clone = safe.Request_clone.call(resource); + } catch { } + collateFetchArgumentsFn.collateKnownProps(clone || resource, out); } - return matched; + if ( typeof options === 'object' && options !== null ) { + collateFetchArgumentsFn.collateKnownProps(options, out); + } + return out; } function replaceFetchResponseFn( trusted = false, @@ -31616,19 +32212,8 @@ function replaceFetchResponseFn( const fetchPromise = Reflect.apply(target, thisArg, args); if ( pattern === '' ) { return fetchPromise; } if ( propNeedles.size !== 0 ) { - const objs = [ args[0] instanceof Object ? args[0] : { url: args[0] } ]; - if ( objs[0] instanceof Request ) { - try { - objs[0] = safe.Request_clone.call(objs[0]); - } - catch(ex) { - safe.uboErr(logPrefix, ex); - } - } - if ( args[1] instanceof Object ) { - objs.push(args[1]); - } - const matched = matchObjectPropertiesFn(propNeedles, ...objs); + const props = collateFetchArgumentsFn(...args); + const matched = matchObjectPropertiesFn(propNeedles, props); if ( matched === undefined ) { return fetchPromise; } if ( safe.logLevel > 1 ) { safe.uboLog(logPrefix, `Matched "propsToMatch":\n\t${matched.join('\n\t')}`); @@ -31719,6 +32304,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -32027,6 +32613,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -32418,6 +33005,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -32826,6 +33414,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -33191,6 +33780,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -33626,6 +34216,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec, @@ -34000,6 +34591,7 @@ function safeSelf() { 'Object_fromEntries': Object.fromEntries.bind(Object), 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object), 'Object_hasOwn': Object.hasOwn.bind(Object), + 'Object_toString': Object.prototype.toString, 'RegExp': self.RegExp, 'RegExp_test': self.RegExp.prototype.test, 'RegExp_exec': self.RegExp.prototype.exec,