diff options
Diffstat (limited to 'ext/mixed/js/api.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) => { |