@@ -1184,7 +1184,7 @@ function trustedCreateHTML(
11841184 // We do not want to recursively create elements
11851185 self.trustedCreateHTML = true;
11861186 let ancestor = self.frameElement;
1187- while ( ancestor !== null ) {
1187+ while ( ancestor ) {
11881188 const doc = ancestor.ownerDocument;
11891189 if ( doc === null ) { break; }
11901190 const win = doc.defaultView;
@@ -21604,6 +21604,241 @@ preventInnerHTML(...args);
2160421604};
2160521605
2160621606
21607+ scriptlets['prevent-textContent.js'] = {
21608+ aliases: [],
21609+
21610+ requiresTrust: false,
21611+ func: function (scriptletGlobals = {}, ...args) {
21612+ function safeSelf() {
21613+ if ( scriptletGlobals.safeSelf ) {
21614+ return scriptletGlobals.safeSelf;
21615+ }
21616+ const self = globalThis;
21617+ const safe = {
21618+ 'Array_from': Array.from,
21619+ 'Error': self.Error,
21620+ 'Function_toStringFn': self.Function.prototype.toString,
21621+ 'Function_toString': thisArg => safe.Function_toStringFn.call(thisArg),
21622+ 'Math_floor': Math.floor,
21623+ 'Math_max': Math.max,
21624+ 'Math_min': Math.min,
21625+ 'Math_random': Math.random,
21626+ 'Object': Object,
21627+ 'Object_defineProperty': Object.defineProperty.bind(Object),
21628+ 'Object_defineProperties': Object.defineProperties.bind(Object),
21629+ 'Object_fromEntries': Object.fromEntries.bind(Object),
21630+ 'Object_getOwnPropertyDescriptor': Object.getOwnPropertyDescriptor.bind(Object),
21631+ 'Object_hasOwn': Object.hasOwn.bind(Object),
21632+ 'Object_toString': Object.prototype.toString,
21633+ 'RegExp': self.RegExp,
21634+ 'RegExp_test': self.RegExp.prototype.test,
21635+ 'RegExp_exec': self.RegExp.prototype.exec,
21636+ 'Request_clone': self.Request.prototype.clone,
21637+ 'String': self.String,
21638+ 'String_fromCharCode': String.fromCharCode,
21639+ 'String_split': String.prototype.split,
21640+ 'XMLHttpRequest': self.XMLHttpRequest,
21641+ 'addEventListener': self.EventTarget.prototype.addEventListener,
21642+ 'removeEventListener': self.EventTarget.prototype.removeEventListener,
21643+ 'fetch': self.fetch,
21644+ 'JSON': self.JSON,
21645+ 'JSON_parseFn': self.JSON.parse,
21646+ 'JSON_stringifyFn': self.JSON.stringify,
21647+ 'JSON_parse': (...args) => safe.JSON_parseFn.call(safe.JSON, ...args),
21648+ 'JSON_stringify': (...args) => safe.JSON_stringifyFn.call(safe.JSON, ...args),
21649+ 'log': console.log.bind(console),
21650+ // Properties
21651+ logLevel: 0,
21652+ // Methods
21653+ makeLogPrefix(...args) {
21654+ return this.sendToLogger && `[${args.join(' \u205D ')}]` || '';
21655+ },
21656+ uboLog(...args) {
21657+ if ( this.sendToLogger === undefined ) { return; }
21658+ if ( args === undefined || args[0] === '' ) { return; }
21659+ return this.sendToLogger('info', ...args);
21660+
21661+ },
21662+ uboErr(...args) {
21663+ if ( this.sendToLogger === undefined ) { return; }
21664+ if ( args === undefined || args[0] === '' ) { return; }
21665+ return this.sendToLogger('error', ...args);
21666+ },
21667+ escapeRegexChars(s) {
21668+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
21669+ },
21670+ initPattern(pattern, options = {}) {
21671+ if ( pattern === '' ) {
21672+ return { matchAll: true, expect: true };
21673+ }
21674+ const expect = (options.canNegate !== true || pattern.startsWith('!') === false);
21675+ if ( expect === false ) {
21676+ pattern = pattern.slice(1);
21677+ }
21678+ const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern);
21679+ if ( match !== null ) {
21680+ return {
21681+ re: new this.RegExp(
21682+ match[1],
21683+ match[2] || options.flags
21684+ ),
21685+ expect,
21686+ };
21687+ }
21688+ if ( options.flags !== undefined ) {
21689+ return {
21690+ re: new this.RegExp(this.escapeRegexChars(pattern),
21691+ options.flags
21692+ ),
21693+ expect,
21694+ };
21695+ }
21696+ return { pattern, expect };
21697+ },
21698+ testPattern(details, haystack) {
21699+ if ( details.matchAll ) { return true; }
21700+ if ( details.re ) {
21701+ return this.RegExp_test.call(details.re, haystack) === details.expect;
21702+ }
21703+ return haystack.includes(details.pattern) === details.expect;
21704+ },
21705+ patternToRegex(pattern, flags = undefined, verbatim = false) {
21706+ if ( pattern === '' ) { return /^/; }
21707+ const match = /^\/(.+)\/([gimsu]*)$/.exec(pattern);
21708+ if ( match === null ) {
21709+ const reStr = this.escapeRegexChars(pattern);
21710+ return new RegExp(verbatim ? `^${reStr}$` : reStr, flags);
21711+ }
21712+ try {
21713+ return new RegExp(match[1], match[2] || undefined);
21714+ }
21715+ catch {
21716+ }
21717+ return /^/;
21718+ },
21719+ getExtraArgs(args, offset = 0) {
21720+ const entries = args.slice(offset).reduce((out, v, i, a) => {
21721+ if ( (i & 1) === 0 ) {
21722+ const rawValue = a[i+1];
21723+ const value = /^\d+$/.test(rawValue)
21724+ ? parseInt(rawValue, 10)
21725+ : rawValue;
21726+ out.push([ a[i], value ]);
21727+ }
21728+ return out;
21729+ }, []);
21730+ return this.Object_fromEntries(entries);
21731+ },
21732+ onIdle(fn, options) {
21733+ if ( self.requestIdleCallback ) {
21734+ return self.requestIdleCallback(fn, options);
21735+ }
21736+ return self.requestAnimationFrame(fn);
21737+ },
21738+ offIdle(id) {
21739+ if ( self.requestIdleCallback ) {
21740+ return self.cancelIdleCallback(id);
21741+ }
21742+ return self.cancelAnimationFrame(id);
21743+ }
21744+ };
21745+ scriptletGlobals.safeSelf = safe;
21746+ if ( scriptletGlobals.bcSecret === undefined ) { return safe; }
21747+ // This is executed only when the logger is opened
21748+ safe.logLevel = scriptletGlobals.logLevel || 1;
21749+ let lastLogType = '';
21750+ let lastLogText = '';
21751+ let lastLogTime = 0;
21752+ safe.toLogText = (type, ...args) => {
21753+ if ( args.length === 0 ) { return; }
21754+ const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`;
21755+ if ( text === lastLogText && type === lastLogType ) {
21756+ if ( (Date.now() - lastLogTime) < 5000 ) { return; }
21757+ }
21758+ lastLogType = type;
21759+ lastLogText = text;
21760+ lastLogTime = Date.now();
21761+ return text;
21762+ };
21763+ try {
21764+ const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret);
21765+ let bcBuffer = [];
21766+ safe.sendToLogger = (type, ...args) => {
21767+ const text = safe.toLogText(type, ...args);
21768+ if ( text === undefined ) { return; }
21769+ if ( bcBuffer === undefined ) {
21770+ return bc.postMessage({ what: 'messageToLogger', type, text });
21771+ }
21772+ bcBuffer.push({ type, text });
21773+ };
21774+ bc.onmessage = ev => {
21775+ const msg = ev.data;
21776+ switch ( msg ) {
21777+ case 'iamready!':
21778+ if ( bcBuffer === undefined ) { break; }
21779+ bcBuffer.forEach(({ type, text }) =>
21780+ bc.postMessage({ what: 'messageToLogger', type, text })
21781+ );
21782+ bcBuffer = undefined;
21783+ break;
21784+ case 'setScriptletLogLevelToOne':
21785+ safe.logLevel = 1;
21786+ break;
21787+ case 'setScriptletLogLevelToTwo':
21788+ safe.logLevel = 2;
21789+ break;
21790+ }
21791+ };
21792+ bc.postMessage('areyouready?');
21793+ } catch {
21794+ safe.sendToLogger = (type, ...args) => {
21795+ const text = safe.toLogText(type, ...args);
21796+ if ( text === undefined ) { return; }
21797+ safe.log(`uBO ${text}`);
21798+ };
21799+ }
21800+ return safe;
21801+ }
21802+ function preventInnerHTML(
21803+ selector = '',
21804+ pattern = ''
21805+ ) {
21806+ const safe = safeSelf();
21807+ const logPrefix = safe.makeLogPrefix('prevent-innerHTML', selector, pattern);
21808+ const matcher = safe.initPattern(pattern, { canNegate: true });
21809+ const current = safe.Object_getOwnPropertyDescriptor(Element.prototype, 'innerHTML');
21810+ if ( current === undefined ) { return; }
21811+ const shouldPreventSet = (elem, a) => {
21812+ if ( selector !== '' ) {
21813+ if ( typeof elem.matches !== 'function' ) { return false; }
21814+ if ( elem.matches(selector) === false ) { return false; }
21815+ }
21816+ return safe.testPattern(matcher, `${a}`);
21817+ };
21818+ Object.defineProperty(Element.prototype, 'innerHTML', {
21819+ get: function() {
21820+ return current.get
21821+ ? current.get.call(this)
21822+ : current.value;
21823+ },
21824+ set: function(a) {
21825+ if ( shouldPreventSet(this, a) ) {
21826+ safe.uboLog(logPrefix, 'Prevented');
21827+ } else if ( current.set ) {
21828+ current.set.call(this, a);
21829+ }
21830+ if ( safe.logLevel > 1 ) {
21831+ safe.uboLog(logPrefix, `Assigned:\n${a}`);
21832+ }
21833+ current.value = a;
21834+ },
21835+ });
21836+ };
21837+ preventInnerHTML(...args);
21838+ },
21839+ };
21840+
21841+
2160721842scriptlets['prevent-setTimeout.js'] = {
2160821843aliases: ["no-setTimeout-if.js","nostif.js","setTimeout-defuser.js"],
2160921844
0 commit comments