diff options
Diffstat (limited to 'ext/bg/js/backend.js')
-rw-r--r-- | ext/bg/js/backend.js | 75 |
1 files changed, 74 insertions, 1 deletions
diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index 2fce4be9..ed01c8df 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -116,8 +116,10 @@ class Backend { ['purgeDatabase', {handler: this._onApiPurgeDatabase.bind(this), async: true}], ['getMedia', {handler: this._onApiGetMedia.bind(this), async: true}], ['log', {handler: this._onApiLog.bind(this), async: false}], - ['logIndicatorClear', {handler: this._onApiLogIndicatorClear.bind(this), async: false}] + ['logIndicatorClear', {handler: this._onApiLogIndicatorClear.bind(this), async: false}], + ['createActionPort', {handler: this._onApiCreateActionPort.bind(this), async: false}] ]); + this._messageHandlersWithProgress = new Map(); this._commandHandlers = new Map([ ['search', this._onCommandSearch.bind(this)], @@ -787,8 +789,79 @@ class Backend { this._updateBadge(); } + _onApiCreateActionPort(params, sender) { + if (!sender || !sender.tab) { throw new Error('Invalid sender'); } + const tabId = sender.tab.id; + if (typeof tabId !== 'number') { throw new Error('Sender has invalid tab ID'); } + + const frameId = sender.frameId; + const id = yomichan.generateId(16); + const portName = `action-port-${id}`; + + const port = chrome.tabs.connect(tabId, {name: portName, frameId}); + try { + this._createActionListenerPort(port, sender, this._messageHandlersWithProgress); + } catch (e) { + port.disconnect(); + throw e; + } + + return portName; + } + // Command handlers + _createActionListenerPort(port, sender, handlers) { + let hasStarted = false; + + const onProgress = (data) => { + try { + if (port === null) { return; } + port.postMessage({type: 'progress', data}); + } catch (e) { + // NOP + } + }; + + const onMessage = async ({action, params}) => { + if (hasStarted) { return; } + hasStarted = true; + port.onMessage.removeListener(onMessage); + + try { + port.postMessage({type: 'ack'}); + + const messageHandler = handlers.get(action); + if (typeof messageHandler === 'undefined') { + throw new Error('Invalid action'); + } + const {handler, async} = messageHandler; + + const promiseOrResult = handler(params, sender, onProgress); + const result = async ? await promiseOrResult : promiseOrResult; + port.postMessage({type: 'complete', data: result}); + } catch (e) { + if (port !== null) { + port.postMessage({type: 'error', data: e}); + } + cleanup(); + } + }; + + const cleanup = () => { + if (port === null) { return; } + if (!hasStarted) { + port.onMessage.removeListener(onMessage); + } + port.onDisconnect.removeListener(cleanup); + port = null; + handlers = null; + }; + + port.onMessage.addListener(onMessage); + port.onDisconnect.addListener(cleanup); + } + _getErrorLevelValue(errorLevel) { switch (errorLevel) { case 'info': return 0; |