diff options
Diffstat (limited to 'ext/js')
-rw-r--r-- | ext/js/app/popup-factory.js | 1 | ||||
-rw-r--r-- | ext/js/background/backend.js | 105 | ||||
-rw-r--r-- | ext/js/comm/api.js | 4 | ||||
-rw-r--r-- | ext/js/comm/cross-frame-api.js | 31 | ||||
-rw-r--r-- | ext/js/core.js | 4 | ||||
-rw-r--r-- | ext/js/dom/dom-text-scanner.js | 4 | ||||
-rw-r--r-- | ext/js/dom/sandbox/css-style-applier.js | 4 | ||||
-rw-r--r-- | ext/js/language/translator.js | 6 | ||||
-rw-r--r-- | ext/js/yomichan.js | 28 |
9 files changed, 89 insertions, 98 deletions
diff --git a/ext/js/app/popup-factory.js b/ext/js/app/popup-factory.js index eeb31a1b..c3c98162 100644 --- a/ext/js/app/popup-factory.js +++ b/ext/js/app/popup-factory.js @@ -71,6 +71,7 @@ class PopupFactory { * @param {?number} [details.depth] A specific depth value to assign to the popup. * @param {boolean} [details.popupWindow] Whether or not a separate popup window should be used, rather than an iframe. * @param {boolean} [details.childrenSupported] Whether or not the popup is able to show child popups. + * @returns {Popup|PopupWindow|PopupProxy} The new or existing popup. */ async getOrCreatePopup({ frameId=null, diff --git a/ext/js/background/backend.js b/ext/js/background/backend.js index dd233abb..565f4abf 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); @@ -228,8 +226,8 @@ class Backend { log.error(e); } - const deinflectionReasions = await this._fetchAsset('/data/deinflect.json', true); - this._translator.prepare(deinflectionReasions); + const deinflectionReasons = await this._fetchAsset('/data/deinflect.json', true); + this._translator.prepare(deinflectionReasons); await this._optionsUtil.prepare(); this._defaultAnkiFieldTemplates = (await this._fetchAsset('/data/templates/default-anki-field-templates.handlebars')).trim(); @@ -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,47 @@ 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 = () => { + 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) { diff --git a/ext/js/core.js b/ext/js/core.js index 5b064a36..1e749c7d 100644 --- a/ext/js/core.js +++ b/ext/js/core.js @@ -309,7 +309,7 @@ function promiseTimeout(delay, resolveValue) { * Creates a promise that will resolve after the next animation frame, using `requestAnimationFrame`. * @param {number} [timeout] A maximum duration (in milliseconds) to wait until the promise resolves. If null or omitted, no timeout is used. * @returns {Promise<{time: number, timeout: number}>} A promise that is resolved with `{time, timeout}`, where `time` is the timestamp from `requestAnimationFrame`, - * and `timeout` is a boolean indicating whether the cause was a timeout or not. + * and `timeout` is a boolean indicating whether the cause was a timeout or not. * @throws The promise throws an error if animation is not supported in this context, such as in a service worker. */ function promiseAnimationFrame(timeout=null) { @@ -609,7 +609,7 @@ class DynamicProperty extends EventDispatcher { * @param {*} value The override value to assign. * @param {number} [priority] The priority value to use, as a number. * @returns {string} A string token which can be passed to the clearOverride function - * to remove the override. + * to remove the override. */ setOverride(value, priority=0) { const overridesCount = this._overrides.length; diff --git a/ext/js/dom/dom-text-scanner.js b/ext/js/dom/dom-text-scanner.js index dc5bd96a..ec4c7bd6 100644 --- a/ext/js/dom/dom-text-scanner.js +++ b/ext/js/dom/dom-text-scanner.js @@ -405,8 +405,8 @@ class DOMTextScanner { * @returns {{enterable: boolean, newlines: number}} The seek information. * The `enterable` value indicates whether the content of this node should be entered. * The `newlines` value corresponds to the number of newline characters that should be added. - * 1 newline corresponds to a simple new line in the layout. - * 2 newlines corresponds to a significant visual distinction since the previous content. + * - 1 newline corresponds to a simple new line in the layout. + * - 2 newlines corresponds to a significant visual distinction since the previous content. */ static getElementSeekInfo(element) { let enterable = true; diff --git a/ext/js/dom/sandbox/css-style-applier.js b/ext/js/dom/sandbox/css-style-applier.js index 01936d26..a47ef6ef 100644 --- a/ext/js/dom/sandbox/css-style-applier.js +++ b/ext/js/dom/sandbox/css-style-applier.js @@ -35,9 +35,10 @@ class CssStyleApplier { * @property {string} value The property's value. */ + /* eslint-disable jsdoc/check-line-alignment */ /** * Creates a new instance of the class. - * @param {string} styleDataUrl The local URL to the JSON file continaing the style rules. + * @param {string} styleDataUrl The local URL to the JSON file containing the style rules. * The style rules should be of the format: * ``` * [ @@ -57,6 +58,7 @@ class CssStyleApplier { this._patternHtmlWhitespace = /[\t\r\n\f ]+/g; this._patternClassNameCharacter = /[0-9a-zA-Z-_]/; } + /* eslint-enable jsdoc/check-line-alignment */ /** * Loads the data file for use. diff --git a/ext/js/language/translator.js b/ext/js/language/translator.js index edb38bfb..3b47cc51 100644 --- a/ext/js/language/translator.js +++ b/ext/js/language/translator.js @@ -93,6 +93,12 @@ class Translator { } if (mode === 'simple') { + if (sortFrequencyDictionary !== null) { + const sortDictionaryMap = [sortFrequencyDictionary] + .filter((key) => enabledDictionaryMap.has(key)) + .reduce((subMap, key) => subMap.set(key, enabledDictionaryMap.get(key)), new Map()); + await this._addTermMeta(dictionaryEntries, sortDictionaryMap); + } this._clearTermTags(dictionaryEntries); } else { await this._addTermMeta(dictionaryEntries, enabledDictionaryMap); diff --git a/ext/js/yomichan.js b/ext/js/yomichan.js index 6eea952e..a4feaa23 100644 --- a/ext/js/yomichan.js +++ b/ext/js/yomichan.js @@ -129,12 +129,12 @@ class Yomichan extends EventDispatcher { if (!isBackground) { this._api = new API(this); - this._crossFrame = new CrossFrameAPI(); - this._crossFrame.prepare(); - this.sendMessage({action: 'requestBackendReadySignal'}); await this._isBackendReadyPromise; + this._crossFrame = new CrossFrameAPI(); + await this._crossFrame.prepare(); + log.on('log', this._onForwardLog.bind(this)); } } @@ -172,24 +172,6 @@ class Yomichan extends EventDispatcher { } } - /** - * Runs `chrome.runtime.connect()` with additional exception handling events. - * @param {...*} args The arguments to be passed to `chrome.runtime.connect()`. - * @returns {Port} The resulting port. - * @throws {Error} Errors thrown by `chrome.runtime.connect()` are re-thrown. - */ - connect(...args) { - try { - return chrome.runtime.connect(...args); - } catch (e) { - this.triggerExtensionUnloaded(); - throw e; - } - } - - /** - * Runs chrome.runtime.connect() with additional exception handling events. - */ triggerExtensionUnloaded() { this._isExtensionUnloaded = true; if (this._isTriggeringExtensionUnloaded) { return; } @@ -232,7 +214,9 @@ class Yomichan extends EventDispatcher { } _onMessageOptionsUpdated({source}) { - this.trigger('optionsUpdated', {source}); + if (source !== 'background') { + this.trigger('optionsUpdated', {source}); + } } _onMessageDatabaseUpdated({type, cause}) { |