Commit bcd622a3 authored by Robert Knight's avatar Robert Knight

Migrate port-provider.js to TypeScript

parent 35b6f8d8
import { TinyEmitter } from 'tiny-emitter'; import { TinyEmitter } from 'tiny-emitter';
import type { Destroyable } from '../../types/annotator';
import { captureErrors, sendError } from '../frame-error-capture'; import { captureErrors, sendError } from '../frame-error-capture';
import { ListenerCollection } from '../listener-collection'; import { ListenerCollection } from '../listener-collection';
import { isMessage, isMessageEqual, isSourceWindow } from './port-util'; import { isMessage, isMessageEqual, isSourceWindow } from './port-util';
import type { Message } from './port-util';
/** type Channel =
* @typedef {import('../../types/annotator').Destroyable} Destroyable | 'guest-host'
* @typedef {import('./port-util').Message} Message | 'guest-sidebar'
* @typedef {import('./port-util').Frame} Frame | 'notebook-sidebar'
* @typedef {'guest-host'|'guest-sidebar'|'notebook-sidebar'|'sidebar-host'} Channel | 'sidebar-host';
*/
/** /**
* PortProvider creates a `MessageChannel` for communication between two * PortProvider creates a `MessageChannel` for communication between two
...@@ -57,20 +58,11 @@ import { isMessage, isMessageEqual, isSourceWindow } from './port-util'; ...@@ -57,20 +58,11 @@ import { isMessage, isMessageEqual, isSourceWindow } from './port-util';
* 5. Send second port to other frame * 5. Send second port to other frame
* (eg. via MessageChannel connection * (eg. via MessageChannel connection
* between host and other frame) * between host and other frame)
*
* @implements {Destroyable}
*/
export class PortProvider {
/**
* Begin listening to port requests from other frames.
*
* @param {string} hypothesisAppsOrigin - the origin of the hypothesis apps
* is use to send the notebook and sidebar ports to only the frames that
* match the origin.
*/ */
constructor(hypothesisAppsOrigin) { export class PortProvider implements Destroyable {
this._hypothesisAppsOrigin = hypothesisAppsOrigin; private _allowedMessages: Array<Partial<Message> & { allowedOrigin: string }>;
this._emitter = new TinyEmitter(); private _emitter: TinyEmitter;
private _hypothesisAppsOrigin: string;
/** /**
* IDs of port requests that have been handled. * IDs of port requests that have been handled.
...@@ -78,9 +70,22 @@ export class PortProvider { ...@@ -78,9 +70,22 @@ export class PortProvider {
* This is used to avoid responding to the same request multiple times. * This is used to avoid responding to the same request multiple times.
* Guest frames in particular may send duplicate requests (see comments in * Guest frames in particular may send duplicate requests (see comments in
* PortFinder). * PortFinder).
*/
private _handledRequests: Set<string>;
private _listeners: ListenerCollection;
private _sidebarHostChannel: MessageChannel;
/**
* Begin listening to port requests from other frames.
* *
* @type {Set<string>} * @param hypothesisAppsOrigin - Origin of the Hypothesis apps (sidebar,
* notebook). Used to verify requests for ports.
*/ */
constructor(hypothesisAppsOrigin: string) {
this._hypothesisAppsOrigin = hypothesisAppsOrigin;
this._emitter = new TinyEmitter();
this._handledRequests = new Set(); this._handledRequests = new Set();
// Create the `sidebar-host` channel immediately, while other channels are // Create the `sidebar-host` channel immediately, while other channels are
...@@ -89,7 +94,6 @@ export class PortProvider { ...@@ -89,7 +94,6 @@ export class PortProvider {
this._listeners = new ListenerCollection(); this._listeners = new ListenerCollection();
/** @type {Array<Partial<Message> & {allowedOrigin: string}>} */
this._allowedMessages = [ this._allowedMessages = [
{ {
allowedOrigin: '*', allowedOrigin: '*',
...@@ -120,12 +124,11 @@ export class PortProvider { ...@@ -120,12 +124,11 @@ export class PortProvider {
this._listen(); this._listen();
} }
_listen() { private _listen() {
const errorContext = 'Handling port request'; const errorContext = 'Handling port request';
const sentErrors = /** @type {Set<string>} */ (new Set()); const sentErrors = new Set<string>();
/** @param {string} message */ const reportError = (message: string) => {
const reportError = message => {
if (sentErrors.has(message)) { if (sentErrors.has(message)) {
// PortFinder will send requests repeatedly until it gets a response or // PortFinder will send requests repeatedly until it gets a response or
// a timeout is reached. // a timeout is reached.
...@@ -137,8 +140,7 @@ export class PortProvider { ...@@ -137,8 +140,7 @@ export class PortProvider {
sendError(new Error(message), errorContext); sendError(new Error(message), errorContext);
}; };
/** @param {MessageEvent} event */ const handleRequest = (event: MessageEvent) => {
const handleRequest = event => {
const { data, origin, source } = event; const { data, origin, source } = event;
if (!isMessage(data) || data.type !== 'request') { if (!isMessage(data) || data.type !== 'request') {
...@@ -147,7 +149,7 @@ export class PortProvider { ...@@ -147,7 +149,7 @@ export class PortProvider {
} }
const { frame1, frame2, requestId, sourceId } = data; const { frame1, frame2, requestId, sourceId } = data;
const channel = /** @type {Channel} */ (`${frame1}-${frame2}`); const channel = `${frame1}-${frame2}` as Channel;
if (!isSourceWindow(source)) { if (!isSourceWindow(source)) {
reportError( reportError(
...@@ -217,17 +219,24 @@ export class PortProvider { ...@@ -217,17 +219,24 @@ export class PortProvider {
); );
} }
/** private _messageMatches({
* @param {object} options allowedMessage,
* @param {Partial<Message>} options.allowedMessage - the `data` must match this allowedOrigin,
* `Message`. data,
* @param {string} options.allowedOrigin - the `origin` must match this origin,
* value. If `allowedOrigin` is '*', the origin is ignored. }: {
* @param {unknown} options.data - the data to be compared with `allowedMessage`. /** Fields that the message must match. */
* @param {string} options.origin - the origin to be compared with allowedMessage: Partial<Message>;
* `allowedOrigin`.
*/ /** Origin must match this value. Accepts `*` wildcard. */
_messageMatches({ allowedMessage, allowedOrigin, data, origin }) { allowedOrigin: string;
/** Data to be compared with `allowedMessage` */
data: unknown;
/** Origin to be compared with `allowedOrigin`. */
origin: string;
}) {
if (allowedOrigin !== '*' && origin !== allowedOrigin) { if (allowedOrigin !== '*' && origin !== allowedOrigin) {
return false; return false;
} }
...@@ -235,12 +244,10 @@ export class PortProvider { ...@@ -235,12 +244,10 @@ export class PortProvider {
return isMessageEqual(data, allowedMessage); return isMessageEqual(data, allowedMessage);
} }
/** on(
* @param {'frameConnected'} eventName eventName: 'frameConnected',
* @param {(source: 'guest'|'sidebar', port: MessagePort) => void} handler - this handler handler: (source: 'guest' | 'sidebar', port: MessagePort) => void
* fires when a frame connects to the host frame ) {
*/
on(eventName, handler) {
this._emitter.on(eventName, handler); this._emitter.on(eventName, handler);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment