diff options
author | RaindropsSys <contact@minteck.org> | 2023-04-24 14:03:36 +0200 |
---|---|---|
committer | RaindropsSys <contact@minteck.org> | 2023-04-24 14:03:36 +0200 |
commit | 633c92eae865e957121e08de634aeee11a8b3992 (patch) | |
tree | 09d881bee1dae0b6eee49db1dfaf0f500240606c /includes/external/matrix/node_modules/matrix-widget-api/src/transport | |
parent | c4657e4509733699c0f26a3c900bab47e915d5a0 (diff) | |
download | pluralconnect-633c92eae865e957121e08de634aeee11a8b3992.tar.gz pluralconnect-633c92eae865e957121e08de634aeee11a8b3992.tar.bz2 pluralconnect-633c92eae865e957121e08de634aeee11a8b3992.zip |
Updated 18 files, added 1692 files and deleted includes/system/compare.inc (automated)
Diffstat (limited to 'includes/external/matrix/node_modules/matrix-widget-api/src/transport')
2 files changed, 307 insertions, 0 deletions
diff --git a/includes/external/matrix/node_modules/matrix-widget-api/src/transport/ITransport.ts b/includes/external/matrix/node_modules/matrix-widget-api/src/transport/ITransport.ts new file mode 100644 index 0000000..b3b2e9a --- /dev/null +++ b/includes/external/matrix/node_modules/matrix-widget-api/src/transport/ITransport.ts @@ -0,0 +1,104 @@ +/* + * Copyright 2020 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { EventEmitter } from "events"; +import { + IWidgetApiAcknowledgeResponseData, + IWidgetApiRequest, + IWidgetApiRequestData, + IWidgetApiResponse, + IWidgetApiResponseData, + WidgetApiAction, +} from ".."; + +/** + * A transport for widget requests/responses. All actions + * get raised through a "message" CustomEvent with detail + * of the IWidgetApiRequest. + */ +export interface ITransport extends EventEmitter { + /** + * True if the transport is ready to start sending, false otherwise. + */ + readonly ready: boolean; + + /** + * The widget ID, if known. If not known, null. + */ + readonly widgetId: string | null; + + /** + * If true, the transport will refuse requests from origins other than the + * widget's current origin. This is intended to be used only by widgets which + * need excess security. + */ + strictOriginCheck: boolean; + + /** + * The origin the transport should be replying/sending to. If not known, leave + * null. + */ + targetOrigin: string | null; + + /** + * The number of seconds an outbound request is allowed to take before it + * times out. + */ + timeoutSeconds: number; + + /** + * Starts the transport for listening + */ + start(): void; + + /** + * Stops the transport. It cannot be re-started. + */ + stop(): void; + + /** + * Sends a request to the remote end. + * @param {WidgetApiAction} action The action to send. + * @param {IWidgetApiRequestData} data The request data. + * @returns {Promise<IWidgetApiResponseData>} A promise which resolves + * to the remote end's response, or throws with an Error if the request + * failed. + */ + send<T extends IWidgetApiRequestData, R extends IWidgetApiResponseData = IWidgetApiAcknowledgeResponseData>( + action: WidgetApiAction, + data: T + ): Promise<R>; + + /** + * Sends a request to the remote end. This is similar to the send() function + * however this version returns the full response rather than just the response + * data. + * @param {WidgetApiAction} action The action to send. + * @param {IWidgetApiRequestData} data The request data. + * @returns {Promise<IWidgetApiResponseData>} A promise which resolves + * to the remote end's response, or throws with an Error if the request + * failed. + */ + sendComplete<T extends IWidgetApiRequestData, R extends IWidgetApiResponse>(action: WidgetApiAction, data: T) + : Promise<R>; + + /** + * Replies to a request. + * @param {IWidgetApiRequest} request The request to reply to. + * @param {IWidgetApiResponseData} responseData The response data to reply with. + */ + reply<T extends IWidgetApiResponseData>(request: IWidgetApiRequest, responseData: T): void; +} diff --git a/includes/external/matrix/node_modules/matrix-widget-api/src/transport/PostmessageTransport.ts b/includes/external/matrix/node_modules/matrix-widget-api/src/transport/PostmessageTransport.ts new file mode 100644 index 0000000..9f86aa5 --- /dev/null +++ b/includes/external/matrix/node_modules/matrix-widget-api/src/transport/PostmessageTransport.ts @@ -0,0 +1,203 @@ +/* + * Copyright 2020 The Matrix.org Foundation C.I.C. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { EventEmitter } from "events"; +import { ITransport } from "./ITransport"; +import { + invertedDirection, + isErrorResponse, + IWidgetApiErrorResponseData, + IWidgetApiRequest, + IWidgetApiRequestData, + IWidgetApiResponse, + IWidgetApiResponseData, + WidgetApiAction, + WidgetApiDirection, + WidgetApiToWidgetAction, +} from ".."; + +interface IOutboundRequest { + request: IWidgetApiRequest; + resolve: (response: IWidgetApiResponse) => void; + reject: (err: Error) => void; +} + +/** + * Transport for the Widget API over postMessage. + */ +export class PostmessageTransport extends EventEmitter implements ITransport { + public strictOriginCheck = false; + public targetOrigin = "*"; + public timeoutSeconds = 10; + + private _ready = false; + private _widgetId: string | null = null; + private outboundRequests = new Map<string, IOutboundRequest | null>(); + private stopController = new AbortController(); + + public get ready(): boolean { + return this._ready; + } + + public get widgetId(): string | null { + return this._widgetId || null; + } + + public constructor( + private sendDirection: WidgetApiDirection, + private initialWidgetId: string | null, + private transportWindow: Window, + private inboundWindow: Window, + ) { + super(); + this._widgetId = initialWidgetId; + } + + private get nextRequestId(): string { + const idBase = `widgetapi-${Date.now()}`; + let index = 0; + let id = idBase; + while (this.outboundRequests.has(id)) { + id = `${idBase}-${index++}`; + } + + // reserve the ID + this.outboundRequests.set(id, null); + + return id; + } + + private sendInternal(message: IWidgetApiRequest | IWidgetApiResponse) { + console.log(`[PostmessageTransport] Sending object to ${this.targetOrigin}: `, message); + this.transportWindow.postMessage(message, this.targetOrigin); + } + + public reply<T extends IWidgetApiResponseData>(request: IWidgetApiRequest, responseData: T) { + return this.sendInternal(<IWidgetApiResponse>{ + ...request, + response: responseData, + }); + } + + public send<T extends IWidgetApiRequestData, R extends IWidgetApiResponseData>( + action: WidgetApiAction, data: T, + ): Promise<R> { + return this.sendComplete(action, data).then(r => <R>r.response); + } + + public sendComplete<T extends IWidgetApiRequestData, R extends IWidgetApiResponse>( + action: WidgetApiAction, data: T, + ): Promise<R> { + if (!this.ready || !this.widgetId) { + return Promise.reject(new Error("Not ready or unknown widget ID")); + } + const request: IWidgetApiRequest = { + api: this.sendDirection, + widgetId: this.widgetId, + requestId: this.nextRequestId, + action: action, + data: data, + }; + if (action === WidgetApiToWidgetAction.UpdateVisibility) { + request['visible'] = data['visible']; + } + return new Promise<R>((prResolve, prReject) => { + const resolve = (response: IWidgetApiResponse) => { + cleanUp(); + prResolve(<R>response); + }; + const reject = (err: Error) => { + cleanUp(); + prReject(err); + }; + + const timerId = setTimeout( + () => reject(new Error("Request timed out")), + (this.timeoutSeconds || 1) * 1000, + ); + + const onStop = () => reject(new Error("Transport stopped")); + this.stopController.signal.addEventListener("abort", onStop); + + const cleanUp = () => { + this.outboundRequests.delete(request.requestId); + clearTimeout(timerId); + this.stopController.signal.removeEventListener("abort", onStop); + }; + + this.outboundRequests.set(request.requestId, { request, resolve, reject }); + this.sendInternal(request); + }); + } + + public start() { + this.inboundWindow.addEventListener("message", (ev: MessageEvent) => { + this.handleMessage(ev); + }); + this._ready = true; + } + + public stop() { + this._ready = false; + this.stopController.abort(); + } + + private handleMessage(ev: MessageEvent) { + if (this.stopController.signal.aborted) return; + if (!ev.data) return; // invalid event + + if (this.strictOriginCheck && ev.origin !== window.origin) return; // bad origin + + // treat the message as a response first, then downgrade to a request + const response = <IWidgetApiResponse>ev.data; + if (!response.action || !response.requestId || !response.widgetId) return; // invalid request/response + + if (!response.response) { + // it's a request + const request = <IWidgetApiRequest>response; + if (request.api !== invertedDirection(this.sendDirection)) return; // wrong direction + this.handleRequest(request); + } else { + // it's a response + if (response.api !== this.sendDirection) return; // wrong direction + this.handleResponse(response); + } + } + + private handleRequest(request: IWidgetApiRequest) { + if (this.widgetId) { + if (this.widgetId !== request.widgetId) return; // wrong widget + } else { + this._widgetId = request.widgetId; + } + + this.emit("message", new CustomEvent("message", {detail: request})); + } + + private handleResponse(response: IWidgetApiResponse) { + if (response.widgetId !== this.widgetId) return; // wrong widget + + const req = this.outboundRequests.get(response.requestId); + if (!req) return; // response to an unknown request + + if (isErrorResponse(response.response)) { + const err = <IWidgetApiErrorResponseData>response.response; + req.reject(new Error(err.error.message)); + } else { + req.resolve(response); + } + } +} |