Update selected item color in Pass menu
[ProtonMail-WebClient.git] / packages / cross-storage / lib / guest.ts
blob7b054e187b77e6b781ed5e4ad000321188214741
1 import { CrossStorageTimeoutError, CrossStorageUnsupportedError } from './errors';
2 import type { CrossStorageMessage } from './interface';
3 import { getIsSupported } from './support';
5 enum States {
6     INIT,
7     SUCCESS,
8     ERROR,
11 const createGuest = <MessagePayload>(urlTarget: string) => {
12     let iframe: HTMLIFrameElement;
13     let state = States.INIT;
14     let promiseHandler: {
15         promise: Promise<void>;
16         resolve: () => void;
17         reject: (error: Error) => void;
18     };
19     const url = new URL(urlTarget);
20     let id = 0;
21     const messagePromiseCache: {
22         [key: string]: { resolve: (value: any) => void; reject: (error: Error) => void };
23     } = {};
25     const initPromise = () => {
26         promiseHandler = {} as any;
27         promiseHandler.promise = new Promise<void>((resolve, reject) => {
28             promiseHandler.resolve = resolve;
29             promiseHandler.reject = reject;
30         });
31         promiseHandler.promise.catch(() => {});
32     };
34     const initListener = (origin: string) => {
35         const timeoutHandle = window.setTimeout(() => {
36             state = States.ERROR;
37             promiseHandler.reject(new CrossStorageTimeoutError());
38         }, 5000);
40         window.addEventListener('message', (event: MessageEvent<CrossStorageMessage>) => {
41             if (!iframe) {
42                 return;
43             }
45             const contentWindow = iframe?.contentWindow;
46             if (!iframe || !contentWindow || event.origin !== origin || event.source !== contentWindow) {
47                 return;
48             }
50             const eventData = event.data;
51             if (!eventData?.type) {
52                 return;
53             }
55             if (eventData.type === 'init') {
56                 window.clearTimeout(timeoutHandle);
57                 if (getIsSupported(eventData.payload.value)) {
58                     state = States.SUCCESS;
59                     promiseHandler.resolve();
60                 } else {
61                     state = States.ERROR;
62                     promiseHandler.reject(new CrossStorageUnsupportedError());
63                 }
64             }
66             if (state !== States.SUCCESS) {
67                 return;
68             }
70             if (eventData.type === 'response') {
71                 if (eventData.status === 'success') {
72                     messagePromiseCache[eventData.id]?.resolve(eventData.payload);
73                 }
74                 if (eventData.status === 'error') {
75                     messagePromiseCache[eventData.id]?.reject(eventData.payload);
76                 }
77             }
78         });
79     };
81     const loadIframe = (url: string) => {
82         iframe = window.document.createElement('iframe');
83         iframe.src = url;
84         iframe.width = '0';
85         iframe.height = '0';
86         iframe.style.display = 'none';
87         document.body.appendChild(iframe);
88     };
90     const getState = () => {
91         return state;
92     };
94     const getMessagePromise = <T>(id: number) => {
95         return new Promise<T>((resolve, reject) => {
96             messagePromiseCache[id] = {
97                 resolve,
98                 reject,
99             };
100         });
101     };
103     const postMessage = async (message: CrossStorageMessage) => {
104         await promiseHandler.promise;
105         iframe.contentWindow?.postMessage(message, url.origin);
106     };
108     const postAndGetMessage = async <T>(messagePayload: MessagePayload) => {
109         const messageId = id++;
110         const promise = getMessagePromise<T>(messageId);
111         await postMessage({
112             type: 'message',
113             id: messageId,
114             payload: messagePayload,
115         });
116         return promise;
117     };
119     const initChildCrossStorage = () => {
120         initPromise();
121         initListener(url.origin);
122         loadIframe(url.toString());
123     };
125     initChildCrossStorage();
127     return {
128         supported: async () => {
129             try {
130                 await promiseHandler.promise;
131                 return true;
132             } catch (e) {
133                 return false;
134             }
135         },
136         getState,
137         postAndGetMessage,
138     };
141 export default createGuest;