/* This file is part of the Notesnook project (https://notesnook.com/) Copyright (C) 2023 Streetwriters (Private) Limited This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ import { browser, Runtime, Tabs } from "webextension-polyfill-ts"; import { Remote, wrap } from "comlink"; import { createEndpoint } from "./utils/comlink-extension"; import { Server } from "./common/bridge"; import { APP_URL, APP_URL_FILTER } from "./common/constants"; type WebExtensionChannelMessage = { success: boolean }; let api: Remote | undefined; export async function connectApi(openNew = false, onDisconnect?: () => void) { if (api) return api; const tabs = await findNotesnookTabs(openNew); for (const tab of tabs) { try { const api = await Promise.race([ connectToTab(tab, onDisconnect), timeout(5000) ]); if (!api) continue; return api as Remote; } catch (e) { console.error(e); } } return false; } function timeout(ms: number) { return new Promise((resolve) => setTimeout(resolve, ms, false)); } async function connectToTab(tab: Tabs.Tab, onDisconnect?: () => void) { if (!tab.id) return false; if (browser.scripting) { await browser.scripting.executeScript({ files: ["nnContentScript.bundle.js"], target: { tabId: tab.id } }); } else { await browser.tabs.executeScript(tab.id, { file: "nnContentScript.bundle.js" }); } const port = browser.tabs.connect(tab.id); port.onDisconnect.addListener(() => { api = undefined; onDisconnect?.(); }); return new Promise | false>(function connect(resolve) { async function onMessage( message: WebExtensionChannelMessage, port: Runtime.Port ) { if (message.success) { port.onMessage.removeListener(onMessage); api = wrap(createEndpoint(port)); resolve(api); } else { resolve(false); } } port.onMessage.addListener(onMessage); }); } export async function findNotesnookTabs(openNew = false) { const tabs = await browser.tabs.query({ url: APP_URL_FILTER, discarded: false, status: "complete" }); if (tabs.length) return tabs; if (openNew) { const tab = await browser.tabs.create({ url: APP_URL, active: false }); await new Promise((resolve) => browser.tabs.onUpdated.addListener(function onUpdated(id, info) { if (id === tab.id && info.status === "complete") { browser.tabs.onUpdated.removeListener(onUpdated); resolve(); } }) ); return [tab]; } return []; }