"use strict"; const HOST_NAME = "com.easygoingaming.firefox_agent_bridge"; const RECONNECT_MS = 2000; const ALLOWED = { "meta.ping": async () => ({ ok: true, extension: "bookmarks-api" }), "meta.methods": async () => Object.keys(ALLOWED).sort(), "bookmarks.getTree": () => browser.bookmarks.getTree(), "bookmarks.getSubTree": (id) => browser.bookmarks.getSubTree(id), "bookmarks.getChildren": (id) => browser.bookmarks.getChildren(id), "bookmarks.get": (idOrIds) => browser.bookmarks.get(idOrIds), "bookmarks.search": (query) => browser.bookmarks.search(query), "bookmarks.create": (details) => browser.bookmarks.create(details), "bookmarks.update": (id, changes) => browser.bookmarks.update(id, changes), "bookmarks.move": (id, destination) => browser.bookmarks.move(id, destination), "bookmarks.remove": (id) => browser.bookmarks.remove(id), "bookmarks.removeTree": (id) => browser.bookmarks.removeTree(id), "bookmarks.getRecent": (numberOfItems) => browser.bookmarks.getRecent(numberOfItems), }; let port = null; let reconnectTimer = null; let hostUp = false; function asArray(value) { if (value === undefined || value === null) { return []; } return Array.isArray(value) ? value : [value]; } async function dispatchBookmark(message) { const method = message && message.method; const fn = ALLOWED[method]; if (!fn) { throw new Error(`method not allowed: ${method || "(missing)"}`); } return fn(...asArray(message.args)); } function attach(nextPort) { port = nextPort; hostUp = true; port.onMessage.addListener(async (message) => { if (message && message.type === "auth") { try { const client = await findClientByTokenHash(message.token_hash); port.postMessage({ type: "auth-result", id: message.id, ok: Boolean(client), client: client || null, }); } catch (err) { port.postMessage({ type: "auth-result", id: message.id, ok: false, error: err && err.message ? err.message : String(err), }); } return; } if (message && message.type) { return; } const id = message && message.id; try { const result = await dispatchBookmark(message); port.postMessage({ id, ok: true, result }); } catch (err) { port.postMessage({ id, ok: false, error: err && err.message ? err.message : String(err), }); } }); port.onDisconnect.addListener(() => { port = null; hostUp = false; scheduleReconnect(); }); } function scheduleReconnect() { if (reconnectTimer) { return; } reconnectTimer = setTimeout(() => { reconnectTimer = null; connect(); }, RECONNECT_MS); } function connect() { try { attach(browser.runtime.connectNative(HOST_NAME)); } catch (err) { hostUp = false; console.error("native host connect failed", err); scheduleReconnect(); } } browser.runtime.onMessage.addListener((message) => { if (message && message.type === "status") { return Promise.resolve({ hostUp, port: 17634 }); } return undefined; }); connect();