forked from magicoflolis/Userscript-Plus
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmessager.js
More file actions
124 lines (122 loc) · 3.57 KB
/
Copy pathmessager.js
File metadata and controls
124 lines (122 loc) · 3.57 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
'use strict';
import { webext } from './ext.js';
export const hermes = {
port: null,
portTimer: null,
portTimerDelay: 10000,
msgIdGenerator: 1,
pending: new Map(),
waitStartTime: 0,
shuttingDown: false,
shutdown: function () {
this.shuttingDown = true;
this.destroyPort();
},
disconnectListener: function () {
void webext.runtime.lastError;
this.port = null;
this.destroyPort();
},
disconnectListenerBound: null,
messageListener: function (details) {
if (typeof details !== 'object' || details === null) {
return;
}
// Response to specific message previously sent
if (details.msgId !== undefined) {
const resolver = this.pending.get(details.msgId);
if (resolver !== undefined) {
this.pending.delete(details.msgId);
resolver(details.msg);
return;
}
}
},
messageListenerBound: null,
canDestroyPort: function () {
return this.pending.size === 0;
},
portPoller: function () {
this.portTimer = null;
if (this.port !== null && this.canDestroyPort()) {
return this.destroyPort();
}
this.portTimer = setTimeout(this.portPollerBound, this.portTimerDelay);
this.portTimerDelay = Math.min(this.portTimerDelay * 2, 60 * 60 * 1000);
},
portPollerBound: null,
destroyPort: function () {
if (this.portTimer !== null) {
clearTimeout(this.portTimer);
this.portTimer = null;
}
const port = this.port;
if (port !== null) {
port.disconnect();
port.onMessage.removeListener(this.messageListenerBound);
port.onDisconnect.removeListener(this.disconnectListenerBound);
this.port = null;
}
// service pending callbacks
if (this.pending.size !== 0) {
const pending = this.pending;
this.pending = new Map();
for (const resolver of pending.values()) {
resolver();
}
}
},
createPort: function () {
if (this.shuttingDown) {
return null;
}
if (this.messageListenerBound === null) {
this.messageListenerBound = this.messageListener.bind(this);
this.disconnectListenerBound = this.disconnectListener.bind(this);
this.portPollerBound = this.portPoller.bind(this);
}
try {
this.port = webext.runtime.connect({ name: 'hermes' }) || null;
// eslint-disable-next-line no-unused-vars
} catch (ex) {
this.port = null;
}
if (this.port === null) {
return null;
}
this.port.onMessage.addListener(this.messageListenerBound);
this.port.onDisconnect.addListener(this.disconnectListenerBound);
this.portTimerDelay = 10000;
if (this.portTimer === null) {
this.portTimer = setTimeout(this.portPollerBound, this.portTimerDelay);
}
return this.port;
},
getPort: function () {
return this.port ?? this.createPort();
},
send: function (channel, msg) {
// Too large a gap between the last request and the last response means
// the main process is no longer reachable: memory leaks and bad
// performance become a risk -- especially for long-lived, dynamic
// pages. Guard against this.
if (this.pending.size > 64) {
if (Date.now() - this.waitStartTime > 60000) {
this.shutdown();
}
}
const port = this.getPort();
if (port === null) {
return Promise.resolve();
}
if (this.pending.size === 0) {
this.waitStartTime = Date.now();
}
const msgId = this.msgIdGenerator++;
const promise = new Promise((resolve) => {
this.pending.set(msgId, resolve);
});
port.postMessage({ channel, msgId, msg });
return promise;
}
};