From 6769ff501c8b5d20e9056de57eff5a62a92339d3 Mon Sep 17 00:00:00 2001 From: praschke Date: Sat, 30 Sep 2023 17:17:31 +0100 Subject: create both cross-frame ports in the background on Chrome (currently 117), the port created in the content script with runtime.connect does not properly receive an onDisconnect event when the service worker sleeps. the port created in the background with tabs.connect does receive the event, so create both ports with tabs.connect. fixes #241. --- ext/js/background/backend.js | 102 +++++++++++++++++++---------------------- ext/js/comm/api.js | 4 ++ ext/js/comm/cross-frame-api.js | 31 +++++++------ 3 files changed, 68 insertions(+), 69 deletions(-) (limited to 'ext') diff --git a/ext/js/background/backend.js b/ext/js/background/backend.js index dd233abb..3cac2e4d 100644 --- a/ext/js/background/backend.js +++ b/ext/js/background/backend.js @@ -139,7 +139,8 @@ class Backend { ['textHasJapaneseCharacters', {async: false, contentScript: true, handler: this._onApiTextHasJapaneseCharacters.bind(this)}], ['getTermFrequencies', {async: true, contentScript: true, handler: this._onApiGetTermFrequencies.bind(this)}], ['findAnkiNotes', {async: true, contentScript: true, handler: this._onApiFindAnkiNotes.bind(this)}], - ['loadExtensionScripts', {async: true, contentScript: true, handler: this._onApiLoadExtensionScripts.bind(this)}] + ['loadExtensionScripts', {async: true, contentScript: true, handler: this._onApiLoadExtensionScripts.bind(this)}], + ['openCrossFramePort', {async: false, contentScript: true, handler: this._onApiOpenCrossFramePort.bind(this)}] ]); this._messageHandlersWithProgress = new Map([ ]); @@ -189,9 +190,6 @@ class Backend { chrome.tabs.onZoomChange.addListener(onZoomChange); } - const onConnect = this._onWebExtensionEventWrapper(this._onConnect.bind(this)); - chrome.runtime.onConnect.addListener(onConnect); - const onMessage = this._onMessageWrapper.bind(this); chrome.runtime.onMessage.addListener(onMessage); @@ -331,58 +329,6 @@ class Backend { return invokeMessageHandler(messageHandler, params, callback, sender); } - _onConnect(port) { - try { - let details; - try { - details = JSON.parse(port.name); - } catch (e) { - return; - } - if (details.name !== 'background-cross-frame-communication-port') { return; } - - const senderTabId = (port.sender && port.sender.tab ? port.sender.tab.id : null); - if (typeof senderTabId !== 'number') { - throw new Error('Port does not have an associated tab ID'); - } - const senderFrameId = port.sender.frameId; - if (typeof senderFrameId !== 'number') { - throw new Error('Port does not have an associated frame ID'); - } - let {targetTabId, targetFrameId} = details; - if (typeof targetTabId !== 'number') { - targetTabId = senderTabId; - } - - const details2 = { - name: 'cross-frame-communication-port', - sourceTabId: senderTabId, - sourceFrameId: senderFrameId - }; - let forwardPort = chrome.tabs.connect(targetTabId, {frameId: targetFrameId, name: JSON.stringify(details2)}); - - const cleanup = () => { - this._checkLastError(chrome.runtime.lastError); - if (forwardPort !== null) { - forwardPort.disconnect(); - forwardPort = null; - } - if (port !== null) { - port.disconnect(); - port = null; - } - }; - - port.onMessage.addListener((message) => { forwardPort.postMessage(message); }); - forwardPort.onMessage.addListener((message) => { port.postMessage(message); }); - port.onDisconnect.addListener(cleanup); - forwardPort.onDisconnect.addListener(cleanup); - } catch (e) { - port.disconnect(); - log.error(e); - } - } - _onZoomChange({tabId, oldZoomFactor, newZoomFactor}) { this._sendMessageTabIgnoreResponse(tabId, {action: 'Yomichan.zoomChanged', params: {oldZoomFactor, newZoomFactor}}); } @@ -2273,4 +2219,48 @@ class Backend { } return results; } + + _onApiOpenCrossFramePort({targetTabId, targetFrameId}, sender) { + const sourceTabId = (sender && sender.tab ? sender.tab.id : null); + if (typeof sourceTabId !== 'number') { + throw new Error('Port does not have an associated tab ID'); + } + const sourceFrameId = sender.frameId; + if (typeof sourceFrameId !== 'number') { + throw new Error('Port does not have an associated frame ID'); + } + + const sourceDetails = { + name: 'cross-frame-communication-port', + otherTabId: targetTabId, + otherFrameId: targetFrameId + }; + const targetDetails = { + name: 'cross-frame-communication-port', + otherTabId: sourceTabId, + otherFrameId: sourceFrameId + }; + let sourcePort = chrome.tabs.connect(sourceTabId, {frameId: sourceFrameId, name: JSON.stringify(sourceDetails)}); + let targetPort = chrome.tabs.connect(targetTabId, {frameId: targetFrameId, name: JSON.stringify(targetDetails)}); + + const cleanup = () => { + console.log('cross-frame cleanup', targetPort, sourcePort); + this._checkLastError(chrome.runtime.lastError); + if (targetPort !== null) { + targetPort.disconnect(); + targetPort = null; + } + if (sourcePort !== null) { + sourcePort.disconnect(); + sourcePort = null; + } + }; + + sourcePort.onMessage.addListener((message) => { targetPort.postMessage(message); }); + targetPort.onMessage.addListener((message) => { sourcePort.postMessage(message); }); + sourcePort.onDisconnect.addListener(cleanup); + targetPort.onDisconnect.addListener(cleanup); + + return {targetTabId, targetFrameId}; + } } diff --git a/ext/js/comm/api.js b/ext/js/comm/api.js index de12bb6c..72d2ba07 100644 --- a/ext/js/comm/api.js +++ b/ext/js/comm/api.js @@ -181,6 +181,10 @@ class API { return this._invoke('loadExtensionScripts', {files}); } + openCrossFramePort(targetTabId, targetFrameId) { + return this._invoke('openCrossFramePort', {targetTabId, targetFrameId}); + } + // Utilities _createActionPort(timeout=5000) { diff --git a/ext/js/comm/cross-frame-api.js b/ext/js/comm/cross-frame-api.js index 7892eb4c..fb2a1718 100644 --- a/ext/js/comm/cross-frame-api.js +++ b/ext/js/comm/cross-frame-api.js @@ -224,10 +224,13 @@ class CrossFrameAPI { this._commPorts = new Map(); this._messageHandlers = new Map(); this._onDisconnectBind = this._onDisconnect.bind(this); + this._tabId = null; + this._frameId = null; } - prepare() { + async prepare() { chrome.runtime.onConnect.addListener(this._onConnect.bind(this)); + ({tabId: this._tabId, frameId: this._frameId} = await yomichan.api.frameInformationGet()); } invoke(targetFrameId, action, params={}) { @@ -235,8 +238,8 @@ class CrossFrameAPI { } async invokeTab(targetTabId, targetFrameId, action, params={}) { - if (typeof targetTabId !== 'number') { targetTabId = null; } - const commPort = this._getOrCreateCommPort(targetTabId, targetFrameId); + if (typeof targetTabId !== 'number') { targetTabId = this._tabId; } + const commPort = await this._getOrCreateCommPort(targetTabId, targetFrameId); return await commPort.invoke(action, params, this._ackTimeout, this._responseTimeout); } @@ -265,8 +268,8 @@ class CrossFrameAPI { } if (details.name !== 'cross-frame-communication-port') { return; } - const otherTabId = details.sourceTabId; - const otherFrameId = details.sourceFrameId; + const otherTabId = details.otherTabId; + const otherFrameId = details.otherFrameId; this._setupCommPort(otherTabId, otherFrameId, port); } catch (e) { port.disconnect(); @@ -297,14 +300,16 @@ class CrossFrameAPI { return this._createCommPort(otherTabId, otherFrameId); } - _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); + async _createCommPort(otherTabId, otherFrameId) { + await yomichan.api.openCrossFramePort(otherTabId, otherFrameId); + + const tabPorts = this._commPorts.get(otherTabId); + if (typeof tabPorts !== 'undefined') { + const commPort = tabPorts.get(otherFrameId); + if (typeof commPort !== 'undefined') { + return commPort; + } + } } _setupCommPort(otherTabId, otherFrameId, port) { -- cgit v1.2.3