diff options
Diffstat (limited to 'ext/js/comm')
-rw-r--r-- | ext/js/comm/api.js | 502 | ||||
-rw-r--r-- | ext/js/comm/frame-ancestry-handler.js | 10 | ||||
-rw-r--r-- | ext/js/comm/frame-endpoint.js | 8 | ||||
-rw-r--r-- | ext/js/comm/frame-offset-forwarder.js | 5 |
4 files changed, 244 insertions, 281 deletions
diff --git a/ext/js/comm/api.js b/ext/js/comm/api.js index 26397d1f..472e464b 100644 --- a/ext/js/comm/api.js +++ b/ext/js/comm/api.js @@ -15,319 +15,291 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ -/* global - * CrossFrameAPI - */ +class API { + constructor(yomichan) { + this._yomichan = yomichan; + } -const api = (() => { - class API { - constructor() { - this._prepared = false; - this._crossFrame = null; - } - - get crossFrame() { - return this._crossFrame; - } - - prepare() { - if (this._prepared) { return; } - this._crossFrame = new CrossFrameAPI(); - this._crossFrame.prepare(); - yomichan.on('log', this._onLog.bind(this)); - this._prepared = true; - } - - // Invoke functions + optionsGet(optionsContext) { + return this._invoke('optionsGet', {optionsContext}); + } - optionsGet(optionsContext) { - return this._invoke('optionsGet', {optionsContext}); - } + optionsGetFull() { + return this._invoke('optionsGetFull'); + } - optionsGetFull() { - return this._invoke('optionsGetFull'); - } + termsFind(text, details, optionsContext) { + return this._invoke('termsFind', {text, details, optionsContext}); + } - termsFind(text, details, optionsContext) { - return this._invoke('termsFind', {text, details, optionsContext}); - } + textParse(text, optionsContext) { + return this._invoke('textParse', {text, optionsContext}); + } - textParse(text, optionsContext) { - return this._invoke('textParse', {text, optionsContext}); - } + kanjiFind(text, optionsContext) { + return this._invoke('kanjiFind', {text, optionsContext}); + } - kanjiFind(text, optionsContext) { - return this._invoke('kanjiFind', {text, optionsContext}); - } + isAnkiConnected() { + return this._invoke('isAnkiConnected'); + } - isAnkiConnected() { - return this._invoke('isAnkiConnected'); - } + getAnkiConnectVersion() { + return this._invoke('getAnkiConnectVersion'); + } - getAnkiConnectVersion() { - return this._invoke('getAnkiConnectVersion'); - } + addAnkiNote(note) { + return this._invoke('addAnkiNote', {note}); + } - addAnkiNote(note) { - return this._invoke('addAnkiNote', {note}); - } + getAnkiNoteInfo(notes) { + return this._invoke('getAnkiNoteInfo', {notes}); + } - getAnkiNoteInfo(notes) { - return this._invoke('getAnkiNoteInfo', {notes}); - } + injectAnkiNoteMedia(timestamp, definitionDetails, audioDetails, screenshotDetails, clipboardDetails) { + return this._invoke('injectAnkiNoteMedia', {timestamp, definitionDetails, audioDetails, screenshotDetails, clipboardDetails}); + } - injectAnkiNoteMedia(timestamp, definitionDetails, audioDetails, screenshotDetails, clipboardDetails) { - return this._invoke('injectAnkiNoteMedia', {timestamp, definitionDetails, audioDetails, screenshotDetails, clipboardDetails}); - } + noteView(noteId) { + return this._invoke('noteView', {noteId}); + } - noteView(noteId) { - return this._invoke('noteView', {noteId}); - } + suspendAnkiCardsForNote(noteId) { + return this._invoke('suspendAnkiCardsForNote', {noteId}); + } - suspendAnkiCardsForNote(noteId) { - return this._invoke('suspendAnkiCardsForNote', {noteId}); - } + getExpressionAudioInfoList(source, expression, reading, details) { + return this._invoke('getExpressionAudioInfoList', {source, expression, reading, details}); + } - getExpressionAudioInfoList(source, expression, reading, details) { - return this._invoke('getExpressionAudioInfoList', {source, expression, reading, details}); - } + commandExec(command, params) { + return this._invoke('commandExec', {command, params}); + } - commandExec(command, params) { - return this._invoke('commandExec', {command, params}); - } - - sendMessageToFrame(frameId, action, params) { - return this._invoke('sendMessageToFrame', {frameId, action, params}); - } - - broadcastTab(action, params) { - return this._invoke('broadcastTab', {action, params}); - } - - frameInformationGet() { - return this._invoke('frameInformationGet'); - } - - injectStylesheet(type, value) { - return this._invoke('injectStylesheet', {type, value}); - } - - getStylesheetContent(url) { - return this._invoke('getStylesheetContent', {url}); - } - - getEnvironmentInfo() { - return this._invoke('getEnvironmentInfo'); - } - - clipboardGet() { - return this._invoke('clipboardGet'); - } - - getDisplayTemplatesHtml() { - return this._invoke('getDisplayTemplatesHtml'); - } - - getZoom() { - return this._invoke('getZoom'); - } - - getDefaultAnkiFieldTemplates() { - return this._invoke('getDefaultAnkiFieldTemplates'); - } + sendMessageToFrame(frameId, action, params) { + return this._invoke('sendMessageToFrame', {frameId, action, params}); + } - getDictionaryInfo() { - return this._invoke('getDictionaryInfo'); - } + broadcastTab(action, params) { + return this._invoke('broadcastTab', {action, params}); + } - getDictionaryCounts(dictionaryNames, getTotal) { - return this._invoke('getDictionaryCounts', {dictionaryNames, getTotal}); - } + frameInformationGet() { + return this._invoke('frameInformationGet'); + } - purgeDatabase() { - return this._invoke('purgeDatabase'); - } + injectStylesheet(type, value) { + return this._invoke('injectStylesheet', {type, value}); + } - getMedia(targets) { - return this._invoke('getMedia', {targets}); - } + getStylesheetContent(url) { + return this._invoke('getStylesheetContent', {url}); + } - logIndicatorClear() { - return this._invoke('logIndicatorClear'); - } + getEnvironmentInfo() { + return this._invoke('getEnvironmentInfo'); + } - modifySettings(targets, source) { - return this._invoke('modifySettings', {targets, source}); - } + clipboardGet() { + return this._invoke('clipboardGet'); + } - getSettings(targets) { - return this._invoke('getSettings', {targets}); - } + getDisplayTemplatesHtml() { + return this._invoke('getDisplayTemplatesHtml'); + } - setAllSettings(value, source) { - return this._invoke('setAllSettings', {value, source}); - } + getZoom() { + return this._invoke('getZoom'); + } - getOrCreateSearchPopup(details) { - return this._invoke('getOrCreateSearchPopup', isObject(details) ? details : {}); - } + getDefaultAnkiFieldTemplates() { + return this._invoke('getDefaultAnkiFieldTemplates'); + } - isTabSearchPopup(tabId) { - return this._invoke('isTabSearchPopup', {tabId}); - } + getDictionaryInfo() { + return this._invoke('getDictionaryInfo'); + } - triggerDatabaseUpdated(type, cause) { - return this._invoke('triggerDatabaseUpdated', {type, cause}); - } + getDictionaryCounts(dictionaryNames, getTotal) { + return this._invoke('getDictionaryCounts', {dictionaryNames, getTotal}); + } - testMecab() { - return this._invoke('testMecab', {}); - } + purgeDatabase() { + return this._invoke('purgeDatabase'); + } - // Utilities - - _createActionPort(timeout=5000) { - return new Promise((resolve, reject) => { - let timer = null; - const portDetails = deferPromise(); - - const onConnect = async (port) => { - try { - 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; - } + getMedia(targets) { + return this._invoke('getMedia', {targets}); + } - clearTimeout(timer); - timer = null; + log(error, level, context) { + return this._invoke('log', {error, level, context}); + } - chrome.runtime.onConnect.removeListener(onConnect); - resolve(port); - }; + logIndicatorClear() { + return this._invoke('logIndicatorClear'); + } - const onError = (e) => { - if (timer !== null) { - clearTimeout(timer); - timer = null; - } - chrome.runtime.onConnect.removeListener(onConnect); - portDetails.reject(e); - reject(e); - }; + modifySettings(targets, source) { + return this._invoke('modifySettings', {targets, source}); + } - timer = setTimeout(() => onError(new Error('Timeout')), timeout); + getSettings(targets) { + return this._invoke('getSettings', {targets}); + } - chrome.runtime.onConnect.addListener(onConnect); - this._invoke('createActionPort').then(portDetails.resolve, onError); - }); - } + setAllSettings(value, source) { + return this._invoke('setAllSettings', {value, source}); + } - _invokeWithProgress(action, params, onProgress, timeout=5000) { - return new Promise((resolve, reject) => { - let port = null; + getOrCreateSearchPopup(details) { + return this._invoke('getOrCreateSearchPopup', isObject(details) ? details : {}); + } + + isTabSearchPopup(tabId) { + return this._invoke('isTabSearchPopup', {tabId}); + } + + triggerDatabaseUpdated(type, cause) { + return this._invoke('triggerDatabaseUpdated', {type, cause}); + } + + testMecab() { + return this._invoke('testMecab', {}); + } + + // Utilities + + _createActionPort(timeout=5000) { + return new Promise((resolve, reject) => { + let timer = null; + const portDetails = deferPromise(); + + const onConnect = async (port) => { + try { + 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; + } + + clearTimeout(timer); + timer = null; + + chrome.runtime.onConnect.removeListener(onConnect); + resolve(port); + }; - if (typeof onProgress !== 'function') { - onProgress = () => {}; + const onError = (e) => { + if (timer !== null) { + clearTimeout(timer); + timer = null; } + chrome.runtime.onConnect.removeListener(onConnect); + portDetails.reject(e); + reject(e); + }; - const onMessage = (message) => { - switch (message.type) { - case 'progress': - try { - onProgress(...message.data); - } catch (e) { - // NOP - } - break; - case 'complete': - cleanup(); - resolve(message.data); - break; - case 'error': - cleanup(); - reject(deserializeError(message.data)); - break; - } - }; + timer = setTimeout(() => onError(new Error('Timeout')), timeout); - const onDisconnect = () => { - cleanup(); - reject(new Error('Disconnected')); - }; - - const cleanup = () => { - if (port !== null) { - port.onMessage.removeListener(onMessage); - port.onDisconnect.removeListener(onDisconnect); - port.disconnect(); - port = null; - } - onProgress = null; - }; - - (async () => { - try { - port = await this._createActionPort(timeout); - port.onMessage.addListener(onMessage); - port.onDisconnect.addListener(onDisconnect); - - // Chrome has a maximum message size that can be sent, so longer messages must be fragmented. - const messageString = JSON.stringify({action, params}); - const fragmentSize = 1e7; // 10 MB - for (let i = 0, ii = messageString.length; i < ii; i += fragmentSize) { - const data = messageString.substring(i, i + fragmentSize); - port.postMessage({action: 'fragment', data}); + chrome.runtime.onConnect.addListener(onConnect); + this._invoke('createActionPort').then(portDetails.resolve, onError); + }); + } + + _invokeWithProgress(action, params, onProgress, timeout=5000) { + return new Promise((resolve, reject) => { + let port = null; + + if (typeof onProgress !== 'function') { + onProgress = () => {}; + } + + const onMessage = (message) => { + switch (message.type) { + case 'progress': + try { + onProgress(...message.data); + } catch (e) { + // NOP } - port.postMessage({action: 'invoke'}); - } catch (e) { + break; + case 'complete': cleanup(); - reject(e); - } finally { - action = null; - params = null; - } - })(); - }); - } + resolve(message.data); + break; + case 'error': + cleanup(); + reject(deserializeError(message.data)); + break; + } + }; + + const onDisconnect = () => { + cleanup(); + reject(new Error('Disconnected')); + }; + + const cleanup = () => { + if (port !== null) { + port.onMessage.removeListener(onMessage); + port.onDisconnect.removeListener(onDisconnect); + port.disconnect(); + port = null; + } + onProgress = null; + }; - _invoke(action, params={}) { - const data = {action, params}; - return new Promise((resolve, reject) => { + (async () => { try { - yomichan.sendMessage(data, (response) => { - this._checkLastError(chrome.runtime.lastError); - if (response !== null && typeof response === 'object') { - if (typeof response.error !== 'undefined') { - reject(deserializeError(response.error)); - } else { - resolve(response.result); - } - } else { - const message = response === null ? 'Unexpected null response' : `Unexpected response of type ${typeof response}`; - reject(new Error(`${message} (${JSON.stringify(data)})`)); - } - }); + port = await this._createActionPort(timeout); + port.onMessage.addListener(onMessage); + port.onDisconnect.addListener(onDisconnect); + + // Chrome has a maximum message size that can be sent, so longer messages must be fragmented. + const messageString = JSON.stringify({action, params}); + const fragmentSize = 1e7; // 10 MB + for (let i = 0, ii = messageString.length; i < ii; i += fragmentSize) { + const data = messageString.substring(i, i + fragmentSize); + port.postMessage({action: 'fragment', data}); + } + port.postMessage({action: 'invoke'}); } catch (e) { + cleanup(); reject(e); + } finally { + action = null; + params = null; } - }); - } - - _checkLastError() { - // NOP - } + })(); + }); + } - async _onLog({error, level, context}) { + _invoke(action, params={}) { + const data = {action, params}; + return new Promise((resolve, reject) => { try { - error = serializeError(error); - await this._invoke('log', {error, level, context}); + this._yomichan.sendMessage(data, (response) => { + this._checkLastError(chrome.runtime.lastError); + if (response !== null && typeof response === 'object') { + if (typeof response.error !== 'undefined') { + reject(deserializeError(response.error)); + } else { + resolve(response.result); + } + } else { + const message = response === null ? 'Unexpected null response' : `Unexpected response of type ${typeof response}`; + reject(new Error(`${message} (${JSON.stringify(data)})`)); + } + }); } catch (e) { - // NOP + reject(e); } - } + }); } - return new API(); -})(); + _checkLastError() { + // NOP + } +} diff --git a/ext/js/comm/frame-ancestry-handler.js b/ext/js/comm/frame-ancestry-handler.js index b1ed7114..d53334e1 100644 --- a/ext/js/comm/frame-ancestry-handler.js +++ b/ext/js/comm/frame-ancestry-handler.js @@ -15,10 +15,6 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ -/* global - * api - */ - /** * This class is used to return the ancestor frame IDs for the current frame. * This is a workaround to using the `webNavigation.getAllFrames` API, which @@ -118,7 +114,7 @@ class FrameAncestryHandler { clearTimeout(timer); timer = null; } - api.crossFrame.unregisterHandler(responseMessageId); + yomichan.crossFrame.unregisterHandler(responseMessageId); }; const onMessage = (params) => { if (params.nonce !== nonce) { return null; } @@ -148,7 +144,7 @@ class FrameAncestryHandler { }; // Start - api.crossFrame.registerHandlers([[responseMessageId, {async: false, handler: onMessage}]]); + yomichan.crossFrame.registerHandlers([[responseMessageId, {async: false, handler: onMessage}]]); resetTimeout(); const frameId = this._frameId; this._requestFrameInfo(targetWindow, frameId, frameId, uniqueId, nonce); @@ -187,7 +183,7 @@ class FrameAncestryHandler { const responseMessageId = `${this._responseMessageIdBase}${uniqueId}`; try { - const response = await api.crossFrame.invoke(originFrameId, responseMessageId, responseParams); + const response = await yomichan.crossFrame.invoke(originFrameId, responseMessageId, responseParams); if (response === null) { return; } nonce = response.nonce; } catch (e) { diff --git a/ext/js/comm/frame-endpoint.js b/ext/js/comm/frame-endpoint.js index 27af9cf3..bc3c50f8 100644 --- a/ext/js/comm/frame-endpoint.js +++ b/ext/js/comm/frame-endpoint.js @@ -15,10 +15,6 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ -/* global - * api - */ - class FrameEndpoint { constructor() { this._secret = generateId(16); @@ -32,7 +28,7 @@ class FrameEndpoint { this._eventListeners.addEventListener(window, 'message', this._onMessage.bind(this), false); this._eventListenersSetup = true; } - api.broadcastTab('frameEndpointReady', {secret: this._secret}); + yomichan.api.broadcastTab('frameEndpointReady', {secret: this._secret}); } authenticate(message) { @@ -60,6 +56,6 @@ class FrameEndpoint { this._token = token; this._eventListeners.removeAllEventListeners(); - api.sendMessageToFrame(hostFrameId, 'frameEndpointConnected', {secret, token}); + yomichan.api.sendMessageToFrame(hostFrameId, 'frameEndpointConnected', {secret, token}); } } diff --git a/ext/js/comm/frame-offset-forwarder.js b/ext/js/comm/frame-offset-forwarder.js index 0a0b4a18..2382a9fa 100644 --- a/ext/js/comm/frame-offset-forwarder.js +++ b/ext/js/comm/frame-offset-forwarder.js @@ -17,7 +17,6 @@ /* global * FrameAncestryHandler - * api */ class FrameOffsetForwarder { @@ -28,7 +27,7 @@ class FrameOffsetForwarder { prepare() { this._frameAncestryHandler.prepare(); - api.crossFrame.registerHandlers([ + yomichan.crossFrame.registerHandlers([ ['FrameOffsetForwarder.getChildFrameRect', {async: false, handler: this._onMessageGetChildFrameRect.bind(this)}] ]); } @@ -43,7 +42,7 @@ class FrameOffsetForwarder { let childFrameId = this._frameId; const promises = []; for (const frameId of ancestorFrameIds) { - promises.push(api.crossFrame.invoke(frameId, 'FrameOffsetForwarder.getChildFrameRect', {frameId: childFrameId})); + promises.push(yomichan.crossFrame.invoke(frameId, 'FrameOffsetForwarder.getChildFrameRect', {frameId: childFrameId})); childFrameId = frameId; } |