1 import { generatePortName } from 'proton-pass-extension/lib/utils/port';
2 import type { Runtime } from 'webextension-polyfill';
4 import { resolveMessageFactory, sendMessage } from '@proton/pass/lib/extension/message/send-message';
5 import browser from '@proton/pass/lib/globals/browser';
6 import type { ClientEndpoint, MaybeNull, TabId } from '@proton/pass/types';
7 import { WorkerMessageType } from '@proton/pass/types';
8 import { contextHandlerFactory } from '@proton/pass/utils/context';
9 import { pipe } from '@proton/pass/utils/fp/pipe';
10 import { safeCall } from '@proton/pass/utils/fp/safe-call';
11 import { logger, registerLoggerEffect } from '@proton/pass/utils/logger';
12 import type { ParsedUrl } from '@proton/pass/utils/url/types';
14 import.meta.webpackHot?.decline();
16 export type ExtensionContextType = {
17 endpoint: ClientEndpoint;
20 url: MaybeNull<ParsedUrl>;
24 export type ExtensionContextOptions = {
25 endpoint: ClientEndpoint;
26 onDisconnect: (previousCtx?: ExtensionContextType) => { recycle: boolean };
27 onRecycle: (nextCtx: ExtensionContextType) => void;
30 export const ExtensionContext = contextHandlerFactory<ExtensionContextType>('extension');
32 export const setupExtensionContext = async (options: ExtensionContextOptions): Promise<ExtensionContextType> => {
33 const { endpoint, onDisconnect, onRecycle } = options;
34 const message = resolveMessageFactory(endpoint);
37 const { tabId, url } = await sendMessage.on(
38 message({ type: WorkerMessageType.TABS_QUERY, payload: { current: endpoint === 'popup' } }),
40 if (res.type === 'error') return { tabId: -1, url: null };
41 return { tabId: res.tabId, url: res.url };
45 const name = generatePortName(endpoint, tabId);
46 const port = browser.runtime.connect(browser.runtime.id, { name });
47 const disconnectPort = safeCall(() => port.disconnect());
48 const destroy = pipe(disconnectPort, ExtensionContext.clear);
49 const ctx = ExtensionContext.set({ endpoint, port, tabId, url, destroy });
51 ctx.port.onDisconnect.addListener(async () => {
52 const { recycle } = onDisconnect?.(ExtensionContext.read());
53 logger.info(`[Context::Extension] port disconnected [reconnect=${recycle}]`);
54 return recycle && onRecycle(await setupExtensionContext(options));
57 registerLoggerEffect((...logs) =>
59 resolveMessageFactory(endpoint)({
60 type: WorkerMessageType.LOG_EVENT,
61 payload: { log: logs.join(' ') },
66 logger.info('[Context::Extension] tabId resolved & port opened');
69 logger.warn('[Context::Extension]', e);
70 throw new Error('Initalization failed');