aboutsummaryrefslogtreecommitdiff
path: root/ext/bg/js/backend.js
diff options
context:
space:
mode:
Diffstat (limited to 'ext/bg/js/backend.js')
-rw-r--r--ext/bg/js/backend.js75
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;