From a13a68990eac548f8439050222ddf3cb97146199 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Sat, 18 Jul 2020 14:16:35 -0400 Subject: [PATCH] Port name details (#667) * Use a stringified JSON details object for extension port names * Fix incorrect frame ID check * Add support for connecting to different tabs * Add function for invoking on a different tab --- ext/bg/js/backend.js | 31 +++++++++++++----- ext/mixed/js/api.js | 15 ++++----- ext/mixed/js/comm.js | 77 ++++++++++++++++++++++++++++++++++---------- 3 files changed, 89 insertions(+), 34 deletions(-) diff --git a/ext/bg/js/backend.js b/ext/bg/js/backend.js index f6c2ef74..f0ed986c 100644 --- a/ext/bg/js/backend.js +++ b/ext/bg/js/backend.js @@ -305,20 +305,32 @@ class Backend { _onConnect(port) { try { - const match = /^background-cross-frame-communication-port-(\d+)$/.exec(`${port.name}`); - if (match === null) { return; } + let details; + try { + details = JSON.parse(port.name); + } catch (e) { + return; + } + if (details.name !== 'background-cross-frame-communication-port') { return; } const tabId = (port.sender && port.sender.tab ? port.sender.tab.id : null); if (typeof tabId !== 'number') { throw new Error('Port does not have an associated tab ID'); } const senderFrameId = port.sender.frameId; - if (typeof tabId !== 'number') { + if (typeof senderFrameId !== 'number') { throw new Error('Port does not have an associated frame ID'); } - const targetFrameId = parseInt(match[1], 10); + let {targetTabId, targetFrameId} = details; + if (typeof targetTabId !== 'number') { + targetTabId = tabId; + } - let forwardPort = chrome.tabs.connect(tabId, {frameId: targetFrameId, name: `cross-frame-communication-port-${senderFrameId}`}); + const details2 = { + name: 'cross-frame-communication-port', + sourceFrameId: senderFrameId + }; + let forwardPort = chrome.tabs.connect(targetTabId, {frameId: targetFrameId, name: JSON.stringify(details2)}); const cleanup = () => { this._checkLastError(chrome.runtime.lastError); @@ -720,9 +732,12 @@ class Backend { const frameId = sender.frameId; const id = yomichan.generateId(16); - const portName = `action-port-${id}`; + const details = { + name: 'action-port', + id + }; - const port = chrome.tabs.connect(tabId, {name: portName, frameId}); + const port = chrome.tabs.connect(tabId, {name: JSON.stringify(details), frameId}); try { this._createActionListenerPort(port, sender, this._messageHandlersWithProgress); } catch (e) { @@ -730,7 +745,7 @@ class Backend { throw e; } - return portName; + return details; } async _onApiImportDictionaryArchive({archiveContent, details}, sender, onProgress) { diff --git a/ext/mixed/js/api.js b/ext/mixed/js/api.js index 534154ef..4009c86e 100644 --- a/ext/mixed/js/api.js +++ b/ext/mixed/js/api.js @@ -212,16 +212,13 @@ const api = (() => { _createActionPort(timeout=5000) { return new Promise((resolve, reject) => { let timer = null; - const { - promise: portNamePromise, - resolve: portNameResolve, - reject: portNameReject - } = deferPromise(); + const portDetails = deferPromise(); const onConnect = async (port) => { try { - const portName = await portNamePromise; - if (port.name !== portName || timer === null) { return; } + const {name: expectedName, id: expectedId} = await portDetails.promise; + const {name, id} = JSON.parse(port.name); + if (name !== expectedName || id !== expectedId || timer === null) { return; } } catch (e) { return; } @@ -239,14 +236,14 @@ const api = (() => { timer = null; } chrome.runtime.onConnect.removeListener(onConnect); - portNameReject(e); + portDetails.reject(e); reject(e); }; timer = setTimeout(() => onError(new Error('Timeout')), timeout); chrome.runtime.onConnect.addListener(onConnect); - this._invoke('createActionPort').then(portNameResolve, onError); + this._invoke('createActionPort').then(portDetails.resolve, onError); }); } diff --git a/ext/mixed/js/comm.js b/ext/mixed/js/comm.js index 1516a98f..1264a33e 100644 --- a/ext/mixed/js/comm.js +++ b/ext/mixed/js/comm.js @@ -16,8 +16,9 @@ */ class CrossFrameAPIPort extends EventDispatcher { - constructor(otherFrameId, port, messageHandlers) { + constructor(otherTabId, otherFrameId, port, messageHandlers) { super(); + this._otherTabId = otherTabId; this._otherFrameId = otherFrameId; this._port = port; this._messageHandlers = messageHandlers; @@ -26,6 +27,10 @@ class CrossFrameAPIPort extends EventDispatcher { this._eventListeners = new EventListenerCollection(); } + get otherTabId() { + return this._otherTabId; + } + get otherFrameId() { return this._otherFrameId; } @@ -211,8 +216,12 @@ class CrossFrameAPI { chrome.runtime.onConnect.addListener(this._onConnect.bind(this)); } - async invoke(targetFrameId, action, params={}) { - const commPort = this._getOrCreateCommPort(targetFrameId); + invoke(targetFrameId, action, params={}) { + return this.invokeTab(null, targetFrameId, action, params); + } + + async invokeTab(targetTabId, targetFrameId, action, params={}) { + const commPort = this._getOrCreateCommPort(targetTabId, targetFrameId); return await commPort.invoke(action, params, this._ackTimeout, this._responseTimeout); } @@ -226,31 +235,65 @@ class CrossFrameAPI { } _onConnect(port) { - const match = /^cross-frame-communication-port-(\d+)$/.exec(`${port.name}`); - if (match === null) { return; } + try { + let details; + try { + details = JSON.parse(port.name); + } catch (e) { + return; + } + if (details.name !== 'cross-frame-communication-port') { return; } - const otherFrameId = parseInt(match[1], 10); - this._setupCommPort(otherFrameId, port); + const otherTabId = details.sourceTabId; + const otherFrameId = details.sourceFrameId; + this._setupCommPort(otherTabId, otherFrameId, port); + } catch (e) { + port.disconnect(); + yomichan.logError(e); + } } _onDisconnect(commPort) { commPort.off('disconnect', this._onDisconnectBind); - this._commPorts.delete(commPort.otherFrameId); + const {otherTabId, otherFrameId} = commPort; + const tabPorts = this._commPorts.get(otherTabId); + if (typeof tabPorts !== 'undefined') { + tabPorts.delete(otherFrameId); + if (tabPorts.size === 0) { + this._commPorts.delete(otherTabId); + } + } } - _getOrCreateCommPort(otherFrameId) { - const commPort = this._commPorts.get(otherFrameId); - return (typeof commPort !== 'undefined' ? commPort : this._createCommPort(otherFrameId)); + _getOrCreateCommPort(otherTabId, otherFrameId) { + const tabPorts = this._commPorts.get(otherTabId); + if (typeof tabPorts !== 'undefined') { + const commPort = tabPorts.get(otherFrameId); + if (typeof commPort !== 'undefined') { + return commPort; + } + } + return this._createCommPort(otherTabId, otherFrameId); } - _createCommPort(otherFrameId) { - const port = yomichan.connect(null, {name: `background-cross-frame-communication-port-${otherFrameId}`}); - return this._setupCommPort(otherFrameId, port); + _createCommPort(otherTabId, otherFrameId) { + const details = { + name: 'background-cross-frame-communication-port', + targetTabId: otherTabId, + targetFrameId: otherFrameId + }; + const port = yomichan.connect(null, {name: JSON.stringify(details)}); + return this._setupCommPort(otherTabId, otherFrameId, port); } - _setupCommPort(otherFrameId, port) { - const commPort = new CrossFrameAPIPort(otherFrameId, port, this._messageHandlers); - this._commPorts.set(otherFrameId, commPort); + _setupCommPort(otherTabId, otherFrameId, port) { + const commPort = new CrossFrameAPIPort(otherTabId, otherFrameId, port, this._messageHandlers); + let tabPorts = this._commPorts.get(otherTabId); + if (typeof tabPorts === 'undefined') { + tabPorts = new Map(); + this._commPorts.set(otherTabId, tabPorts); + } + tabPorts.set(otherFrameId, commPort); commPort.prepare(); commPort.on('disconnect', this._onDisconnectBind); return commPort;