mirror of
https://github.com/SoPat712/Speeder.git
synced 2026-08-19 11:52:31 -04:00
feat(popup): copy privacy-safe diagnostics
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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 &&
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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: {
|
||||
|
||||
Reference in New Issue
Block a user