fix(theseus/screenshot): 0.2.4 — deliver capture via addon storage, not a cross-origin file://
Blank editor + broken buttons root cause: index.js was writing the capture to <userData>/addons-data/screenshot-scratch/<name>.png and passing "?src=file://<that path>" to editor.html. The editor lives at file:///<userData>/addons/screenshot/editor.html — different directory tree under file://. Chromium's file:// origin policy treats those as different origins and quietly refuses the <img> load, so init()'s loadImage() rejects, the canvas never gets an image, and every tool after that operates on a still-empty 300×150 default canvas — the tools appear to work but produce no visible output because the base image never landed. The sidebar version we replaced set `previewImg.src = dataUrl` (a base64 data URL) directly, which has no origin and just worked; the tab version regressed by adding the file hop. Fix keeps the scratch file for the recent-captures ring but hands the raw capture through the add-on's per-add-on kv store (`__pending` key). Same store, same origin scoping, no cross-directory read: index.js writes via api.storage.set from main; editor.js reads via window.silentmode.storage.get through the tab preload (packaged since 0.3.27). Fallback path retained for "openRecent" callers still passing ?src=… — those will need their own fix in a follow-up. Bumped to 0.2.4 and signed for the OTA endpoint — first real independent add-on ship: no Theseus release needed to fix this, 0.3.27 installs pick up 0.2.4 via the boot-time signed-update poll.
This commit is contained in:
parent
1d0e25c4e0
commit
2e42783dfe
3 changed files with 47 additions and 9 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"id": "screenshot",
|
"id": "screenshot",
|
||||||
"name": "Screenshot",
|
"name": "Screenshot",
|
||||||
"version": "0.2.3",
|
"version": "0.2.4",
|
||||||
"description": "Capture the current tab — visible viewport, entire scrollable page, or a rectangle you draw. Pick a mode from a toolbar dropdown; the capture opens in a full-tab editor (crop, annotate, redact, save).",
|
"description": "Capture the current tab — visible viewport, entire scrollable page, or a rectangle you draw. Pick a mode from a toolbar dropdown; the capture opens in a full-tab editor (crop, annotate, redact, save).",
|
||||||
"author": "Silent Mode",
|
"author": "Silent Mode",
|
||||||
"icon": "📸",
|
"icon": "📸",
|
||||||
|
|
|
||||||
|
|
@ -112,10 +112,38 @@ function loadImage(url) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function init() {
|
async function init() {
|
||||||
if (!srcUrl) { toast("Missing ?src=", true); return; }
|
// Preferred source: a pending capture handed to us through addon storage
|
||||||
|
// (the toolbar-menu capture path). A file:// src won't load — Chromium
|
||||||
|
// treats the editor at file:///…/addons/screenshot/editor.html as a
|
||||||
|
// different origin from the scratch PNG at file:///…/addons-data/…,
|
||||||
|
// so <img> quietly errors. Storage-based delivery sidesteps the origin
|
||||||
|
// entirely.
|
||||||
|
let dataUrl = null;
|
||||||
|
let toolFromPending = "";
|
||||||
|
let pendingName = baseName;
|
||||||
|
try {
|
||||||
|
if (window.silentmode?.storage) {
|
||||||
|
const pending = await window.silentmode.storage.get("__pending", null);
|
||||||
|
if (pending && pending.dataUrl) {
|
||||||
|
dataUrl = pending.dataUrl;
|
||||||
|
toolFromPending = pending.tool || "";
|
||||||
|
if (pending.name) { pendingName = pending.name; nameEl.textContent = pending.name; document.title = pending.name + " — editor"; }
|
||||||
|
// Clear so a later editor open (e.g. "recent captures") doesn't
|
||||||
|
// accidentally reload the same capture.
|
||||||
|
await window.silentmode.storage.set("__pending", null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) { console.warn("editor: storage read failed:", e); }
|
||||||
|
// Fallback path — retained so the "openRecent" flow (which currently
|
||||||
|
// still passes ?src=file://) keeps working after we teach it to use
|
||||||
|
// storage too.
|
||||||
|
if (!dataUrl) {
|
||||||
|
if (!srcUrl) { toast("No capture to edit", true); return; }
|
||||||
|
dataUrl = srcUrl;
|
||||||
|
}
|
||||||
let img;
|
let img;
|
||||||
try { img = await loadImage(srcUrl); }
|
try { img = await loadImage(dataUrl); }
|
||||||
catch (e) { toast(e.message, true); return; }
|
catch (e) { toast("Couldn't load capture: " + e.message, true); return; }
|
||||||
sizeCanvases(img.naturalWidth, img.naturalHeight);
|
sizeCanvases(img.naturalWidth, img.naturalHeight);
|
||||||
cctx.drawImage(img, 0, 0);
|
cctx.drawImage(img, 0, 0);
|
||||||
undo = [];
|
undo = [];
|
||||||
|
|
@ -123,7 +151,8 @@ async function init() {
|
||||||
pushSnapshot(); // baseline so the very first edit is undoable
|
pushSnapshot(); // baseline so the very first edit is undoable
|
||||||
updateUndoButtons();
|
updateUndoButtons();
|
||||||
fitBoard();
|
fitBoard();
|
||||||
if (initialTool) setTool(initialTool);
|
const t = initialTool || toolFromPending;
|
||||||
|
if (t) setTool(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scale the stage to fit within the board when the image is bigger than
|
// Scale the stage to fit within the board when the image is bigger than
|
||||||
|
|
|
||||||
|
|
@ -83,11 +83,20 @@ module.exports = {
|
||||||
recent.unshift({ id: name, name, path: written.path, url: fileUrl(written.path), bytes: written.bytes, at: Date.now(), mode: id });
|
recent.unshift({ id: name, name, path: written.path, url: fileUrl(written.path), bytes: written.bytes, at: Date.now(), mode: id });
|
||||||
recent = pruneRecent(recent);
|
recent = pruneRecent(recent);
|
||||||
api.storage.set("recent", recent);
|
api.storage.set("recent", recent);
|
||||||
|
// Hand the raw data URL to the editor tab through addon storage. We
|
||||||
|
// used to pass `?src=file://…` and let the editor load the scratch
|
||||||
|
// file, but Chromium's file:// origin policy blocks <img> from
|
||||||
|
// cross-directory file loads: editor.html lives under
|
||||||
|
// <userData>/addons/screenshot/ and scratch PNGs land under
|
||||||
|
// <userData>/addons-data/screenshot-scratch/, which is a sibling of
|
||||||
|
// the addon dir, and the load quietly fails — the editor initializes
|
||||||
|
// but the image never draws, so every subsequent tool (draw, save,
|
||||||
|
// discard) operates on a still-empty canvas. Storage isn't
|
||||||
|
// origin-scoped, so the editor can pull the data URL out and paint
|
||||||
|
// it locally.
|
||||||
|
api.storage.set("__pending", { name, dataUrl: cap.dataUrl, tool: id === "region" ? "crop" : "", at: Date.now() });
|
||||||
api.log(`captured ${id} → ${name} (${written.bytes} bytes)`);
|
api.log(`captured ${id} → ${name} (${written.bytes} bytes)`);
|
||||||
// Region mode = land in the crop tool immediately so the user can trim
|
api.openTab("editor.html", { query: { name } });
|
||||||
// further if the freehand rect was rough.
|
|
||||||
const tool = id === "region" ? "crop" : "";
|
|
||||||
api.openTab("editor.html", { query: { src: fileUrl(written.path), name, tool } });
|
|
||||||
return { ok: true, name };
|
return { ok: true, name };
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue