diff options
author | toasted-nutbread <toasted-nutbread@users.noreply.github.com> | 2023-12-29 19:17:46 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-12-30 00:17:46 +0000 |
commit | 7303da3991814a0ce220bf2fff3e51b968913b86 (patch) | |
tree | 809c289d824ec2a08c5ff54579766b7f5c5e09e1 /ext/js/comm/cross-frame-api.js | |
parent | 1b0e0c551d1505ed4242c04ebac224e5fff81f04 (diff) |
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
Diffstat (limited to 'ext/js/comm/cross-frame-api.js')
-rw-r--r-- | ext/js/comm/cross-frame-api.js | 99 |
1 files changed, 38 insertions, 61 deletions
diff --git a/ext/js/comm/cross-frame-api.js b/ext/js/comm/cross-frame-api.js index 14e410a8..3569c037 100644 --- a/ext/js/comm/cross-frame-api.js +++ b/ext/js/comm/cross-frame-api.js @@ -16,7 +16,8 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. */ -import {EventDispatcher, EventListenerCollection, invokeMessageHandler, log} from '../core.js'; +import {EventDispatcher, EventListenerCollection, log} from '../core.js'; +import {extendApiMap, invokeApiMapHandler} from '../core/api-map.js'; import {ExtensionError} from '../core/extension-error.js'; import {parseJson} from '../core/json.js'; import {yomitan} from '../yomitan.js'; @@ -29,9 +30,9 @@ export class CrossFrameAPIPort extends EventDispatcher { * @param {number} otherTabId * @param {number} otherFrameId * @param {chrome.runtime.Port} port - * @param {import('core').MessageHandlerMap} messageHandlers + * @param {import('cross-frame-api').ApiMap} apiMap */ - constructor(otherTabId, otherFrameId, port, messageHandlers) { + constructor(otherTabId, otherFrameId, port, apiMap) { super(); /** @type {number} */ this._otherTabId = otherTabId; @@ -39,8 +40,8 @@ export class CrossFrameAPIPort extends EventDispatcher { this._otherFrameId = otherFrameId; /** @type {?chrome.runtime.Port} */ this._port = port; - /** @type {import('core').MessageHandlerMap} */ - this._messageHandlers = messageHandlers; + /** @type {import('cross-frame-api').ApiMap} */ + this._apiMap = apiMap; /** @type {Map<number, import('cross-frame-api').Invocation>} */ this._activeInvocations = new Map(); /** @type {number} */ @@ -69,13 +70,12 @@ export class CrossFrameAPIPort extends EventDispatcher { } /** - * @template [TParams=import('core').SerializableObject] - * @template [TReturn=unknown] - * @param {string} action - * @param {TParams} params + * @template {import('cross-frame-api').ApiNames} TName + * @param {TName} action + * @param {import('cross-frame-api').ApiParams<TName>} params * @param {number} ackTimeout * @param {number} responseTimeout - * @returns {Promise<TReturn>} + * @returns {Promise<import('cross-frame-api').ApiReturn<TName>>} */ invoke(action, params, ackTimeout, responseTimeout) { return new Promise((resolve, reject) => { @@ -186,7 +186,7 @@ export class CrossFrameAPIPort extends EventDispatcher { /** * @param {number} id - * @param {import('core').Response<unknown>} data + * @param {import('core').Response<import('cross-frame-api').ApiReturnAny>} data */ _onResult(id, data) { const invocation = this._activeInvocations.get(id); @@ -217,15 +217,13 @@ export class CrossFrameAPIPort extends EventDispatcher { /** * @param {number} id - * @param {unknown} error + * @param {unknown} errorOrMessage */ - _onError(id, error) { + _onError(id, errorOrMessage) { const invocation = this._activeInvocations.get(id); if (typeof invocation === 'undefined') { return; } - if (!(error instanceof Error)) { - error = new Error(`${error} (${invocation.action})`); - } + const error = errorOrMessage instanceof Error ? errorOrMessage : new Error(`${errorOrMessage} (${invocation.action})`); this._activeInvocations.delete(id); if (invocation.timer !== null) { @@ -239,23 +237,18 @@ export class CrossFrameAPIPort extends EventDispatcher { /** * @param {number} id - * @param {import('cross-frame-api').InvocationData} details - * @returns {boolean} + * @param {import('cross-frame-api').ApiMessageAny} details */ _onInvoke(id, {action, params}) { - const messageHandler = this._messageHandlers.get(action); this._sendAck(id); - if (typeof messageHandler === 'undefined') { - this._sendError(id, new Error(`Unknown action: ${action}`)); - return false; - } - - /** - * @param {import('core').Response<unknown>} data - * @returns {void} - */ - const callback = (data) => this._sendResult(id, data); - return invokeMessageHandler(messageHandler, params, callback); + invokeApiMapHandler( + this._apiMap, + action, + params, + [], + (data) => this._sendResult(id, data), + () => this._sendError(id, new Error(`Unknown action: ${action}`)) + ); } /** @@ -279,7 +272,7 @@ export class CrossFrameAPIPort extends EventDispatcher { /** * @param {number} id - * @param {import('core').Response<unknown>} data + * @param {import('core').Response<import('cross-frame-api').ApiReturnAny>} data */ _sendResult(id, data) { this._sendResponse({type: 'result', id, data}); @@ -302,8 +295,8 @@ export class CrossFrameAPI { this._responseTimeout = 10000; // 10 seconds /** @type {Map<number, Map<number, CrossFrameAPIPort>>} */ this._commPorts = new Map(); - /** @type {import('core').MessageHandlerMap} */ - this._messageHandlers = new Map(); + /** @type {import('cross-frame-api').ApiMap} */ + this._apiMap = new Map(); /** @type {(port: CrossFrameAPIPort) => void} */ this._onDisconnectBind = this._onDisconnect.bind(this); /** @type {?number} */ @@ -319,25 +312,23 @@ export class CrossFrameAPI { } /** - * @template [TParams=import('core').SerializableObject] - * @template [TReturn=unknown] + * @template {import('cross-frame-api').ApiNames} TName * @param {number} targetFrameId - * @param {string} action - * @param {TParams} params - * @returns {Promise<TReturn>} + * @param {TName} action + * @param {import('cross-frame-api').ApiParams<TName>} params + * @returns {Promise<import('cross-frame-api').ApiReturn<TName>>} */ invoke(targetFrameId, action, params) { return this.invokeTab(null, targetFrameId, action, params); } /** - * @template [TParams=import('core').SerializableObject] - * @template [TReturn=unknown] + * @template {import('cross-frame-api').ApiNames} TName * @param {?number} targetTabId * @param {number} targetFrameId - * @param {string} action - * @param {TParams} params - * @returns {Promise<TReturn>} + * @param {TName} action + * @param {import('cross-frame-api').ApiParams<TName>} params + * @returns {Promise<import('cross-frame-api').ApiReturn<TName>>} */ async invokeTab(targetTabId, targetFrameId, action, params) { if (typeof targetTabId !== 'number') { @@ -351,24 +342,10 @@ export class CrossFrameAPI { } /** - * @param {import('core').MessageHandlerMapInit} messageHandlers - * @throws {Error} - */ - registerHandlers(messageHandlers) { - for (const [key, value] of messageHandlers) { - if (this._messageHandlers.has(key)) { - throw new Error(`Handler ${key} is already registered`); - } - this._messageHandlers.set(key, value); - } - } - - /** - * @param {string} key - * @returns {boolean} + * @param {import('cross-frame-api').ApiMapInit} handlers */ - unregisterHandler(key) { - return this._messageHandlers.delete(key); + registerHandlers(handlers) { + extendApiMap(this._apiMap, handlers); } // Private @@ -451,7 +428,7 @@ export class CrossFrameAPI { * @returns {CrossFrameAPIPort} */ _setupCommPort(otherTabId, otherFrameId, port) { - const commPort = new CrossFrameAPIPort(otherTabId, otherFrameId, port, this._messageHandlers); + const commPort = new CrossFrameAPIPort(otherTabId, otherFrameId, port, this._apiMap); let tabPorts = this._commPorts.get(otherTabId); if (typeof tabPorts === 'undefined') { tabPorts = new Map(); |