import RemoteMessenger from '@joplin/lib/utils/ipc/RemoteMessenger'; import { PluginMainProcessApi, PluginWebViewApi } from '../types'; import WebViewToRNMessenger from '../../../utils/ipc/WebViewToRNMessenger'; import WindowMessenger from '@joplin/lib/utils/ipc/WindowMessenger'; import makeSandboxedIframe from './utils/makeSandboxedIframe'; type PluginRecord = { iframe: HTMLIFrameElement; connectionToParent: RemoteMessenger|null; connectionToIframe: RemoteMessenger|null; }; const loadedPlugins: Record = Object.create(null); export const stopPlugin = async (pluginId: string) => { if (!loadedPlugins[pluginId]) { return; } loadedPlugins[pluginId].connectionToIframe?.closeConnection?.(); loadedPlugins[pluginId].connectionToParent?.closeConnection?.(); const iframe = loadedPlugins[pluginId].iframe; iframe.srcdoc = ''; iframe.remove(); delete loadedPlugins[pluginId]; }; export const runPlugin = ( pluginBackgroundScript: string, pluginScript: string, messageChannelId: string, pluginId: string, ) => { if (loadedPlugins[pluginId]) { console.warn(`Plugin already running ${pluginId}`); return; } const bodyHtml = ''; const initialJavaScript = ` "use strict"; ${pluginBackgroundScript} (async () => { window.require = pluginBackgroundPage.requireModule; window.exports = window.exports || {}; await pluginBackgroundPage.initializePluginBackgroundIframe(${JSON.stringify(messageChannelId)}); ${pluginScript} })(); `; const backgroundIframe = makeSandboxedIframe(bodyHtml, [initialJavaScript]).iframe; loadedPlugins[pluginId] = { iframe: backgroundIframe, connectionToParent: null, connectionToIframe: null, }; backgroundIframe.addEventListener('load', async () => { if (!loadedPlugins[pluginId]) { return; } const connectionToParent = new WebViewToRNMessenger(messageChannelId, null); const connectionToIframe = new WindowMessenger( messageChannelId, backgroundIframe.contentWindow, connectionToParent.remoteApi, ); // The two messengers are intermediate links in a chain (they serve to forward messages // between the parent and the iframe). connectionToParent.setIsChainedMessenger(true); connectionToIframe.setIsChainedMessenger(true); connectionToParent.setLocalInterface(connectionToIframe.remoteApi); loadedPlugins[pluginId].connectionToIframe = connectionToIframe; loadedPlugins[pluginId].connectionToParent = connectionToParent; }, { once: true }); document.body.appendChild(backgroundIframe); };