feat(popup): copy privacy-safe diagnostics

This commit is contained in:
2026-08-02 21:02:08 -04:00
parent 98546adf41
commit 7e9ce436d3
6 changed files with 206 additions and 0 deletions
@@ -12,6 +12,10 @@
typeof tc === "object" && typeof tc.frameToken === "string"
? tc.frameToken
: null,
diagnostics:
typeof getDiagnosticsSnapshot === "function"
? getDiagnosticsSnapshot(v)
: null,
preferred: !v.paused,
forceLastSavedSpeed: Boolean(
typeof tc === "object" && tc.settings && tc.settings.forceLastSavedSpeed
+75
View File
@@ -90,6 +90,80 @@ function getPrimaryVideoElement(mediaElements) {
return best;
}
function getDiagnosticsSnapshot(media) {
var primary = media || getPrimaryVideoElement();
if (!primary) return null;
var controller = primary.vsc || null;
var wrapper = controller && controller.div;
var fullscreenElement = getFullscreenElement(primary.ownerDocument);
var rect = null;
try {
rect = primary.getBoundingClientRect();
} catch (_error) {}
return {
mediaCount: tc.mediaElements.filter(function(item) {
return item && item.isConnected;
}).length,
mediaType: String(primary.nodeName || "media").toLowerCase(),
playbackRate: Number(primary.playbackRate),
paused: primary.paused === true,
ended: primary.ended === true,
readyState: Number(primary.readyState) || 0,
muted: primary.muted === true,
volume: Number(primary.volume),
dimensions: rect
? [Math.round(Number(rect.width) || 0), Math.round(Number(rect.height) || 0)]
: [0, 0],
fullscreen: {
active: Boolean(fullscreenElement),
element: fullscreenElement
? String(fullscreenElement.nodeName || "element").toLowerCase()
: null,
ownsMedia: Boolean(
fullscreenElement &&
(fullscreenElement === primary ||
isComposedDescendant(primary, fullscreenElement))
)
},
controller: {
present: Boolean(controller && wrapper),
connected: Boolean(wrapper && wrapper.isConnected),
hidden: Boolean(wrapper && wrapper.classList.contains("vsc-hidden")),
geometryHidden: Boolean(
wrapper && wrapper.classList.contains("vsc-geometry-hidden")
),
fullscreenPopover: Boolean(
wrapper && wrapper.classList.contains("vsc-fullscreen-popover")
),
location: controller ? controller.controllerLocation : null
},
effectiveSettings: {
enabled: tc.settings.enabled !== false,
startHidden: tc.settings.startHidden === true,
hideWithControls: tc.settings.hideWithControls === true,
rememberSpeed: tc.settings.rememberSpeed === true,
forceLastSavedSpeed: tc.settings.forceLastSavedSpeed === true,
shortcutTargetMode: tc.settings.shortcutTargetMode
},
siteRule: {
matched: Boolean(tc.activeSiteRule),
disabled: Boolean(
tc.activeSiteRule &&
siteRuleUtils.isSiteRuleDisabled(tc.activeSiteRule)
),
overrideKeys: tc.activeSiteRule
? Object.keys(tc.activeSiteRule)
.filter(function(key) {
return key !== "pattern" && key !== "title";
})
.sort()
: []
}
};
}
var tc = {
settings: {
lastSpeed: getSharedDefault("lastSpeed", 1.0),
@@ -2344,6 +2418,7 @@ function loadInitialRuntimeSettings(attempt) {
sendResponse({
speed: videoGs.playbackRate,
frameToken: tc.frameToken,
diagnostics: getDiagnosticsSnapshot(videoGs),
forceLastSavedSpeed: tc.settings.forceLastSavedSpeed === true,
forceLastSavedSpeedControlledBySiteRule: Boolean(
tc.activeSiteRule &&
+1
View File
@@ -41,6 +41,7 @@
<div class="popup-links">
<button id="config">Settings</button>
<div class="popup-secondary">
<button id="copyDiagnostics" class="secondary">Copy diagnostics</button>
<button id="feedback" class="secondary">Feedback</button>
<button id="about" class="secondary">About</button>
</div>
+79
View File
@@ -29,6 +29,58 @@ document.addEventListener("DOMContentLoaded", function () {
var forceLastSavedSpeedControlledBySiteRule = null;
var selectedFrameToken = null;
var shortcutTargetMode = "closest";
var diagnosticContext = null;
function getSafePageDetails(url) {
try {
var parsed = new URL(url);
return {
protocol: parsed.protocol,
hostname: parsed.hostname || null
};
} catch (_error) {
return { protocol: null, hostname: null };
}
}
function buildDiagnosticReport() {
if (!diagnosticContext) return null;
var storage = diagnosticContext.storage;
var siteRule = diagnosticContext.siteRule;
var frame = diagnosticContext.frame;
return JSON.stringify(
{
speederVersion: chrome.runtime.getManifest().version,
browser: navigator.userAgent,
platform: navigator.platform || null,
page: getSafePageDetails(diagnosticContext.url),
globalSettings: {
enabled: storage.enabled !== false,
rememberSpeed: storage.rememberSpeed === true,
forceLastSavedSpeed: storage.forceLastSavedSpeed === true,
audioEnabled: storage.audioBoolean === true,
startHidden: storage.startHidden === true,
hideWithControls: storage.hideWithControls === true,
controllerLocation: storage.controllerLocation,
shortcutTargetMode: storage.shortcutTargetMode
},
matchedSiteRule: {
matched: Boolean(siteRule),
disabled: isSiteRuleDisabled(siteRule),
overrideKeys: siteRule
? Object.keys(siteRule)
.filter(function(key) {
return key !== "pattern" && key !== "title";
})
.sort()
: []
},
frame: frame && frame.diagnostics ? frame.diagnostics : null
},
null,
2
);
}
function persistExpandedSettings(rawStorage, settings, callback) {
var mutation = vscBuildManagedStorageMutation(rawStorage, settings);
@@ -288,6 +340,26 @@ document.addEventListener("DOMContentLoaded", function () {
window.open("https://github.com/SoPat712/Speeder/issues");
});
document.querySelector("#copyDiagnostics").addEventListener("click", function () {
var report = buildDiagnosticReport();
if (!report) {
setStatusMessage("Diagnostics are still loading.");
return;
}
if (!navigator.clipboard || typeof navigator.clipboard.writeText !== "function") {
setStatusMessage("Clipboard access is unavailable.");
return;
}
navigator.clipboard.writeText(report).then(
function () {
setStatusMessage("Diagnostics copied. Review before sharing.");
},
function () {
setStatusMessage("Could not copy diagnostics.");
}
);
});
document.querySelector("#donate").addEventListener("click", function () {
this.classList.add("hide");
document.querySelector("#donateOptions").classList.remove("hide");
@@ -419,6 +491,12 @@ document.addEventListener("DOMContentLoaded", function () {
forceLastSavedSpeedControlledBySiteRule
? siteRule.forceLastSavedSpeed === true
: storage.forceLastSavedSpeed === true;
diagnosticContext = {
url: url,
storage: storage,
siteRule: siteRule,
frame: null
};
if (siteRule && siteRule.showPopupControlBar !== undefined) {
showBar = siteRule.showPopupControlBar;
@@ -443,6 +521,7 @@ document.addEventListener("DOMContentLoaded", function () {
if (siteAvailable) {
querySpeed(function(frameContext) {
if (currentRenderToken !== renderToken) return;
diagnosticContext.frame = frameContext || null;
if (
frameContext &&
typeof frameContext.forceLastSavedSpeed === "boolean"
+3
View File
@@ -71,6 +71,9 @@
if (typeof result.frameToken === "string") {
normalized.frameToken = result.frameToken;
}
if (result.diagnostics && typeof result.diagnostics === "object") {
normalized.diagnostics = result.diagnostics;
}
if (typeof result.forceLastSavedSpeed === "boolean") {
normalized.forceLastSavedSpeed = result.forceLastSavedSpeed;
}
+44
View File
@@ -167,6 +167,50 @@ describe("popup.js", () => {
);
});
it("copies redacted diagnostics for the active media frame", async () => {
bootPopup({
activeTab: {
id: 12,
active: true,
url: "https://video.example/watch?private_token=secret"
},
executeScriptImpl: (tabId, details, callback) => {
callback([
{
speed: 1.5,
preferred: true,
diagnostics: {
mediaType: "video",
fullscreen: { active: true, element: "div", ownsMedia: true },
controller: { present: true, hidden: false }
}
}
]);
}
});
const writeText = vi.fn(() => Promise.resolve());
Object.defineProperty(navigator, "clipboard", {
configurable: true,
value: { writeText }
});
await flushAsyncWork();
document.querySelector("#copyDiagnostics").click();
await flushAsyncWork();
expect(writeText).toHaveBeenCalled();
const reportText = writeText.mock.calls.at(-1)[0];
const report = JSON.parse(reportText);
expect(report.page).toEqual({
protocol: "https:",
hostname: "video.example"
});
expect(report.frame.mediaType).toBe("video");
expect(reportText).not.toContain("private_token");
expect(reportText).not.toContain("secret");
expect(document.querySelector("#status").textContent).toContain("copied");
});
it("toggles enablement and closes after a successful refresh", async () => {
const chrome = bootPopup({
syncData: {