Declare Firefox data-collection consent and stop assigning innerHTML in options.

This commit is contained in:
alexveley
2026-09-22 08:29:33 -04:00
parent 01f62855aa
commit a989f0dcf1
3 changed files with 26 additions and 18 deletions
+1 -1
View File
@@ -4,7 +4,7 @@ Bookmarks API for Scripting and AI runs on your computer. It does not send your
## What it can see ## What it can see
With the `bookmarks` permission, the add-on can read and change bookmarks in the Firefox profile where it is installed. That is the product. Local scripts you authorize can request the same operations through the loopback API. With the `bookmarks` permission, the add-on can read and change bookmarks in the Firefox profile where it is installed. That is the product. Local scripts you authorize can request the same operations through the loopback API. Firefoxs install prompt lists this as bookmark information (`bookmarksInfo`). Nothing is sent to the authors.
## What it stores ## What it stores
+5 -2
View File
@@ -2,12 +2,15 @@
"manifest_version": 3, "manifest_version": 3,
"name": "Bookmarks API for Scripting and AI", "name": "Bookmarks API for Scripting and AI",
"short_name": "Bookmarks API", "short_name": "Bookmarks API",
"version": "0.4.2", "version": "0.4.3",
"description": "An API add-on for Firefox to allow agentic and script-based management of user bookmarks.", "description": "An API add-on for Firefox to allow agentic and script-based management of user bookmarks.",
"browser_specific_settings": { "browser_specific_settings": {
"gecko": { "gecko": {
"id": "firefox-agent-bridge@easygoingaming.com", "id": "firefox-agent-bridge@easygoingaming.com",
"strict_min_version": "115.0" "strict_min_version": "115.0",
"data_collection_permissions": {
"required": ["bookmarksInfo"]
}
} }
}, },
"permissions": ["bookmarks", "nativeMessaging", "storage"], "permissions": ["bookmarks", "nativeMessaging", "storage"],
+20 -15
View File
@@ -31,30 +31,35 @@ function showError(text) {
} }
function render(clients) { function render(clients) {
rows.replaceChildren();
if (!clients.length) { if (!clients.length) {
rows.innerHTML = '<tr><td colspan="4" class="muted">No clients yet. Generate a secret to get started.</td></tr>'; const tr = document.createElement("tr");
const td = document.createElement("td");
td.colSpan = 4;
td.className = "muted";
td.textContent = "No clients yet. Generate a secret to get started.";
tr.appendChild(td);
rows.appendChild(tr);
return; return;
} }
rows.innerHTML = "";
for (const client of clients) { for (const client of clients) {
const tr = document.createElement("tr"); const tr = document.createElement("tr");
tr.innerHTML = ` for (const text of [client.name, client.created || "", client.last_used || "never"]) {
<td>${escapeHtml(client.name)}</td> const td = document.createElement("td");
<td>${escapeHtml(client.created || "")}</td> td.textContent = text;
<td>${escapeHtml(client.last_used || "never")}</td> tr.appendChild(td);
<td><button type="button" data-revoke="${escapeHtml(client.id)}">Revoke</button></td>`; }
const action = document.createElement("td");
const btn = document.createElement("button");
btn.type = "button";
btn.textContent = "Revoke";
btn.dataset.revoke = client.id;
action.appendChild(btn);
tr.appendChild(action);
rows.appendChild(tr); rows.appendChild(tr);
} }
} }
function escapeHtml(value) {
return String(value)
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;");
}
async function refresh() { async function refresh() {
render(await listClients()); render(await listClients());
try { try {