@@ -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,25 @@ 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+ return null;
21583+ })();
21584+ if ( owner === null ) { return; }
21585+ const current = safe.Object_getOwnPropertyDescriptor(owner, property);
2157521586 if ( current === undefined ) { return; }
2157621587 const shouldPreventSet = (elem, a) => {
2157721588 if ( selector !== '' ) {
@@ -21580,15 +21591,15 @@ function preventInnerHTML(
2158021591 }
2158121592 return safe.testPattern(matcher, `${a}`);
2158221593 };
21583- Object.defineProperty(Element.prototype, 'innerHTML' , {
21594+ Object.defineProperty(owner, property , {
2158421595 get: function() {
2158521596 return current.get
2158621597 ? current.get.call(this)
2158721598 : current.value;
2158821599 },
2158921600 set: function(a) {
2159021601 if ( shouldPreventSet(this, a) ) {
21591- safe.uboLog(logPrefix, 'Prevented ');
21602+ safe.uboLog(logPrefix, 'Assignment prevented ');
2159221603 } else if ( current.set ) {
2159321604 current.set.call(this, a);
2159421605 }
@@ -21599,12 +21610,12 @@ function preventInnerHTML(
2159921610 },
2160021611 });
2160121612};
21602- preventInnerHTML (...args);
21613+ freezeElementProperty (...args);
2160321614},
2160421615};
2160521616
2160621617
21607- scriptlets['prevent-textContent .js'] = {
21618+ scriptlets['prevent-innerHTML .js'] = {
2160821619aliases: [],
2160921620
2161021621requiresTrust: false,
@@ -21799,14 +21810,25 @@ function safeSelf() {
2179921810 }
2180021811 return safe;
2180121812}
21802- function preventInnerHTML(
21813+ function freezeElementProperty(
21814+ property = '',
2180321815 selector = '',
2180421816 pattern = ''
2180521817) {
2180621818 const safe = safeSelf();
21807- const logPrefix = safe.makeLogPrefix('prevent-innerHTML' , selector, pattern);
21819+ const logPrefix = safe.makeLogPrefix('freeze-element-property', property , selector, pattern);
2180821820 const matcher = safe.initPattern(pattern, { canNegate: true });
21809- const current = safe.Object_getOwnPropertyDescriptor(Element.prototype, 'innerHTML');
21821+ const owner = (( ) => {
21822+ if ( Object.hasOwn(Element.prototype, property) ) {
21823+ return Element.prototype;
21824+ }
21825+ if ( Object.hasOwn(HTMLElement.prototype, property) ) {
21826+ return HTMLElement.prototype;
21827+ }
21828+ return null;
21829+ })();
21830+ if ( owner === null ) { return; }
21831+ const current = safe.Object_getOwnPropertyDescriptor(owner, property);
2181021832 if ( current === undefined ) { return; }
2181121833 const shouldPreventSet = (elem, a) => {
2181221834 if ( selector !== '' ) {
@@ -21815,15 +21837,15 @@ function preventInnerHTML(
2181521837 }
2181621838 return safe.testPattern(matcher, `${a}`);
2181721839 };
21818- Object.defineProperty(Element.prototype, 'innerHTML' , {
21840+ Object.defineProperty(owner, property , {
2181921841 get: function() {
2182021842 return current.get
2182121843 ? current.get.call(this)
2182221844 : current.value;
2182321845 },
2182421846 set: function(a) {
2182521847 if ( shouldPreventSet(this, a) ) {
21826- safe.uboLog(logPrefix, 'Prevented ');
21848+ safe.uboLog(logPrefix, 'Assignment prevented ');
2182721849 } else if ( current.set ) {
2182821850 current.set.call(this, a);
2182921851 }
@@ -21833,12 +21855,238 @@ function preventInnerHTML(
2183321855 current.value = a;
2183421856 },
2183521857 });
21858+ }
21859+ function preventInnerHTML(
21860+ selector = '',
21861+ pattern = ''
21862+ ) {
21863+ freezeElementProperty('innerHTML', selector, pattern);
2183621864};
2183721865preventInnerHTML(...args);
2183821866},
2183921867};
2184021868
2184121869
21870+ scriptlets['prevent-navigation.js'] = {
21871+ aliases: [],
21872+ world: 'ISOLATED',
21873+ requiresTrust: false,
21874+ func: function (scriptletGlobals = {}, ...args) {
21875+ function safeSelf() {
21876+ if ( scriptletGlobals.safeSelf ) {
21877+ return scriptletGlobals.safeSelf;
21878+ }
21879+ const self = globalThis;
21880+ const safe = {
21881+ 'Array_from': Array.from,
21882+ 'Error': self.Error,
21883+ 'Function_toStringFn': self.Function.prototype.toString,
21884+ 'Function_toString': thisArg => safe.Function_toStringFn.call(thisArg),
21885+ 'Math_floor': Math.floor,
21886+ 'Math_max': Math.max,
21887+ 'Math_min': Math.min,
21888+ 'Math_random': Math.random,
21889+ 'Object': Object,
21890+ 'Object_defineProperty': Object.defineProperty.bind(Object),
21891+ 'Object_defineProperties': Object.defineProperties.bind(Object),
21892+ 'Object_fromEntries': Object.fromEntries.bind(Object),
21893+ 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object),
21894+ 'Object_hasOwn': Object.hasOwn.bind(Object),
21895+ 'Object_toString': Object.prototype.toString,
21896+ 'RegExp': self.RegExp,
21897+ 'RegExp_test': self.RegExp.prototype.test,
21898+ 'RegExp_exec': self.RegExp.prototype.exec,
21899+ 'Request_clone': self.Request.prototype.clone,
21900+ 'String': self.String,
21901+ 'String_fromCharCode': String.fromCharCode,
21902+ 'String_split': String.prototype.split,
21903+ 'XMLHttpRequest': self.XMLHttpRequest,
21904+ 'addEventListener': self.EventTarget.prototype.addEventListener,
21905+ 'removeEventListener': self.EventTarget.prototype.removeEventListener,
21906+ 'fetch': self.fetch,
21907+ 'JSON': self.JSON,
21908+ 'JSON_parseFn': self.JSON.parse,
21909+ 'JSON_stringifyFn': self.JSON.stringify,
21910+ 'JSON_parse': (...args) => safe.JSON_parseFn.call(safe.JSON, ...args),
21911+ 'JSON_stringify': (...args) => safe.JSON_stringifyFn.call(safe.JSON, ...args),
21912+ 'log': console.log.bind(console),
21913+ // Properties
21914+ logLevel: 0,
21915+ // Methods
21916+ makeLogPrefix(...args) {
21917+ return this.sendToLogger && `[${args.join(' \u205D ')}]` || '';
21918+ },
21919+ uboLog(...args) {
21920+ if ( this.sendToLogger === undefined ) { return; }
21921+ if ( args === undefined || args[0] === '' ) { return; }
21922+ return this.sendToLogger('info', ...args);
21923+
21924+ },
21925+ uboErr(...args) {
21926+ if ( this.sendToLogger === undefined ) { return; }
21927+ if ( args === undefined || args[0] === '' ) { return; }
21928+ return this.sendToLogger('error', ...args);
21929+ },
21930+ escapeRegexChars(s) {
21931+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
21932+ },
21933+ initPattern(pattern, options = {}) {
21934+ if ( pattern === '' ) {
21935+ return { matchAll: true, expect: true };
21936+ }
21937+ const expect = (options.canNegate !== true || pattern.startsWith('!') === false);
21938+ if ( expect === false ) {
21939+ pattern = pattern.slice(1);
21940+ }
21941+ const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern);
21942+ if ( match !== null ) {
21943+ return {
21944+ re: new this.RegExp(
21945+ match[1],
21946+ match[2] || options.flags
21947+ ),
21948+ expect,
21949+ };
21950+ }
21951+ if ( options.flags !== undefined ) {
21952+ return {
21953+ re: new this.RegExp(this.escapeRegexChars(pattern),
21954+ options.flags
21955+ ),
21956+ expect,
21957+ };
21958+ }
21959+ return { pattern, expect };
21960+ },
21961+ testPattern(details, haystack) {
21962+ if ( details.matchAll ) { return true; }
21963+ if ( details.re ) {
21964+ return this.RegExp_test.call(details.re, haystack) === details.expect;
21965+ }
21966+ return haystack.includes(details.pattern) === details.expect;
21967+ },
21968+ patternToRegex(pattern, flags = undefined, verbatim = false) {
21969+ if ( pattern === '' ) { return /^/; }
21970+ const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern);
21971+ if ( match === null ) {
21972+ const reStr = this.escapeRegexChars(pattern);
21973+ return new RegExp(verbatim ? `^${reStr}$` : reStr, flags);
21974+ }
21975+ try {
21976+ return new RegExp(match[1], match[2] || undefined);
21977+ }
21978+ catch {
21979+ }
21980+ return /^/;
21981+ },
21982+ getExtraArgs(args, offset = 0) {
21983+ const entries = args.slice(offset).reduce((out, v, i, a) => {
21984+ if ( (i & 1) === 0 ) {
21985+ const rawValue = a[i+1];
21986+ const value = /^\d+$/.test(rawValue)
21987+ ? parseInt(rawValue, 10)
21988+ : rawValue;
21989+ out.push([ a[i], value ]);
21990+ }
21991+ return out;
21992+ }, []);
21993+ return this.Object_fromEntries(entries);
21994+ },
21995+ onIdle(fn, options) {
21996+ if ( self.requestIdleCallback ) {
21997+ return self.requestIdleCallback(fn, options);
21998+ }
21999+ return self.requestAnimationFrame(fn);
22000+ },
22001+ offIdle(id) {
22002+ if ( self.requestIdleCallback ) {
22003+ return self.cancelIdleCallback(id);
22004+ }
22005+ return self.cancelAnimationFrame(id);
22006+ }
22007+ };
22008+ scriptletGlobals.safeSelf = safe;
22009+ if ( scriptletGlobals.bcSecret === undefined ) { return safe; }
22010+ // This is executed only when the logger is opened
22011+ safe.logLevel = scriptletGlobals.logLevel || 1;
22012+ let lastLogType = '';
22013+ let lastLogText = '';
22014+ let lastLogTime = 0;
22015+ safe.toLogText = (type, ...args) => {
22016+ if ( args.length === 0 ) { return; }
22017+ const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`;
22018+ if ( text === lastLogText && type === lastLogType ) {
22019+ if ( (Date.now() - lastLogTime) < 5000 ) { return; }
22020+ }
22021+ lastLogType = type;
22022+ lastLogText = text;
22023+ lastLogTime = Date.now();
22024+ return text;
22025+ };
22026+ try {
22027+ const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret);
22028+ let bcBuffer = [];
22029+ safe.sendToLogger = (type, ...args) => {
22030+ const text = safe.toLogText(type, ...args);
22031+ if ( text === undefined ) { return; }
22032+ if ( bcBuffer === undefined ) {
22033+ return bc.postMessage({ what: 'messageToLogger', type, text });
22034+ }
22035+ bcBuffer.push({ type, text });
22036+ };
22037+ bc.onmessage = ev => {
22038+ const msg = ev.data;
22039+ switch ( msg ) {
22040+ case 'iamready!':
22041+ if ( bcBuffer === undefined ) { break; }
22042+ bcBuffer.forEach(({ type, text }) =>
22043+ bc.postMessage({ what: 'messageToLogger', type, text })
22044+ );
22045+ bcBuffer = undefined;
22046+ break;
22047+ case 'setScriptletLogLevelToOne':
22048+ safe.logLevel = 1;
22049+ break;
22050+ case 'setScriptletLogLevelToTwo':
22051+ safe.logLevel = 2;
22052+ break;
22053+ }
22054+ };
22055+ bc.postMessage('areyouready?');
22056+ } catch {
22057+ safe.sendToLogger = (type, ...args) => {
22058+ const text = safe.toLogText(type, ...args);
22059+ if ( text === undefined ) { return; }
22060+ safe.log(`uBO ${text}`);
22061+ };
22062+ }
22063+ return safe;
22064+ }
22065+ function preventNavigation(
22066+ pattern = ''
22067+ ) {
22068+ const safe = safeSelf();
22069+ const logPrefix = safe.makeLogPrefix('prevent-navigation', pattern);
22070+ const needle = pattern === 'location.href' ? self.location.href : pattern;
22071+ const matcher = safe.initPattern(needle, { canNegate: true });
22072+ self.navigation.addEventListener('navigate', ev => {
22073+ if ( ev.userInitiated ) { return; }
22074+ const { url } = ev.destination;
22075+ if ( pattern === '' ) {
22076+ safe.uboLog(logPrefix, `Navigation to ${url}`);
22077+ return;
22078+ }
22079+ if ( safe.testPattern(matcher, url) ) {
22080+ ev.preventDefault();
22081+ safe.uboLog(logPrefix, `Prevented navigation to ${url}`);
22082+ }
22083+ });
22084+ };
22085+ preventNavigation(...args);
22086+ },
22087+ };
22088+
22089+
2184222090scriptlets['prevent-setTimeout.js'] = {
2184322091aliases: ["no-setTimeout-if.js","nostif.js","setTimeout-defuser.js"],
2184422092
0 commit comments