diff options
Diffstat (limited to 'ext/mixed/js')
| -rw-r--r-- | ext/mixed/js/api.js | 118 | 
1 files changed, 118 insertions, 0 deletions
| diff --git a/ext/mixed/js/api.js b/ext/mixed/js/api.js index afd68aa2..bf85338e 100644 --- a/ext/mixed/js/api.js +++ b/ext/mixed/js/api.js @@ -152,6 +152,124 @@ function apiLogIndicatorClear() {      return _apiInvoke('logIndicatorClear');  } +function _apiCreateActionPort(timeout=5000) { +    return new Promise((resolve, reject) => { +        let timer = null; +        let portNameResolve; +        let portNameReject; +        const portNamePromise = new Promise((resolve2, reject2) => { +            portNameResolve = resolve2; +            portNameReject = reject2; +        }); + +        const onConnect = async (port) => { +            try { +                const portName = await portNamePromise; +                if (port.name !== portName || timer === null) { return; } +            } catch (e) { +                return; +            } + +            clearTimeout(timer); +            timer = null; + +            chrome.runtime.onConnect.removeListener(onConnect); +            resolve(port); +        }; + +        const onError = (e) => { +            if (timer !== null) { +                clearTimeout(timer); +                timer = null; +            } +            chrome.runtime.onConnect.removeListener(onConnect); +            portNameReject(e); +            reject(e); +        }; + +        timer = setTimeout(() => onError(new Error('Timeout')), timeout); + +        chrome.runtime.onConnect.addListener(onConnect); +        _apiInvoke('createActionPort').then(portNameResolve, onError); +    }); +} + +function _apiInvokeWithProgress(action, params, onProgress, timeout=5000) { +    return new Promise((resolve, reject) => { +        let timer = null; +        let port = null; + +        if (typeof onProgress !== 'function') { +            onProgress = () => {}; +        } + +        const onMessage = (message) => { +            switch (message.type) { +                case 'ack': +                    if (timer !== null) { +                        clearTimeout(timer); +                        timer = null; +                    } +                    break; +                case 'progress': +                    try { +                        onProgress(message.data); +                    } catch (e) { +                        // NOP +                    } +                    break; +                case 'complete': +                    cleanup(); +                    resolve(message.data); +                    break; +                case 'error': +                    cleanup(); +                    reject(jsonToError(message.data)); +                    break; +            } +        }; + +        const onDisconnect = () => { +            cleanup(); +            reject(new Error('Disconnected')); +        }; + +        const cleanup = () => { +            if (timer !== null) { +                clearTimeout(timer); +                timer = null; +            } +            if (port !== null) { +                port.onMessage.removeListener(onMessage); +                port.onDisconnect.removeListener(onDisconnect); +                port.disconnect(); +                port = null; +            } +            onProgress = null; +        }; + +        timer = setTimeout(() => { +            cleanup(); +            reject(new Error('Timeout')); +        }, timeout); + +        (async () => { +            try { +                port = await _apiCreateActionPort(timeout); +                port.onMessage.addListener(onMessage); +                port.onDisconnect.addListener(onDisconnect); +                port.postMessage({action, params}); +            } catch (e) { +                cleanup(); +                reject(e); +            } finally { +                action = null; +                params = null; +            } +        })(); +    }); +} +  function _apiInvoke(action, params={}) {      const data = {action, params};      return new Promise((resolve, reject) => { |