-
Notifications
You must be signed in to change notification settings - Fork 332
Expand file tree
/
Copy pathwindow_message.ts
More file actions
231 lines (206 loc) · 7.08 KB
/
window_message.ts
File metadata and controls
231 lines (206 loc) · 7.08 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
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
import type { Message, MessageConnect, MessageSend, RuntimeMessageSender, TMessage } from "./types";
import { uuidv4 } from "@App/pkg/utils/uuid";
import EventEmitter from "eventemitter3";
// 通过 window.postMessage/onmessage 实现通信
export interface PostMessage {
postMessage<T = any>(message: T): void;
}
class WindowPostMessage implements PostMessage {
constructor(private target: Window) {}
postMessage<T = any>(message: T): void {
this.target.postMessage(message, "*");
}
}
// 消息体
export type WindowMessageBody<T = any> = {
messageId: string; // 消息id
type: "sendMessage" | "respMessage" | "connect" | "disconnect" | "connectMessage"; // 消息类型
data: T | null; // 消息数据
};
export class WindowMessage implements Message {
EE = new EventEmitter<string, any>();
// source: Window 消息来源
// target: Window 消息目标
constructor(
private source: Window,
private target: Window,
private serviceWorker?: boolean
) {
// 监听消息
this.source.addEventListener("message", (e) => {
if (e.source === this.target || e.source === this.source) {
this.messageHandle(e.data, new WindowPostMessage(this.target));
}
});
// 是否监听serviceWorker消息
if (this.serviceWorker) {
navigator.serviceWorker.addEventListener("message", (e) => {
if (e.source) {
this.messageHandle(e.data, e.source as Window);
}
});
}
}
messageHandle(data: WindowMessageBody, target: PostMessage) {
// 处理消息
if (data.type === "sendMessage") {
// 接收到消息
this.EE.emit("message", data.data, (resp: any) => {
// 发送响应消息
// 无消息id则不发送响应消息
if (!data.messageId) {
return;
}
const body: WindowMessageBody = {
messageId: data.messageId,
type: "respMessage",
data: resp,
};
target.postMessage(body);
});
} else if (data.type === "respMessage") {
// 接收到响应消息
this.EE.emit(`response:${data.messageId}`, data);
} else if (data.type === "connect") {
this.EE.emit("connect", data.data, new WindowMessageConnect(data.messageId, this.EE, target));
} else if (data.type === "disconnect") {
this.EE.emit(`disconnect:${data.messageId}`);
} else if (data.type === "connectMessage") {
this.EE.emit(`connectMessage:${data.messageId}`, data.data);
}
}
onConnect(callback: (data: TMessage, con: MessageConnect) => void): void {
this.EE.addListener("connect", callback);
}
connect(data: TMessage): Promise<MessageConnect> {
return new Promise((resolve) => {
const body: WindowMessageBody<TMessage> = {
messageId: uuidv4(),
type: "connect",
data,
};
this.target.postMessage(body, "*");
resolve(new WindowMessageConnect(body.messageId, this.EE, this.target));
});
}
onMessage(
callback: (data: TMessage, sendResponse: (data: any) => void, sender: RuntimeMessageSender) => boolean | void
): void {
this.EE.addListener("message", callback);
}
// 发送消息 注意不进行回调的内存泄漏
sendMessage<T = any>(data: TMessage): Promise<T> {
return new Promise((resolve: ((value: T) => void) | null) => {
const messageId = uuidv4();
const body: WindowMessageBody<TMessage> = {
messageId,
type: "sendMessage",
data,
};
const eventId = `response:${messageId}`;
this.EE.addListener(eventId, (body: WindowMessageBody<TMessage>) => {
this.EE.removeAllListeners(eventId);
resolve!(body.data as T);
resolve = null; // 设为 null 提醒JS引擎可以GC
});
this.target.postMessage(body, "*");
});
}
}
export class WindowMessageConnect implements MessageConnect {
constructor(
private messageId: string,
private EE: EventEmitter<string, any>,
private target: PostMessage
) {
this.onDisconnect(() => {
// 移除所有监听
this.EE.removeAllListeners("connectMessage:" + this.messageId);
this.EE.removeAllListeners("disconnect:" + this.messageId);
});
}
sendMessage(data: TMessage) {
const body: WindowMessageBody<TMessage> = {
messageId: this.messageId,
type: "connectMessage",
data,
};
this.target.postMessage(body);
}
onMessage(callback: (data: TMessage) => void) {
this.EE.addListener(`connectMessage:${this.messageId}`, callback);
}
disconnect() {
const body: WindowMessageBody<TMessage> = {
messageId: this.messageId,
type: "disconnect",
data: null,
};
this.target.postMessage(body);
}
onDisconnect(callback: () => void) {
this.EE.addListener(`disconnect:${this.messageId}`, callback);
}
}
// service_worker和offscreen同时监听消息,会导致消息被两边同时接收,但是返回结果时会产生问题,导致报错
// 不进行监听的话又无法从service_worker主动发送消息
// 所以service_worker与offscreen使用ServiceWorker的方式进行通信
export class ServiceWorkerMessageSend implements MessageSend {
EE = new EventEmitter<string, any>();
private target: PostMessage | undefined = undefined;
constructor() {}
listened: boolean = false;
async init() {
if (!this.target && self.clients) {
if (!this.listened) {
this.listened = true;
self.addEventListener("message", (e) => {
this.messageHandle(e.data);
});
}
const list = await self.clients.matchAll({ includeUncontrolled: true, type: "window" });
// 找到offscreen.html窗口
this.target = list.find((client) => client.url == chrome.runtime.getURL("src/offscreen.html")) as PostMessage;
}
}
messageHandle(data: WindowMessageBody) {
// 处理消息
if (data.type === "respMessage") {
// 接收到响应消息
this.EE.emit(`response:${data.messageId}`, data);
} else if (data.type === "disconnect") {
this.EE.emit(`disconnect:${data.messageId}`);
} else if (data.type === "connectMessage") {
this.EE.emit(`connectMessage:${data.messageId}`, data.data);
}
}
async connect(data: TMessage): Promise<MessageConnect> {
await this.init();
const body: WindowMessageBody<TMessage> = {
messageId: uuidv4(),
type: "connect",
data,
};
this.target!.postMessage(body);
return new WindowMessageConnect(body.messageId, this.EE, this.target!);
}
// 发送消息 注意不进行回调的内存泄漏
async sendMessage<T = any>(data: TMessage): Promise<T> {
await this.init();
return new Promise((resolve: ((value: T) => void) | null) => {
const messageId = uuidv4();
const body: WindowMessageBody<TMessage> = {
messageId,
type: "sendMessage",
data,
};
const eventId = `response:${messageId}`;
this.EE.addListener(eventId, (body: WindowMessageBody<TMessage>) => {
this.EE.removeAllListeners(eventId);
resolve!(body.data as T);
resolve = null; // 设为 null 提醒JS引擎可以GC
});
this.target!.postMessage(body);
});
}
}