From 28ea00fd11b1204f359c256d998c2e04328ec54b Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Mon, 11 Aug 2025 17:53:59 -0400 Subject: [PATCH] [mv3] Share console error in troubleshooting information This shoould help investigating issues, especially with mobile devices, where the browser dev tools console is not available. --- platform/mv3/extension/js/background.js | 19 +++-- platform/mv3/extension/js/dashboard.js | 14 ++-- platform/mv3/extension/js/debug.js | 69 ++++++++++++++++++- platform/mv3/extension/js/fetch.js | 4 +- platform/mv3/extension/js/filter-manager.js | 10 +-- platform/mv3/extension/js/report.js | 6 +- platform/mv3/extension/js/ruleset-manager.js | 12 ++-- .../mv3/extension/js/scripting-manager.js | 14 ++-- platform/mv3/extension/js/settings.js | 8 +-- platform/mv3/extension/js/troubleshooting.js | 30 +++----- 10 files changed, 126 insertions(+), 60 deletions(-) diff --git a/platform/mv3/extension/js/background.js b/platform/mv3/extension/js/background.js index adf0e649c..bf316e925 100644 --- a/platform/mv3/extension/js/background.js +++ b/platform/mv3/extension/js/background.js @@ -79,6 +79,7 @@ import { getMatchedRules, isSideloaded, toggleDeveloperMode, + ubolErr, ubolLog, } from './debug.js'; @@ -90,6 +91,7 @@ import { } from './config.js'; import { dnr } from './ext-compat.js'; +import { getTroubleshootingInfo } from './troubleshooting.js'; import { registerInjectables } from './scripting-manager.js'; import { toggleToolbarIcon } from './action.js'; @@ -189,7 +191,7 @@ function onMessage(request, sender, callback) { origin: 'USER', target: { tabId, frameIds: [ frameId ] }, }).catch(reason => { - console.log(reason); + ubolErr(reason); }); return false; } @@ -201,7 +203,7 @@ function onMessage(request, sender, callback) { origin: 'USER', target: { tabId, frameIds: [ frameId ] }, }).catch(reason => { - console.log(reason); + ubolErr(reason); }); return false; } @@ -240,7 +242,7 @@ function onMessage(request, sender, callback) { target: { tabId, frameIds: [ frameId ] }, injectImmediately: true, }).catch(reason => { - console.log(reason); + ubolErr(reason); }).then(( ) => { callback(); }); @@ -271,8 +273,9 @@ function onMessage(request, sender, callback) { return registerInjectables(); }).then(( ) => { callback(result); - broadcastMessage({ enabledRulesets: result.enabledRulesets }); }); + }).finally(( ) => { + broadcastMessage({ enabledRulesets: rulesetConfig.enabledRulesets }); }); return true; } @@ -506,6 +509,12 @@ function onMessage(request, sender, callback) { }); return true; + case 'getTroubleshootingInfo': + getTroubleshootingInfo(request.siteMode).then(info => { + callback(info); + }); + return true; + default: break; } @@ -639,7 +648,7 @@ const isFullyInitialized = start().then(( ) => { localRemove('goodStart'); return false; }).catch(reason => { - console.trace(reason); + ubolErr(reason); if ( process.wakeupRun ) { return; } return localRead('goodStart').then(goodStart => { if ( goodStart === false ) { diff --git a/platform/mv3/extension/js/dashboard.js b/platform/mv3/extension/js/dashboard.js index 113d7481f..16a44c831 100644 --- a/platform/mv3/extension/js/dashboard.js +++ b/platform/mv3/extension/js/dashboard.js @@ -24,11 +24,10 @@ import { localRead, localRemove, localWrite, + runtime, + sendMessage, } from './ext.js'; -import { getTroubleshootingInfo } from './troubleshooting.js'; -import { runtime } from './ext.js'; - /******************************************************************************/ { @@ -53,9 +52,14 @@ localRead('dashboard.activePane').then(pane => { dom.body.dataset.pane = pane; }); -getTroubleshootingInfo().then(config => { - qs$('[data-i18n="supportS5H"] + pre').textContent = config; +// Update troubleshooting on-demand +const tsinfoObserver = new IntersectionObserver(entries => { + if ( entries.every(a => a.isIntersecting === false) ) { return; } + sendMessage({ what: 'getTroubleshootingInfo' }).then(config => { + qs$('[data-i18n="supportS5H"] + pre').textContent = config; + }); }); +tsinfoObserver.observe(qs$('[data-i18n="supportS5H"] + pre')); /******************************************************************************/ diff --git a/platform/mv3/extension/js/debug.js b/platform/mv3/extension/js/debug.js index e57781327..8a299b6e7 100644 --- a/platform/mv3/extension/js/debug.js +++ b/platform/mv3/extension/js/debug.js @@ -19,26 +19,89 @@ Home: https://github.com/gorhill/uBlock */ -import { dnr, normalizeDNRRules } from './ext-compat.js'; -import { browser } from './ext.js'; +import { + dnr, + normalizeDNRRules, + webext, +} from './ext-compat.js'; + +import { + sessionRead, + sessionWrite, +} from './ext.js'; /******************************************************************************/ const isModern = dnr.onRuleMatchedDebug instanceof Object; export const isSideloaded = (( ) => { - const { permissions } = browser.runtime.getManifest(); + const { permissions } = webext.runtime.getManifest(); return permissions?.includes('declarativeNetRequestFeedback') ?? false; })(); /******************************************************************************/ +const CONSOLE_MAX_LINES = 32; +const consoleOutput = []; +let consoleWritePtr = 0; + +sessionRead('console').then(before => { + if ( Array.isArray(before) === false ) { return; } + const current = getConsoleOutput(); + const merged = [ ...before, ...current ].slice(-CONSOLE_MAX_LINES); + for ( let i = 0; i < merged.length; i++ ) { + consoleOutput[i] = merged[i]; + } + consoleWritePtr = merged.length % CONSOLE_MAX_LINES; +}); + +const consoleAdd = (...args) => { + if ( args.length === 0 ) { return; } + const now = new Date(); + const time = [ + `${now.getUTCMonth()+1}`.padStart(2, '0'), + `${now.getUTCDate()}`.padStart(2, '0'), + '.', + `${now.getUTCHours()}`.padStart(2, '0'), + `${now.getUTCMinutes()}`.padStart(2, '0'), + ].join(''); + for ( let i = 0; i < args.length; i++ ) { + const s = `[${time}]${args[i]}`; + if ( Boolean(s) === false ) { continue; } + consoleOutput[consoleWritePtr++] = s; + consoleWritePtr %= CONSOLE_MAX_LINES; + } + sessionWrite('console', getConsoleOutput()); +} + export const ubolLog = (...args) => { // Do not pollute dev console in stable releases. if ( isSideloaded !== true ) { return; } console.info('[uBOL]', ...args); }; +export const ubolErr = (...args) => { + if ( Array.isArray(args) === false ) { return; } + if ( globalThis.ServiceWorkerGlobalScope ) { + consoleAdd(...args); + } + // Do not pollute dev console in stable releases. + if ( isSideloaded !== true ) { return; } + console.error('[uBOL]', ...args); +}; + +export const getConsoleOutput = ( ) => { + return [ + ...consoleOutput.slice(consoleWritePtr), + ...consoleOutput.slice(0, consoleWritePtr), + ].reduce((acc, val) => { + if ( val !== acc.at(-1) ) { + acc.push(val); + } + return acc; + }, []); +}; + /******************************************************************************/ const rulesets = new Map(); diff --git a/platform/mv3/extension/js/fetch.js b/platform/mv3/extension/js/fetch.js index 675a37eba..3c44ae102 100644 --- a/platform/mv3/extension/js/fetch.js +++ b/platform/mv3/extension/js/fetch.js @@ -19,13 +19,15 @@ Home: https://github.com/gorhill/uBlock */ +import { ubolErr } from './debug.js'; + /******************************************************************************/ function fetchJSON(path) { return fetch(`${path}.json`).then(response => response.json() ).catch(reason => { - console.info(reason); + ubolErr(reason); }); } diff --git a/platform/mv3/extension/js/filter-manager.js b/platform/mv3/extension/js/filter-manager.js index 8f734af0d..52839cc64 100644 --- a/platform/mv3/extension/js/filter-manager.js +++ b/platform/mv3/extension/js/filter-manager.js @@ -34,6 +34,8 @@ import { subtractHostnameIters, } from './utils.js'; +import { ubolErr } from './debug.js'; + /******************************************************************************/ export async function selectorsFromCustomFilters(hostname) { @@ -79,7 +81,7 @@ export function startCustomFilters(tabId, frameId) { target: { tabId, frameIds: [ frameId ] }, injectImmediately: true, }).catch(reason => { - console.log(reason); + ubolErr(reason); }) } @@ -89,7 +91,7 @@ export function terminateCustomFilters(tabId, frameId) { target: { tabId, frameIds: [ frameId ] }, injectImmediately: true, }).catch(reason => { - console.log(reason); + ubolErr(reason); }) } @@ -107,7 +109,7 @@ export async function injectCustomFilters(tabId, frameId, hostname) { origin: 'USER', target: { tabId, frameIds: [ frameId ] }, }).catch(reason => { - console.log(reason); + ubolErr(reason); }) ); } @@ -119,7 +121,7 @@ export async function injectCustomFilters(tabId, frameId, hostname) { target: { tabId, frameIds: [ frameId ] }, injectImmediately: true, }).catch(reason => { - console.log(reason); + ubolErr(reason); }) ); } diff --git a/platform/mv3/extension/js/report.js b/platform/mv3/extension/js/report.js index 3f5f50e07..5b00da252 100644 --- a/platform/mv3/extension/js/report.js +++ b/platform/mv3/extension/js/report.js @@ -20,7 +20,6 @@ */ import { dom, qs$ } from './dom.js'; -import { getTroubleshootingInfo } from './troubleshooting.js'; import { sendMessage } from './ext.js'; /******************************************************************************/ @@ -93,7 +92,10 @@ async function reportSpecificFilterIssue() { /******************************************************************************/ -getTroubleshootingInfo(reportedPage.mode).then(config => { +sendMessage({ + what: 'getTroubleshootingInfo', + siteMode: reportedPage.mode, +}).then(config => { qs$('[data-i18n="supportS5H"] + pre').textContent = config; dom.on('[data-url]', 'click', ev => { diff --git a/platform/mv3/extension/js/ruleset-manager.js b/platform/mv3/extension/js/ruleset-manager.js index e81172811..ee33fdfb5 100644 --- a/platform/mv3/extension/js/ruleset-manager.js +++ b/platform/mv3/extension/js/ruleset-manager.js @@ -36,7 +36,7 @@ import { fetchJSON } from './fetch.js'; import { getAdminRulesets } from './admin.js'; import { hasBroadHostPermissions } from './utils.js'; import { rulesFromText } from './dnr-parser.js'; -import { ubolLog } from './debug.js'; +import { ubolErr, ubolLog } from './debug.js'; /******************************************************************************/ @@ -318,7 +318,7 @@ async function updateDynamicRules() { ubolLog(`Add ${addRules.length} dynamic DNR rules`); } } catch(reason) { - console.error(`updateDynamicRules() / ${reason}`); + ubolErr(`updateDynamicRules() / ${reason}`); response.error = `${reason}`; } @@ -472,7 +472,7 @@ async function updateSessionRules() { ubolLog(`Add ${addRules.length} session DNR rules`); } } catch(reason) { - console.error(`updateSessionRules() / ${reason}`); + ubolErr(`updateSessionRules() / ${reason}`); response.error = `${reason}`; } return response; @@ -667,7 +667,7 @@ async function enableRulesets(ids) { enableRulesetIds, disableRulesetIds, }).catch(reason => { - ubolLog(reason); + ubolErr(reason); response.error = `${reason}`; }); @@ -684,7 +684,7 @@ async function enableRulesets(ids) { ubolLog(`Available static rule count: ${count}`); response.staticRuleCount = count; }).catch(reason => { - ubolLog(reason); + ubolErr(reason); }); return response; @@ -781,7 +781,7 @@ async function updateUserRules() { out.added = addRules.length; out.removed = removeRuleIds.length; } catch(reason) { - console.info(`updateUserRules() / ${reason}`); + ubolErr(`updateUserRules() / ${reason}`); out.errors.push(`${reason}`); } finally { const userRules = await getEffectiveUserRules(); diff --git a/platform/mv3/extension/js/scripting-manager.js b/platform/mv3/extension/js/scripting-manager.js index c7c9773d6..58d7161cd 100644 --- a/platform/mv3/extension/js/scripting-manager.js +++ b/platform/mv3/extension/js/scripting-manager.js @@ -21,18 +21,14 @@ import * as ut from './utils.js'; -import { - browser, - localRemove, - localWrite, -} from './ext.js'; +import { browser, localRemove } from './ext.js'; +import { ubolErr, ubolLog } from './debug.js'; import { fetchJSON } from './fetch.js'; import { getEnabledRulesetsDetails } from './ruleset-manager.js'; import { getFilteringModeDetails } from './mode-manager.js'; import { registerCustomFilters } from './filter-manager.js'; import { registerToolbarIconToggler } from './action.js'; -import { ubolLog } from './debug.js'; /******************************************************************************/ @@ -555,8 +551,7 @@ async function registerInjectables() { await browser.scripting.unregisterContentScripts({ ids: toRemove }); localRemove('$scripting.unregisterContentScripts'); } catch(reason) { - localWrite('$scripting.unregisterContentScripts', `${reason}`); - console.info(reason); + ubolErr(reason); } } @@ -566,8 +561,7 @@ async function registerInjectables() { await browser.scripting.registerContentScripts(toAdd); localRemove('$scripting.registerContentScripts'); } catch(reason) { - localWrite('$scripting.registerContentScripts', `${reason}`); - console.info(reason); + ubolErr(reason); } } diff --git a/platform/mv3/extension/js/settings.js b/platform/mv3/extension/js/settings.js index 254e321d7..c9e244948 100644 --- a/platform/mv3/extension/js/settings.js +++ b/platform/mv3/extension/js/settings.js @@ -222,10 +222,8 @@ listen.onmessage = ev => { } if ( message.enabledRulesets !== undefined ) { - if ( hashFromIterable(message.enabledRulesets) !== hashFromIterable(local.enabledRulesets) ) { - local.enabledRulesets = message.enabledRulesets; - render = true; - } + local.enabledRulesets = message.enabledRulesets; + render = true; } if ( render === false ) { return; } @@ -250,7 +248,7 @@ sendMessage({ } listen(); }).catch(reason => { - console.trace(reason); + console.error(reason); }); /******************************************************************************/ diff --git a/platform/mv3/extension/js/troubleshooting.js b/platform/mv3/extension/js/troubleshooting.js index 4300d1eb1..9fa28c5f9 100644 --- a/platform/mv3/extension/js/troubleshooting.js +++ b/platform/mv3/extension/js/troubleshooting.js @@ -19,14 +19,11 @@ Home: https://github.com/gorhill/uBlock */ -import { - localRead, - runtime, - sendMessage, -} from './ext.js'; - import { dnr } from './ext-compat.js'; -import { dom } from './dom.js'; +import { getConsoleOutput } from './debug.js'; +import { getDefaultFilteringMode } from './mode-manager.js'; +import { getEffectiveUserRules } from './ruleset-manager.js'; +import { runtime } from './ext.js'; /******************************************************************************/ @@ -63,15 +60,11 @@ export async function getTroubleshootingInfo(siteMode) { rulesets, defaultMode, userRules, - registerContentScriptsReason, - unregisterContentScriptsReason, ] = await Promise.all([ runtime.getPlatformInfo(), dnr.getEnabledRulesets(), - sendMessage({ what: 'getDefaultFilteringMode' }), - sendMessage({ what: 'getEffectiveUserRules' }), - localRead('$scripting.registerContentScripts'), - localRead('$scripting.unregisterContentScripts'), + getDefaultFilteringMode(), + getEffectiveUserRules(), ]); const browser = (( ) => { const extURL = runtime.getURL(''); @@ -85,7 +78,6 @@ export async function getTroubleshootingInfo(siteMode) { } else { agent = 'Chrome'; } - dom.cl.add('html', agent.toLowerCase()); if ( /\bMobile\b/.test(navigator.userAgent) ) { agent += ' Mobile'; } @@ -113,11 +105,11 @@ export async function getTroubleshootingInfo(siteMode) { config['user rules'] = userRules.length; } config.rulesets = rulesets; - if ( registerContentScriptsReason !== undefined ) { - config.registerContentScripts = registerContentScriptsReason; - } - if ( unregisterContentScriptsReason !== undefined ) { - config.unregisterContentScripts = unregisterContentScriptsReason; + const consoleOutput = getConsoleOutput(); + if ( consoleOutput.length !== 0 ) { + config.console = siteMode + ? consoleOutput.slice(-8) + : consoleOutput; } return renderData(config); }