@@ -20113,7 +20113,7 @@ preventAddEventListener(...args);
2011320113
2011420114scriptlets['prevent-dialog.js'] = {
2011520115aliases: [],
20116-
20116+ world: 'ISOLATED',
2011720117requiresTrust: false,
2011820118func: function (scriptletGlobals = {}, ...args) {
2011920119function safeSelf() {
@@ -21369,7 +21369,7 @@ trustedPreventFetch(...args);
2136921369};
2137021370
2137121371
21372- scriptlets['prevent-innerHTML .js'] = {
21372+ scriptlets['freeze-element-property .js'] = {
2137321373aliases: [],
2137421374
2137521375requiresTrust: false,
@@ -21564,14 +21564,28 @@ function safeSelf() {
2156421564 }
2156521565 return safe;
2156621566}
21567- function preventInnerHTML(
21567+ function freezeElementProperty(
21568+ property = '',
2156821569 selector = '',
2156921570 pattern = ''
2157021571) {
2157121572 const safe = safeSelf();
21572- const logPrefix = safe.makeLogPrefix('prevent-innerHTML' , selector, pattern);
21573+ const logPrefix = safe.makeLogPrefix('freeze-element-property', property , selector, pattern);
2157321574 const matcher = safe.initPattern(pattern, { canNegate: true });
21574- const current = safe.Object_getOwnPropertyDescriptor(Element.prototype, 'innerHTML');
21575+ const owner = (( ) => {
21576+ if ( Object.hasOwn(Element.prototype, property) ) {
21577+ return Element.prototype;
21578+ }
21579+ if ( Object.hasOwn(HTMLElement.prototype, property) ) {
21580+ return HTMLElement.prototype;
21581+ }
21582+ if ( Object.hasOwn(Node.prototype, property) ) {
21583+ return Node.prototype;
21584+ }
21585+ return null;
21586+ })();
21587+ if ( owner === null ) { return; }
21588+ const current = safe.Object_getOwnPropertyDescriptor(owner, property);
2157521589 if ( current === undefined ) { return; }
2157621590 const shouldPreventSet = (elem, a) => {
2157721591 if ( selector !== '' ) {
@@ -21580,15 +21594,15 @@ function preventInnerHTML(
2158021594 }
2158121595 return safe.testPattern(matcher, `${a}`);
2158221596 };
21583- Object.defineProperty(Element.prototype, 'innerHTML' , {
21597+ Object.defineProperty(owner, property , {
2158421598 get: function() {
2158521599 return current.get
2158621600 ? current.get.call(this)
2158721601 : current.value;
2158821602 },
2158921603 set: function(a) {
2159021604 if ( shouldPreventSet(this, a) ) {
21591- safe.uboLog(logPrefix, 'Prevented ');
21605+ safe.uboLog(logPrefix, 'Assignment prevented ');
2159221606 } else if ( current.set ) {
2159321607 current.set.call(this, a);
2159421608 }
@@ -21599,12 +21613,12 @@ function preventInnerHTML(
2159921613 },
2160021614 });
2160121615};
21602- preventInnerHTML (...args);
21616+ freezeElementProperty (...args);
2160321617},
2160421618};
2160521619
2160621620
21607- scriptlets['prevent-textContent .js'] = {
21621+ scriptlets['prevent-innerHTML .js'] = {
2160821622aliases: [],
2160921623
2161021624requiresTrust: false,
@@ -21799,14 +21813,28 @@ function safeSelf() {
2179921813 }
2180021814 return safe;
2180121815}
21802- function preventInnerHTML(
21816+ function freezeElementProperty(
21817+ property = '',
2180321818 selector = '',
2180421819 pattern = ''
2180521820) {
2180621821 const safe = safeSelf();
21807- const logPrefix = safe.makeLogPrefix('prevent-innerHTML' , selector, pattern);
21822+ const logPrefix = safe.makeLogPrefix('freeze-element-property', property , selector, pattern);
2180821823 const matcher = safe.initPattern(pattern, { canNegate: true });
21809- const current = safe.Object_getOwnPropertyDescriptor(Element.prototype, 'innerHTML');
21824+ const owner = (( ) => {
21825+ if ( Object.hasOwn(Element.prototype, property) ) {
21826+ return Element.prototype;
21827+ }
21828+ if ( Object.hasOwn(HTMLElement.prototype, property) ) {
21829+ return HTMLElement.prototype;
21830+ }
21831+ if ( Object.hasOwn(Node.prototype, property) ) {
21832+ return Node.prototype;
21833+ }
21834+ return null;
21835+ })();
21836+ if ( owner === null ) { return; }
21837+ const current = safe.Object_getOwnPropertyDescriptor(owner, property);
2181021838 if ( current === undefined ) { return; }
2181121839 const shouldPreventSet = (elem, a) => {
2181221840 if ( selector !== '' ) {
@@ -21815,15 +21843,15 @@ function preventInnerHTML(
2181521843 }
2181621844 return safe.testPattern(matcher, `${a}`);
2181721845 };
21818- Object.defineProperty(Element.prototype, 'innerHTML' , {
21846+ Object.defineProperty(owner, property , {
2181921847 get: function() {
2182021848 return current.get
2182121849 ? current.get.call(this)
2182221850 : current.value;
2182321851 },
2182421852 set: function(a) {
2182521853 if ( shouldPreventSet(this, a) ) {
21826- safe.uboLog(logPrefix, 'Prevented ');
21854+ safe.uboLog(logPrefix, 'Assignment prevented ');
2182721855 } else if ( current.set ) {
2182821856 current.set.call(this, a);
2182921857 }
@@ -21833,12 +21861,238 @@ function preventInnerHTML(
2183321861 current.value = a;
2183421862 },
2183521863 });
21864+ }
21865+ function preventInnerHTML(
21866+ selector = '',
21867+ pattern = ''
21868+ ) {
21869+ freezeElementProperty('innerHTML', selector, pattern);
2183621870};
2183721871preventInnerHTML(...args);
2183821872},
2183921873};
2184021874
2184121875
21876+ scriptlets['prevent-navigation.js'] = {
21877+ aliases: [],
21878+ world: 'ISOLATED',
21879+ requiresTrust: false,
21880+ func: function (scriptletGlobals = {}, ...args) {
21881+ function safeSelf() {
21882+ if ( scriptletGlobals.safeSelf ) {
21883+ return scriptletGlobals.safeSelf;
21884+ }
21885+ const self = globalThis;
21886+ const safe = {
21887+ 'Array_from': Array.from,
21888+ 'Error': self.Error,
21889+ 'Function_toStringFn': self.Function.prototype.toString,
21890+ 'Function_toString': thisArg => safe.Function_toStringFn.call(thisArg),
21891+ 'Math_floor': Math.floor,
21892+ 'Math_max': Math.max,
21893+ 'Math_min': Math.min,
21894+ 'Math_random': Math.random,
21895+ 'Object': Object,
21896+ 'Object_defineProperty': Object.defineProperty.bind(Object),
21897+ 'Object_defineProperties': Object.defineProperties.bind(Object),
21898+ 'Object_fromEntries': Object.fromEntries.bind(Object),
21899+ 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object),
21900+ 'Object_hasOwn': Object.hasOwn.bind(Object),
21901+ 'Object_toString': Object.prototype.toString,
21902+ 'RegExp': self.RegExp,
21903+ 'RegExp_test': self.RegExp.prototype.test,
21904+ 'RegExp_exec': self.RegExp.prototype.exec,
21905+ 'Request_clone': self.Request.prototype.clone,
21906+ 'String': self.String,
21907+ 'String_fromCharCode': String.fromCharCode,
21908+ 'String_split': String.prototype.split,
21909+ 'XMLHttpRequest': self.XMLHttpRequest,
21910+ 'addEventListener': self.EventTarget.prototype.addEventListener,
21911+ 'removeEventListener': self.EventTarget.prototype.removeEventListener,
21912+ 'fetch': self.fetch,
21913+ 'JSON': self.JSON,
21914+ 'JSON_parseFn': self.JSON.parse,
21915+ 'JSON_stringifyFn': self.JSON.stringify,
21916+ 'JSON_parse': (...args) => safe.JSON_parseFn.call(safe.JSON, ...args),
21917+ 'JSON_stringify': (...args) => safe.JSON_stringifyFn.call(safe.JSON, ...args),
21918+ 'log': console.log.bind(console),
21919+ // Properties
21920+ logLevel: 0,
21921+ // Methods
21922+ makeLogPrefix(...args) {
21923+ return this.sendToLogger && `[${args.join(' \u205D ')}]` || '';
21924+ },
21925+ uboLog(...args) {
21926+ if ( this.sendToLogger === undefined ) { return; }
21927+ if ( args === undefined || args[0] === '' ) { return; }
21928+ return this.sendToLogger('info', ...args);
21929+
21930+ },
21931+ uboErr(...args) {
21932+ if ( this.sendToLogger === undefined ) { return; }
21933+ if ( args === undefined || args[0] === '' ) { return; }
21934+ return this.sendToLogger('error', ...args);
21935+ },
21936+ escapeRegexChars(s) {
21937+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
21938+ },
21939+ initPattern(pattern, options = {}) {
21940+ if ( pattern === '' ) {
21941+ return { matchAll: true, expect: true };
21942+ }
21943+ const expect = (options.canNegate !== true || pattern.startsWith('!') === false);
21944+ if ( expect === false ) {
21945+ pattern = pattern.slice(1);
21946+ }
21947+ const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern);
21948+ if ( match !== null ) {
21949+ return {
21950+ re: new this.RegExp(
21951+ match[1],
21952+ match[2] || options.flags
21953+ ),
21954+ expect,
21955+ };
21956+ }
21957+ if ( options.flags !== undefined ) {
21958+ return {
21959+ re: new this.RegExp(this.escapeRegexChars(pattern),
21960+ options.flags
21961+ ),
21962+ expect,
21963+ };
21964+ }
21965+ return { pattern, expect };
21966+ },
21967+ testPattern(details, haystack) {
21968+ if ( details.matchAll ) { return true; }
21969+ if ( details.re ) {
21970+ return this.RegExp_test.call(details.re, haystack) === details.expect;
21971+ }
21972+ return haystack.includes(details.pattern) === details.expect;
21973+ },
21974+ patternToRegex(pattern, flags = undefined, verbatim = false) {
21975+ if ( pattern === '' ) { return /^/; }
21976+ const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern);
21977+ if ( match === null ) {
21978+ const reStr = this.escapeRegexChars(pattern);
21979+ return new RegExp(verbatim ? `^${reStr}$` : reStr, flags);
21980+ }
21981+ try {
21982+ return new RegExp(match[1], match[2] || undefined);
21983+ }
21984+ catch {
21985+ }
21986+ return /^/;
21987+ },
21988+ getExtraArgs(args, offset = 0) {
21989+ const entries = args.slice(offset).reduce((out, v, i, a) => {
21990+ if ( (i & 1) === 0 ) {
21991+ const rawValue = a[i+1];
21992+ const value = /^\d+$/.test(rawValue)
21993+ ? parseInt(rawValue, 10)
21994+ : rawValue;
21995+ out.push([ a[i], value ]);
21996+ }
21997+ return out;
21998+ }, []);
21999+ return this.Object_fromEntries(entries);
22000+ },
22001+ onIdle(fn, options) {
22002+ if ( self.requestIdleCallback ) {
22003+ return self.requestIdleCallback(fn, options);
22004+ }
22005+ return self.requestAnimationFrame(fn);
22006+ },
22007+ offIdle(id) {
22008+ if ( self.requestIdleCallback ) {
22009+ return self.cancelIdleCallback(id);
22010+ }
22011+ return self.cancelAnimationFrame(id);
22012+ }
22013+ };
22014+ scriptletGlobals.safeSelf = safe;
22015+ if ( scriptletGlobals.bcSecret === undefined ) { return safe; }
22016+ // This is executed only when the logger is opened
22017+ safe.logLevel = scriptletGlobals.logLevel || 1;
22018+ let lastLogType = '';
22019+ let lastLogText = '';
22020+ let lastLogTime = 0;
22021+ safe.toLogText = (type, ...args) => {
22022+ if ( args.length === 0 ) { return; }
22023+ const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`;
22024+ if ( text === lastLogText && type === lastLogType ) {
22025+ if ( (Date.now() - lastLogTime) < 5000 ) { return; }
22026+ }
22027+ lastLogType = type;
22028+ lastLogText = text;
22029+ lastLogTime = Date.now();
22030+ return text;
22031+ };
22032+ try {
22033+ const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret);
22034+ let bcBuffer = [];
22035+ safe.sendToLogger = (type, ...args) => {
22036+ const text = safe.toLogText(type, ...args);
22037+ if ( text === undefined ) { return; }
22038+ if ( bcBuffer === undefined ) {
22039+ return bc.postMessage({ what: 'messageToLogger', type, text });
22040+ }
22041+ bcBuffer.push({ type, text });
22042+ };
22043+ bc.onmessage = ev => {
22044+ const msg = ev.data;
22045+ switch ( msg ) {
22046+ case 'iamready!':
22047+ if ( bcBuffer === undefined ) { break; }
22048+ bcBuffer.forEach(({ type, text }) =>
22049+ bc.postMessage({ what: 'messageToLogger', type, text })
22050+ );
22051+ bcBuffer = undefined;
22052+ break;
22053+ case 'setScriptletLogLevelToOne':
22054+ safe.logLevel = 1;
22055+ break;
22056+ case 'setScriptletLogLevelToTwo':
22057+ safe.logLevel = 2;
22058+ break;
22059+ }
22060+ };
22061+ bc.postMessage('areyouready?');
22062+ } catch {
22063+ safe.sendToLogger = (type, ...args) => {
22064+ const text = safe.toLogText(type, ...args);
22065+ if ( text === undefined ) { return; }
22066+ safe.log(`uBO ${text}`);
22067+ };
22068+ }
22069+ return safe;
22070+ }
22071+ function preventNavigation(
22072+ pattern = ''
22073+ ) {
22074+ const safe = safeSelf();
22075+ const logPrefix = safe.makeLogPrefix('prevent-navigation', pattern);
22076+ const needle = pattern === 'location.href' ? self.location.href : pattern;
22077+ const matcher = safe.initPattern(needle, { canNegate: true });
22078+ self.navigation.addEventListener('navigate', ev => {
22079+ if ( ev.userInitiated ) { return; }
22080+ const { url } = ev.destination;
22081+ if ( pattern === '' ) {
22082+ safe.uboLog(logPrefix, `Navigation to ${url}`);
22083+ return;
22084+ }
22085+ if ( safe.testPattern(matcher, url) ) {
22086+ ev.preventDefault();
22087+ safe.uboLog(logPrefix, `Prevented navigation to ${url}`);
22088+ }
22089+ });
22090+ };
22091+ preventNavigation(...args);
22092+ },
22093+ };
22094+
22095+
2184222096scriptlets['prevent-setTimeout.js'] = {
2184322097aliases: ["no-setTimeout-if.js","nostif.js","setTimeout-defuser.js"],
2184422098
0 commit comments