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] |