From 7303da3991814a0ce220bf2fff3e51b968913b86 Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Fri, 29 Dec 2023 19:17:46 -0500 Subject: Cross frame API safety (#491) * Require error type * Add TODOs * Fix init * Updates * More type safety * Fix incorrect API map * Update type safety * Updates * Add API * Update types * Update types * Updates * Remove unused * Restore types * Update frame ancestry handler * Simplify names * Fix * Remove old message handlers --- ext/js/display/display.js | 74 +++++++++++++++++++++++------------------------ 1 file changed, 36 insertions(+), 38 deletions(-) (limited to 'ext/js/display/display.js') diff --git a/ext/js/display/display.js b/ext/js/display/display.js index e3c92ee2..cae394f8 100644 --- a/ext/js/display/display.js +++ b/ext/js/display/display.js @@ -19,7 +19,7 @@ import {ThemeController} from '../app/theme-controller.js'; import {FrameEndpoint} from '../comm/frame-endpoint.js'; import {DynamicProperty, EventDispatcher, EventListenerCollection, clone, deepEqual, log, promiseTimeout} from '../core.js'; -import {invokeApiMapHandler} from '../core/api-map.js'; +import {extendApiMap, invokeApiMapHandler} from '../core/api-map.js'; import {ExtensionError} from '../core/extension-error.js'; import {PopupMenu} from '../dom/popup-menu.js'; import {querySelectorNotNull} from '../dom/query-selector.js'; @@ -91,7 +91,7 @@ export class Display extends EventDispatcher { }); /** @type {import('display').DirectApiMap} */ this._directApiMap = new Map(); - /** @type {import('display').WindowApiMap} */ + /** @type {import('api-map').ApiMap} */ // import('display').WindowApiMap this._windowApiMap = new Map(); /** @type {DisplayHistory} */ this._history = new DisplayHistory({clearable: true, useBrowserHistory: false}); @@ -328,7 +328,8 @@ export class Display extends EventDispatcher { this._progressIndicatorVisible.on('change', this._onProgressIndicatorVisibleChanged.bind(this)); yomitan.on('extensionUnloaded', this._onExtensionUnloaded.bind(this)); yomitan.crossFrame.registerHandlers([ - ['popupMessage', this._onDirectMessage.bind(this)] + ['displayPopupMessage1', this._onDisplayPopupMessage1.bind(this)], + ['displayPopupMessage2', this._onDisplayPopupMessage2.bind(this)] ]); window.addEventListener('message', this._onWindowMessage.bind(this), false); @@ -507,25 +508,21 @@ export class Display extends EventDispatcher { * @param {import('display').DirectApiMapInit} handlers */ registerDirectMessageHandlers(handlers) { - for (const [name, handlerInfo] of handlers) { - this._directApiMap.set(name, handlerInfo); - } + extendApiMap(this._directApiMap, handlers); } /** * @param {import('display').WindowApiMapInit} handlers */ registerWindowMessageHandlers(handlers) { - for (const [name, handlerInfo] of handlers) { - this._windowApiMap.set(name, handlerInfo); - } + extendApiMap(this._windowApiMap, handlers); } /** */ close() { switch (this._pageType) { case 'popup': - this.invokeContentOrigin('Frontend.closePopup'); + this.invokeContentOrigin('frontendClosePopup', void 0); break; case 'search': this._closeTab(); @@ -578,12 +575,12 @@ export class Display extends EventDispatcher { } /** - * @template [TReturn=unknown] - * @param {string} action - * @param {import('core').SerializableObject} [params] - * @returns {Promise} + * @template {import('cross-frame-api').ApiNames} TName + * @param {TName} action + * @param {import('cross-frame-api').ApiParams} params + * @returns {Promise>} */ - async invokeContentOrigin(action, params = {}) { + async invokeContentOrigin(action, params) { if (this._contentOriginTabId === this._tabId && this._contentOriginFrameId === this._frameId) { throw new Error('Content origin is same page'); } @@ -594,12 +591,12 @@ export class Display extends EventDispatcher { } /** - * @template [TReturn=unknown] - * @param {string} action - * @param {import('core').SerializableObject} [params] - * @returns {Promise} + * @template {import('cross-frame-api').ApiNames} TName + * @param {TName} action + * @param {import('cross-frame-api').ApiParams} params + * @returns {Promise>} */ - async invokeParentFrame(action, params = {}) { + async invokeParentFrame(action, params) { if (this._parentFrameId === null || this._parentFrameId === this._frameId) { throw new Error('Invalid parent frame'); } @@ -634,14 +631,17 @@ export class Display extends EventDispatcher { // Message handlers - /** - * @param {import('frame-client').Message} data - * @returns {Promise} - * @throws {Error} - */ - _onDirectMessage(data) { + /** @type {import('cross-frame-api').ApiHandler<'displayPopupMessage1'>} */ + async _onDisplayPopupMessage1(message) { + /** @type {import('display').DirectApiMessageAny} */ + const messageInner = this._authenticateMessageData(message); + return await this._onDisplayPopupMessage2(messageInner); + } + + /** @type {import('cross-frame-api').ApiHandler<'displayPopupMessage2'>} */ + _onDisplayPopupMessage2(message) { return new Promise((resolve, reject) => { - const {action, params} = this._authenticateMessageData(data); + const {action, params} = message; invokeApiMapHandler( this._directApiMap, action, @@ -663,9 +663,10 @@ export class Display extends EventDispatcher { } /** - * @param {MessageEvent>} details + * @param {MessageEvent} details */ _onWindowMessage({data}) { + /** @type {import('display').WindowApiMessageAny} */ let data2; try { data2 = this._authenticateMessageData(data); @@ -676,7 +677,7 @@ export class Display extends EventDispatcher { try { const {action, params} = data2; const callback = () => {}; // NOP - invokeApiMapHandler(this._directApiMap, action, params, [], callback); + invokeApiMapHandler(this._windowApiMap, action, params, [], callback); } catch (e) { // NOP } @@ -729,18 +730,15 @@ export class Display extends EventDispatcher { /** * @template [T=unknown] - * @param {T|import('frame-client').Message} data + * @param {import('frame-client').Message} message * @returns {T} * @throws {Error} */ - _authenticateMessageData(data) { - if (this._frameEndpoint === null) { - return /** @type {T} */ (data); - } - if (!this._frameEndpoint.authenticate(data)) { + _authenticateMessageData(message) { + if (this._frameEndpoint !== null && !this._frameEndpoint.authenticate(message)) { throw new Error('Invalid authentication'); } - return /** @type {import('frame-client').Message} */ (data).data; + return /** @type {import('frame-client').Message} */ (message).data; } /** */ @@ -1767,7 +1765,7 @@ export class Display extends EventDispatcher { /** @type {string} */ let text; try { - text = await this.invokeContentOrigin('Frontend.getSelectionText'); + text = await this.invokeContentOrigin('frontendGetSelectionText', void 0); } catch (e) { break; } @@ -1775,7 +1773,7 @@ export class Display extends EventDispatcher { } break; default: - await this.invokeContentOrigin('Frontend.copySelection'); + await this.invokeContentOrigin('frontendCopySelection', void 0); break; } } -- cgit v1.2.3