From 6d2b3375f863d21751794f2d3745c6705600f7f2 Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sat, 5 Oct 2024 12:42:30 -0400 Subject: [PATCH] Mind that BroadcastChannel contructor can throw in Firefox BroadcastChannel constructor throws in Firefox when Enhanced Tracking Protection is set to "strict". This behavior could cause scriptlet injection to wholly break when uBO's logger was opened, as BroadcastChannel() is used by scriptlets to report information to the logger. This commit ensures that exceptions from BroadcastChannel constructor are properly handled. The scriptlets will fall back to report at the console should they be unable to report to the logger through BroadcastChannel. --- assets/resources/scriptlets.js | 67 ++++++++++++++++++++-------------- src/js/scriptlet-filtering.js | 39 +++++++++++--------- 2 files changed, 60 insertions(+), 46 deletions(-) diff --git a/assets/resources/scriptlets.js b/assets/resources/scriptlets.js index 2b188ebef..788354f92 100644 --- a/assets/resources/scriptlets.js +++ b/assets/resources/scriptlets.js @@ -173,13 +173,11 @@ function safeSelf() { scriptletGlobals.safeSelf = safe; if ( scriptletGlobals.bcSecret === undefined ) { return safe; } // This is executed only when the logger is opened - const bc = new self.BroadcastChannel(scriptletGlobals.bcSecret); - let bcBuffer = []; safe.logLevel = scriptletGlobals.logLevel || 1; let lastLogType = ''; let lastLogText = ''; let lastLogTime = 0; - safe.sendToLogger = (type, ...args) => { + safe.toLogText = (type, ...args) => { if ( args.length === 0 ) { return; } const text = `[${document.location.hostname || document.location.href}]${args.join(' ')}`; if ( text === lastLogText && type === lastLogType ) { @@ -188,30 +186,45 @@ function safeSelf() { lastLogType = type; lastLogText = text; lastLogTime = Date.now(); - if ( bcBuffer === undefined ) { - return bc.postMessage({ what: 'messageToLogger', type, text }); - } - bcBuffer.push({ type, text }); + return 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?'); + 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; } + console.log(`uBO${text}`); + }; + } return safe; } @@ -5144,7 +5157,6 @@ function trustedPreventXhr(...args) { } /** - * * @trustedScriptlet trusted-prevent-dom-bypass * * @description @@ -5219,7 +5231,6 @@ function trustedPreventDomBypass( } /** - * * @trustedScriptlet trusted-override-element-method * * @description diff --git a/src/js/scriptlet-filtering.js b/src/js/scriptlet-filtering.js index e221a4197..1cc6a959b 100644 --- a/src/js/scriptlet-filtering.js +++ b/src/js/scriptlet-filtering.js @@ -176,25 +176,28 @@ const onScriptletMessageInjector = (( ) => { '(', function(name) { if ( self.uBO_bcSecret ) { return; } - const bcSecret = new self.BroadcastChannel(name); - bcSecret.onmessage = ev => { - const msg = ev.data; - switch ( typeof msg ) { - case 'string': - if ( msg !== 'areyouready?' ) { break; } - bcSecret.postMessage('iamready!'); - break; - case 'object': - if ( self.vAPI && self.vAPI.messaging ) { - self.vAPI.messaging.send('contentscript', msg); - } else { - console.log(`[uBO][${msg.type}]${msg.text}`); + try { + const bcSecret = new self.BroadcastChannel(name); + bcSecret.onmessage = ev => { + const msg = ev.data; + switch ( typeof msg ) { + case 'string': + if ( msg !== 'areyouready?' ) { break; } + bcSecret.postMessage('iamready!'); + break; + case 'object': + if ( self.vAPI && self.vAPI.messaging ) { + self.vAPI.messaging.send('contentscript', msg); + } else { + console.log(`[uBO][${msg.type}]${msg.text}`); + } + break; } - break; - } - }; - bcSecret.postMessage('iamready!'); - self.uBO_bcSecret = bcSecret; + }; + bcSecret.postMessage('iamready!'); + self.uBO_bcSecret = bcSecret; + } catch(_) { + } }.toString(), ')(', 'bcSecret-slot',