@samitouri / QOS-React-2 / commits / c12194f748

[DevTools] improve error handling in extension (#26068)

## Summary This is to fix some edge cases I recently observed when developing and using the extension: - When you reload the page, there's a chance that a port (most likely the devtools one) is not properly unloaded. In this case, the React DevTools will stop working unless you create a new tab. - For unknown reasons, Chrome sometimes spins up two service worker processes. In this case, an error will be thrown "duplicate ID when registering content script" and sometimes interrupt the execution of the rest of service worker. This is an attempt to make the logic more robust - Automatically shutting down the double pipe if the message fails, and allowing the runtime to rebuild the double pipe. - Log the error message so Chrome believes we've handled it and will not interrupt the execution. This also seems to be helpful in fixing #25806.

Mengdi Chen committed Feb 7, 2023 at 07:59 UTC c12194f7485f298fadc1e51cfffb93e63d61ad96
1 file changed +44 -19
packages/react-devtools-extensions/src/background.js
+44 -19
@@ -12,22 +12,31 @@ if (!IS_FIREFOX) {
12 // It's critical since it allows us to directly run scripts on the "main" world on the page
13 // "document_start" allows it to run before the page's scripts
14 // so the hook can be detected by react reconciler
15 - chrome.scripting.registerContentScripts([
16 - {
17 - id: 'hook',
18 - matches: ['<all_urls>'],
19 - js: ['build/installHook.js'],
20 - runAt: 'document_start',
21 - world: chrome.scripting.ExecutionWorld.MAIN,
22 - },
23 - {
24 - id: 'renderer',
25 - matches: ['<all_urls>'],
26 - js: ['build/renderer.js'],
27 - runAt: 'document_start',
28 - world: chrome.scripting.ExecutionWorld.MAIN,
15 + chrome.scripting.registerContentScripts(
16 + [
17 + {
18 + id: 'hook',
19 + matches: ['<all_urls>'],
20 + js: ['build/installHook.js'],
21 + runAt: 'document_start',
22 + world: chrome.scripting.ExecutionWorld.MAIN,
23 + },
24 + {
25 + id: 'renderer',
26 + matches: ['<all_urls>'],
27 + js: ['build/renderer.js'],
28 + runAt: 'document_start',
29 + world: chrome.scripting.ExecutionWorld.MAIN,
30 + },
31 + ],
32 + function() {
33 + // When the content scripts are already registered, an error will be thrown.
34 + // It happens when the service worker process is incorrectly duplicated.
35 + if (chrome.runtime.lastError) {
36 + console.error(chrome.runtime.lastError);
37 + }
38 },
30 - ]);
39 + );
40 }
41
42 chrome.runtime.onConnect.addListener(function (port) {
@@ -51,7 +60,7 @@ chrome.runtime.onConnect.addListener(function (port) {
60 ports[tab][name] = port;
61
62 if (ports[tab].devtools && ports[tab]['content-script']) {
54 - doublePipe(ports[tab].devtools, ports[tab]['content-script']);
63 + doublePipe(ports[tab].devtools, ports[tab]['content-script'], tab);
64 }
65 });
66
@@ -70,20 +79,36 @@ function installProxy(tabId: number) {
79 }
80 }
81
73 -function doublePipe(one, two) {
82 +function doublePipe(one, two, tabId) {
83 one.onMessage.addListener(lOne);
84 function lOne(message) {
76 - two.postMessage(message);
85 + try {
86 + two.postMessage(message);
87 + } catch (e) {
88 + if (__DEV__) {
89 + console.log(`Broken pipe ${tabId}: `, e);
90 + }
91 + shutdown();
92 + }
93 }
94 two.onMessage.addListener(lTwo);
95 function lTwo(message) {
80 - one.postMessage(message);
96 + try {
97 + one.postMessage(message);
98 + } catch (e) {
99 + if (__DEV__) {
100 + console.log(`Broken pipe ${tabId}: `, e);
101 + }
102 + shutdown();
103 + }
104 }
105 function shutdown() {
106 one.onMessage.removeListener(lOne);
107 two.onMessage.removeListener(lTwo);
108 one.disconnect();
109 two.disconnect();
110 + // clean up so that we can rebuild the double pipe if the page is reloaded
111 + ports[tabId] = null;
112 }
113 one.onDisconnect.addListener(shutdown);
114 two.onDisconnect.addListener(shutdown);