diff options
Diffstat (limited to 'ext/js')
-rw-r--r-- | ext/js/background/backend.js | 151 | ||||
-rw-r--r-- | ext/js/comm/api.js | 129 |
2 files changed, 2 insertions, 278 deletions
diff --git a/ext/js/background/backend.js b/ext/js/background/backend.js index d62c852b..c62685b2 100644 --- a/ext/js/background/backend.js +++ b/ext/js/background/backend.js @@ -22,9 +22,9 @@ import {AnkiConnect} from '../comm/anki-connect.js'; import {ClipboardMonitor} from '../comm/clipboard-monitor.js'; import {ClipboardReader} from '../comm/clipboard-reader.js'; import {Mecab} from '../comm/mecab.js'; -import {clone, deferPromise, generateId, invokeMessageHandler, isObject, log, promiseTimeout} from '../core.js'; +import {clone, deferPromise, invokeMessageHandler, isObject, log, promiseTimeout} from '../core.js'; import {ExtensionError} from '../core/extension-error.js'; -import {parseJson, readResponseJson} from '../core/json.js'; +import {readResponseJson} from '../core/json.js'; import {AnkiUtil} from '../data/anki-util.js'; import {OptionsUtil} from '../data/options-util.js'; import {PermissionsUtil} from '../data/permissions-util.js'; @@ -36,7 +36,6 @@ import {JapaneseUtil} from '../language/sandbox/japanese-util.js'; import {Translator} from '../language/translator.js'; import {AudioDownloader} from '../media/audio-downloader.js'; import {MediaUtil} from '../media/media-util.js'; -import {yomitan} from '../yomitan.js'; import {ClipboardReaderProxy, DictionaryDatabaseProxy, OffscreenProxy, TranslatorProxy} from './offscreen-proxy.js'; import {ProfileConditionsUtil} from './profile-conditions-util.js'; import {RequestBuilder} from './request-builder.js'; @@ -179,7 +178,6 @@ export class Backend { ['getMedia', this._onApiGetMedia.bind(this)], ['log', this._onApiLog.bind(this)], ['logIndicatorClear', this._onApiLogIndicatorClear.bind(this)], - ['createActionPort', this._onApiCreateActionPort.bind(this)], ['modifySettings', this._onApiModifySettings.bind(this)], ['getSettings', this._onApiGetSettings.bind(this)], ['setAllSettings', this._onApiSetAllSettings.bind(this)], @@ -194,10 +192,6 @@ export class Backend { ['openCrossFramePort', this._onApiOpenCrossFramePort.bind(this)] ])); /* eslint-enable no-multi-spaces */ - /** @type {import('backend').MessageHandlerWithProgressMap} */ - this._messageHandlersWithProgress = new Map(/** @type {import('backend').MessageHandlerWithProgressMapInit} */ ([ - // Empty - ])); /** @type {Map<string, (params?: import('core').SerializableObject) => void>} */ this._commandHandlers = new Map(/** @type {[name: string, handler: (params?: import('core').SerializableObject) => void][]} */ ([ @@ -746,31 +740,6 @@ export class Backend { this._updateBadge(); } - /** @type {import('api').Handler<import('api').CreateActionPortDetails, import('api').CreateActionPortResult, true>} */ - _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 = generateId(16); - /** @type {import('cross-frame-api').ActionPortDetails} */ - const details = { - name: 'action-port', - id - }; - - const port = chrome.tabs.connect(tabId, {name: JSON.stringify(details), frameId}); - try { - this._createActionListenerPort(port, sender, this._messageHandlersWithProgress); - } catch (e) { - port.disconnect(); - throw e; - } - - return details; - } - /** @type {import('api').Handler<import('api').ModifySettingsDetails, import('api').ModifySettingsResult>} */ _onApiModifySettings({targets, source}) { return this._modifySettings(targets, source); @@ -1484,107 +1453,6 @@ export class Backend { } /** - * @param {chrome.runtime.Port} port - * @param {chrome.runtime.MessageSender} sender - * @param {import('backend').MessageHandlerWithProgressMap} handlers - */ - _createActionListenerPort(port, sender, handlers) { - let done = false; - let hasStarted = false; - /** @type {?string} */ - let messageString = ''; - - /** - * @param {...unknown} data - */ - const onProgress = (...data) => { - try { - if (done) { return; } - port.postMessage(/** @type {import('backend').InvokeWithProgressResponseProgressMessage} */ ({type: 'progress', data})); - } catch (e) { - // NOP - } - }; - - /** - * @param {import('backend').InvokeWithProgressRequestMessage} message - */ - const onMessage = (message) => { - if (hasStarted) { return; } - - try { - const {action} = message; - switch (action) { - case 'fragment': - messageString += message.data; - break; - case 'invoke': - if (messageString !== null) { - hasStarted = true; - port.onMessage.removeListener(onMessage); - - /** @type {{action: string, params?: import('core').SerializableObject}} */ - const messageData = parseJson(messageString); - messageString = null; - onMessageComplete(messageData); - } - break; - } - } catch (e) { - cleanup(e); - } - }; - - /** - * @param {{action: string, params?: import('core').SerializableObject}} message - */ - const onMessageComplete = async (message) => { - try { - const {action, params} = message; - port.postMessage(/** @type {import('backend').InvokeWithProgressResponseAcknowledgeMessage} */ ({type: 'ack'})); - - const messageHandler = handlers.get(action); - if (typeof messageHandler === 'undefined') { - throw new Error('Invalid action'); - } - const {handler, async, contentScript} = messageHandler; - - if (!contentScript) { - this._validatePrivilegedMessageSender(sender); - } - - const promiseOrResult = handler(params, sender, onProgress); - const result = async ? await promiseOrResult : promiseOrResult; - port.postMessage(/** @type {import('backend').InvokeWithProgressResponseCompleteMessage} */ ({type: 'complete', data: result})); - } catch (e) { - cleanup(e); - } - }; - - const onDisconnect = () => { - cleanup(null); - }; - - /** - * @param {unknown} error - */ - const cleanup = (error) => { - if (done) { return; } - if (error !== null) { - port.postMessage(/** @type {import('backend').InvokeWithProgressResponseErrorMessage} */ ({type: 'error', data: ExtensionError.serialize(error)})); - } - if (!hasStarted) { - port.onMessage.removeListener(onMessage); - } - port.onDisconnect.removeListener(onDisconnect); - done = true; - }; - - port.onMessage.addListener(onMessage); - port.onDisconnect.addListener(onDisconnect); - } - - /** * @param {?import('log').LogLevel} errorLevel * @returns {number} */ @@ -1693,21 +1561,6 @@ export class Backend { } /** - * @param {chrome.runtime.MessageSender} sender - * @throws {Error} - */ - _validatePrivilegedMessageSender(sender) { - let {url} = sender; - if (typeof url === 'string' && yomitan.isExtensionUrl(url)) { return; } - const {tab} = sender; - if (typeof tab === 'object' && tab !== null) { - ({url} = tab); - if (typeof url === 'string' && yomitan.isExtensionUrl(url)) { return; } - } - throw new Error('Invalid message sender'); - } - - /** * @returns {Promise<string>} */ _getBrowserIconTitle() { diff --git a/ext/js/comm/api.js b/ext/js/comm/api.js index 43f707e2..de19650d 100644 --- a/ext/js/comm/api.js +++ b/ext/js/comm/api.js @@ -16,9 +16,7 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ -import {deferPromise} from '../core.js'; import {ExtensionError} from '../core/extension-error.js'; -import {parseJson} from '../core/json.js'; export class API { /** @@ -427,133 +425,6 @@ export class API { // Utilities /** - * @param {number} timeout - * @returns {Promise<chrome.runtime.Port>} - */ - _createActionPort(timeout) { - return new Promise((resolve, reject) => { - /** @type {?import('core').Timeout} */ - let timer = null; - /** @type {import('core').DeferredPromiseDetails<import('api').CreateActionPortResult>} */ - const portDetails = deferPromise(); - - /** - * @param {chrome.runtime.Port} port - */ - const onConnect = async (port) => { - try { - const {name: expectedName, id: expectedId} = await portDetails.promise; - /** @type {import('cross-frame-api').PortDetails} */ - const portDetails2 = parseJson(port.name); - if (portDetails2.name !== expectedName || portDetails2.id !== expectedId || timer === null) { return; } - } catch (e) { - return; - } - - clearTimeout(timer); - timer = null; - - chrome.runtime.onConnect.removeListener(onConnect); - resolve(port); - }; - - /** - * @param {Error} e - */ - const onError = (e) => { - if (timer !== null) { - clearTimeout(timer); - timer = null; - } - chrome.runtime.onConnect.removeListener(onConnect); - portDetails.reject(e); - reject(e); - }; - - timer = setTimeout(() => onError(new Error('Timeout')), timeout); - - chrome.runtime.onConnect.addListener(onConnect); - /** @type {Promise<import('api').CreateActionPortResult>} */ - const createActionPortResult = this._invoke('createActionPort'); - createActionPortResult.then(portDetails.resolve, onError); - }); - } - - /** - * @template [TReturn=unknown] - * @param {string} action - * @param {import('core').SerializableObject} params - * @param {?(...args: unknown[]) => void} onProgress0 - * @param {number} [timeout] - * @returns {Promise<TReturn>} - */ - _invokeWithProgress(action, params, onProgress0, timeout = 5000) { - return new Promise((resolve, reject) => { - /** @type {?chrome.runtime.Port} */ - let port = null; - - const onProgress = typeof onProgress0 === 'function' ? onProgress0 : () => {}; - - /** - * @param {import('backend').InvokeWithProgressResponseMessage<TReturn>} message - */ - 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(ExtensionError.deserialize(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; - } - }; - - (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(/** @type {import('backend').InvokeWithProgressRequestFragmentMessage} */ ({action: 'fragment', data})); - } - port.postMessage(/** @type {import('backend').InvokeWithProgressRequestInvokeMessage} */ ({action: 'invoke'})); - } catch (e) { - cleanup(); - reject(e); - } - })(); - }); - } - - /** * @template [TReturn=unknown] * @param {string} action * @param {import('core').SerializableObject} [params] |