From 354a2aa6f0b97880cb144c762d42f7559babecea Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Sun, 7 Dec 2025 14:35:45 -0500 Subject: [PATCH] draft --- platform/chromium/manifest.json | 8 +--- platform/firefox/manifest.json | 8 +--- platform/safari/manifest.json | 8 +--- src/css/popup.css | 25 ++++++++++- src/js/background.js | 32 +++----------- src/js/page-timing.js | 32 +++++--------- src/js/popup.js | 74 +++++++++++++++++++++++---------- src/popup.html | 23 +++++++++- 8 files changed, 116 insertions(+), 94 deletions(-) diff --git a/platform/chromium/manifest.json b/platform/chromium/manifest.json index be851b4..f235a6d 100644 --- a/platform/chromium/manifest.json +++ b/platform/chromium/manifest.json @@ -14,13 +14,6 @@ "service_worker": "/js/background.js", "type": "module" }, - "content_scripts": [ - { - "matches": [ "https://*/*", "http://*/*" ], - "js": [ "/js/page-timing.js" ], - "runAt": "document_end" - } - ], "description": "A tool which reports remote server connections", "host_permissions": [ "https://*/*", @@ -42,6 +35,7 @@ "name": "uBlock Origin Scope", "permissions": [ "activeTab", + "scripting", "storage", "webNavigation", "webRequest" diff --git a/platform/firefox/manifest.json b/platform/firefox/manifest.json index a4ded0d..c4de770 100644 --- a/platform/firefox/manifest.json +++ b/platform/firefox/manifest.json @@ -26,13 +26,6 @@ "strict_min_version": "128.0" } }, - "content_scripts": [ - { - "matches": [ "https://*/*", "http://*/*" ], - "js": [ "/js/page-timing.js" ], - "runAt": "document_end" - } - ], "description": "A tool which reports remote server connections", "host_permissions": [ "https://*/*", @@ -53,6 +46,7 @@ "name": "uBlock Origin Scope", "permissions": [ "activeTab", + "scripting", "storage", "webNavigation", "webRequest" diff --git a/platform/safari/manifest.json b/platform/safari/manifest.json index 52cc7b2..d415a33 100644 --- a/platform/safari/manifest.json +++ b/platform/safari/manifest.json @@ -19,13 +19,6 @@ "strict_min_version": "18.5" } }, - "content_scripts": [ - { - "matches": [ "https://*/*", "http://*/*" ], - "js": [ "/js/page-timing.js" ], - "runAt": "document_end" - } - ], "description": "A tool which reports remote server connections", "icons": { "16": "img/browsericons/logo16.png", @@ -44,6 +37,7 @@ ], "permissions": [ "activeTab", + "scripting", "storage", "webNavigation", "webRequest" diff --git a/src/css/popup.css b/src/css/popup.css index 8b60051..8e0e5e1 100644 --- a/src/css/popup.css +++ b/src/css/popup.css @@ -70,7 +70,10 @@ h2 { h2 > span:first-of-type { opacity: 60%; } - +a { + color: inherit !important; + text-decoration: none; +} main { } @@ -86,8 +89,26 @@ main section#summary #domainCount { font-size: large; } +main section#summary #pageTiming > span { + align-items: center; + display: flex; + font-size: 13px; + gap: 1em; + justify-content: center; +} + main section#summary #pageTiming { - font-size: small; + display: flex; + flex-direction: column; +} + +main section#summary #pageTiming > legend { + font-size: 11px; + opacity: 0.6; +} + +main section#summary #pageTiming > span > span { + padding: 0 2px; } main section[data-outcome] { diff --git a/src/js/background.js b/src/js/background.js index 24cd393..958dd1f 100644 --- a/src/js/background.js +++ b/src/js/background.js @@ -40,7 +40,6 @@ const manifest = runtime.getManifest(); const TABDETAILS_TEMPLATE = { domain: '', hostname: '', - timing: 0, allowed: new Map(), stealth: new Map(), blocked: new Map(), @@ -138,7 +137,6 @@ function getTabDetails(tabId, hostname) { function tabDetailsReset(tabDetails) { tabDetails.domain = ''; tabDetails.hostname = ''; - tabDetails.timing = 0; tabDetails.allowed.clear(); tabDetails.stealth.clear(); tabDetails.blocked.clear(); @@ -241,13 +239,6 @@ async function processNetworkRequestJournal() { recordOutcome(tabId, request); } break; - case 'timing': { - const tabDetails = getTabDetails(tabId, request.hostname); - if ( tabDetails === undefined ) { break; } - if ( request.timing <= 0 ) { break; } - tabDetails.timing = request.timing; - break; - } default: break; } @@ -296,24 +287,13 @@ runtime.onMessage.addListener((msg, sender, callback) => { break; } case 'getPageTiming': { - response = 0; - const tabDetails = getTabDetails(msg.tabId, msg.hostname); - if ( tabDetails === undefined ) { break; } - response = tabDetails.timing; - break; - } - case 'setPageTiming': { - const tabId = sender?.tab?.id; - if ( Boolean(tabId) === false ) { break; } - networkRequestJournal.push({ - event: 'timing', - tabId, - hostname: msg.hostname, - timing: msg.timing, + response = browser.scripting.executeScript({ + files: [ '/js/page-timing.js' ], + injectImmediately: true, + target: { tabId: msg.tabId } + }).then(result => { + callback(result); }); - const tabDetails = getTabDetails(tabId, msg.hostname); - if ( tabDetails === undefined ) { break; } - tabDetails.timing = 0; break; } default: diff --git a/src/js/page-timing.js b/src/js/page-timing.js index 21fcfc1..f1fce62 100644 --- a/src/js/page-timing.js +++ b/src/js/page-timing.js @@ -19,24 +19,14 @@ Home: https://github.com/gorhill/uBO-Scope */ -function reportTiming() { - if ( document.readyState !== 'complete' ) { return; } - const entries = performance.getEntriesByType('navigation'); - if ( entries.length === 0 ) { return; } - const entry = entries[0]; - const timing = Math.round(entry.domComplete - entry.responseStart); - if ( timing > 0 ) { - chrome.runtime.sendMessage({ - what: 'setPageTiming', - hostname: document.location.hostname, - timing: Math.round(timing), - }); - } - document.removeEventListener('readystatechange', reportTiming); -} - -if ( document.readyState === 'complete' ) { - reportTiming(); -} else { - document.addEventListener('readystatechange', reportTiming); -} +(( ) => { + const entries = performance.getEntries(); + const nav = entries.find(a => a.entryType === 'navigation') || {}; + const fcp = entries.find(a => a.name === 'first-contentful-paint') || {}; + return { + frb: Math.round(nav.responseStart || 0), + dcl: Math.round(nav.domInteractive || 0), + l: Math.round(nav.domComplete || 0), + fcp: Math.round(fcp.startTime || 0), + }; +})(); diff --git a/src/js/popup.js b/src/js/popup.js index 72b1527..8e815e5 100644 --- a/src/js/popup.js +++ b/src/js/popup.js @@ -135,34 +135,64 @@ function renderPanelSection(topDomain, domainMap, outcome) { } } -function renderTiming(timing) { - if ( typeof timing !== 'number' ) { - timing = tabData.timing; - } else { - tabData.timing = timing; +/******************************************************************************/ + +async function renderTiming() { + const parts = [ + { name: 'frb', time: 0 }, + { name: 'dcl', time: 0, rel: true }, + { name: 'l', time: 0, rel: true }, + { name: 'fcp', time: 0, rel: true }, + ]; + const result = await sendMessage({ + what: 'getPageTiming', + tabId: tabData.tabId, + }); + const data = result?.[0]?.result ?? {}; + for ( const part of parts ) { + if ( data[part.name] === 0 ) { continue; } + part.time = data[part.name]; } - if ( timing === 0 ) { - return self.setTimeout(( ) => { - sendMessage({ - what: 'getPageTiming', - tabId: tabData.tabId, - hostname: tabData.hostname, - }).then(renderTiming); - }, 1000); + parts.sort((a, b) => a.time - b.time); + const intl = new Intl.NumberFormat(undefined, { maximumSignificantDigits: 3 }); + const fragment = document.createDocumentFragment(); + const slice = 800; + for ( const part of parts ) { + const node = nodeFromTemplate(part.name, 'span'); + let time = part.time; + const text = time < 1000 + ? `${intl.format(time)} ms` + : `${intl.format(time / 1000)} sec`; + let h = 0; + if ( part.time < slice ) { + h = 120 - Math.round(part.time * 40 / slice); + } else if ( part.time < slice*2 ) { + h = 80 - Math.round((part.time - slice) * 40 / slice); + } else { + h = 40 - Math.min(Math.round((part.time - slice*2) * 40 / slice), 40); + } + node.style.borderColor = `hsl(${h} 100% 40%)`; + dom.text(qs$(node, 'span'), text); + fragment.append(node); } - const unit = timing > 1000 ? 'second' : 'millisecond' ; - if ( unit === 'second' ) { - timing /= 1000; + dom.clear('#pageTiming > span'); + qs$('#pageTiming > span').append(fragment); + if ( parts.some(a => a.time === 0) ) { + return self.setTimeout(renderTiming, 1000); } - const intl = new Intl.NumberFormat(undefined, { - notation: 'compact', - maximumSignificantDigits: 3, - style: 'unit', - unit, + timingStatsPromise.then((stats = []) => { + const s = JSON.stringify({ hn: tabData.hostname, parts }); + if ( stats.length !== 0 && stats[0] === s ) { return; } + stats.unshift(s); + if ( stats.length > 20 ) { + stats = stats.slice(0, 20); + } + sessionWrite('timingStats', stats); }); - dom.text('#pageTiming > span', intl.format(timing)); } +const timingStatsPromise = sessionRead('timingStats'); + /******************************************************************************/ function toggleExpand(expandKey, afterState, persist) { diff --git a/src/popup.html b/src/popup.html index 7f60b78..56ada97 100644 --- a/src/popup.html +++ b/src/popup.html @@ -13,8 +13,11 @@

NO DATA

+ + + browser extensions & cache influence visible, interactive, and complete – results may vary across page reloads + domains connected: ? - page load time:

not blocked

@@ -39,7 +42,23 @@

blocked

+ + + + + + + +