-
Notifications
You must be signed in to change notification settings - Fork 652
Expand file tree
/
Copy pathWebViewMessageStream.ts
More file actions
91 lines (74 loc) · 2.54 KB
/
WebViewMessageStream.ts
File metadata and controls
91 lines (74 loc) · 2.54 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
import type { PostMessageEvent } from '@metamask/post-message-stream';
import {
BasePostMessageStream,
isValidStreamMessage,
} from '@metamask/post-message-stream';
import { assert } from '@metamask/utils';
export type WebViewInterface = {
injectJavaScript(js: string): void;
registerMessageListener(listener: (event: PostMessageEvent) => void): void;
unregisterMessageListener(listener: (event: PostMessageEvent) => void): void;
};
export type WebViewStreamArgs = {
name: string;
target: string;
webView: WebViewInterface;
};
/**
* A special postMessage stream used to interface with a WebView.
*/
export class WebViewMessageStream extends BasePostMessageStream {
readonly #name;
readonly #target;
readonly #webView: WebViewInterface | undefined;
/**
* Creates a stream for communicating with other streams inside a WebView.
*
* @param args - Options bag.
* @param args.name - The name of the stream. Used to differentiate between
* multiple streams sharing the same window object.
* @param args.target - The name of the stream to exchange messages with.
* @param args.webView - A reference to the WebView.
*/
constructor({ name, target, webView }: WebViewStreamArgs) {
super();
this.#name = name;
this.#target = target;
this._onMessage = this._onMessage.bind(this);
this.#webView = webView;
// This method is already bound.
// eslint-disable-next-line @typescript-eslint/unbound-method
this.#webView.registerMessageListener(this._onMessage);
this._handshake();
}
protected _postMessage(data: unknown): void {
assert(this.#webView);
const json = JSON.stringify({
target: this.#target,
data,
});
const encoded = JSON.stringify(json);
this.#webView.injectJavaScript(`window.postMessage(${encoded})`);
}
// TODO: Either fix this lint violation or explain why it's necessary to
// ignore.
// eslint-disable-next-line no-restricted-syntax
private _onMessage(event: PostMessageEvent): void {
if (typeof event.data !== 'string') {
return;
}
const message = JSON.parse(event.data);
// Notice that we don't check targetWindow or targetOrigin here.
// This doesn't seem possible to do in RN.
if (!isValidStreamMessage(message) || message.target !== this.#name) {
return;
}
this._onData(message.data);
}
_destroy() {
assert(this.#webView);
// This method is already bound.
// eslint-disable-next-line @typescript-eslint/unbound-method
this.#webView.unregisterMessageListener(this._onMessage);
}
}