22
23
let welcomeHasInitialized = false;
24
const requiredBackends = new Set<string>();
25
+const activeBackendsShutdownCallbacks = new Set<() => void>();
26
+let cleanupBackendManagerSetup: (() => void) | null = null;
27
+let hasShutdownBackendManager = false;
28
+
29
+function finishBackendManagerShutdown() {
30
+ if (hasShutdownBackendManager) {
31
+ return;
32
+ }
33
+ hasShutdownBackendManager = true;
34
+
35
+ window.removeEventListener('message', welcome);
36
+ window.removeEventListener('pagehide', handlePageHide);
37
+
38
+ const cleanup = cleanupBackendManagerSetup;
39
+ cleanupBackendManagerSetup = null;
40
+ cleanup?.();
41
+
42
+ delete window.__REACT_DEVTOOLS_BACKEND_MANAGER_INJECTED__;
43
+}
44
+
45
+function handlePageHide() {
46
+ // A document in the back-forward cache keeps its JavaScript heap but loses
47
+ // its extension messaging port. Shut down locally while the document is
48
+ // still active so a restored page can attach a new Agent and replay its tree.
49
+ // eslint-disable-next-line no-for-of-loops/no-for-of-loops
50
+ for (const shutdownBackend of activeBackendsShutdownCallbacks) {
51
+ shutdownBackend();
52
+ }
53
+
54
+ finishBackendManagerShutdown();
55
+}
56
57
function welcome(event: $FlowFixMe) {
58
if (
120
},
121
);
122
92
- const unsubscribeShutdownListener: () => void = hook.sub('shutdown', () => {
123
+ let didCleanup = false;
124
+ let unsubscribeShutdownListener: (() => void) | null = null;
125
+ const cleanup = () => {
126
+ if (didCleanup) {
127
+ return;
128
+ }
129
+ didCleanup = true;
130
+
131
unsubscribeRendererListener();
132
unsubscribeBackendInstallationListener();
95
- unsubscribeShutdownListener();
96
- });
133
+ unsubscribeShutdownListener?.();
134
+ unsubscribeShutdownListener = null;
135
+
136
+ if (cleanupBackendManagerSetup === cleanup) {
137
+ cleanupBackendManagerSetup = null;
138
+ }
139
+ };
140
+
141
+ unsubscribeShutdownListener = hook.sub('shutdown', cleanup);
142
+ cleanupBackendManagerSetup = cleanup;
143
}
144
145
function registerRenderer(renderer: ReactRenderer, hook: DevToolsHook) {
161
}
162
163
const {Agent, Bridge, initBackend, setupNativeStyleEditor} = backend;
164
+ let shouldSendMessages = true;
165
const bridge = new Bridge({
166
listen(fn) {
167
const listener = (event: $FlowFixMe) => {
181
};
182
},
183
send(event: string, payload: mixed, transferable?: $ReadOnlyArray<mixed>) {
184
+ if (!shouldSendMessages) {
185
+ return;
186
+ }
187
+
188
window.postMessage(
189
{
190
source: 'react-devtools-bridge',
205
// Clean up flags, so that next reload won't start profiling
206
onReloadAndProfileFlagsReset();
207
208
+ let hasShutdownBackend = false;
209
+ const shutdownBackend = () => {
210
+ if (hasShutdownBackend) {
211
+ return;
212
+ }
213
+ hasShutdownBackend = true;
214
+ shouldSendMessages = false;
215
+
216
+ bridge.shutdown();
217
+ };
218
+ activeBackendsShutdownCallbacks.add(shutdownBackend);
219
+
220
agent.addListener('shutdown', () => {
158
- // If we received 'shutdown' from `agent`, we assume the `bridge` is already shutting down,
159
- // and that caused the 'shutdown' event on the `agent`, so we don't need to call `bridge.shutdown()` here.
221
+ hasShutdownBackend = true;
222
+ shouldSendMessages = false;
223
+ activeBackendsShutdownCallbacks.delete(shutdownBackend);
224
+
225
hook.emit('shutdown');
161
- delete window.__REACT_DEVTOOLS_BACKEND_MANAGER_INJECTED__;
226
+
227
+ if (activeBackendsShutdownCallbacks.size === 0) {
228
+ finishBackendManagerShutdown();
229
+ }
230
});
231
232
initBackend(hook, agent, window, getIsReloadAndProfileSupported());
275
window.__REACT_DEVTOOLS_BACKEND_MANAGER_INJECTED__ = true;
276
277
window.addEventListener('message', welcome);
278
+ window.addEventListener('pagehide', handlePageHide);
279
}