Skip to content

Commit 852c93a

Browse files
hoxyqAndyPengc12
authored andcommitted
fix[devtools/extension]: handle tab navigation events before react is loaded (facebook#27316)
This is mostly hotfix for facebook#27215. Contains 3 fixes: - Handle cases when `react` is not loaded yet and user performs in-tab navigation. Previously, because of the uncleared interval we would try to mount DevTools twice, resulting into multiple errors. - Handle case when extension port disconnected (probably by the browser or just due to its lifetime) - Removed duplicate `render()` call on line 327
1 parent 6f9ec4b commit 852c93a

File tree

2 files changed

+51
-11
lines changed

2 files changed

+51
-11
lines changed

packages/react-devtools-extensions/src/background/index.js

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ function isNumeric(str: string): boolean {
9595
return +str + '' === str;
9696
}
9797

98-
chrome.runtime.onConnect.addListener(async port => {
98+
chrome.runtime.onConnect.addListener(port => {
9999
if (port.name === 'proxy') {
100100
// Proxy content script is executed in tab, so it should have it specified.
101101
const tabId = port.sender.tab.id;
@@ -115,11 +115,28 @@ chrome.runtime.onConnect.addListener(async port => {
115115
if (isNumeric(port.name)) {
116116
// Extension port doesn't have tab id specified, because its sender is the extension.
117117
const tabId = +port.name;
118+
const extensionPortAlreadyConnected = ports[tabId]?.extension != null;
119+
120+
// Handle the case when extension port was disconnected and we were not notified
121+
if (extensionPortAlreadyConnected) {
122+
ports[tabId].disconnectPipe?.();
123+
}
118124

119125
registerTab(tabId);
120126
registerExtensionPort(port, tabId);
121127

122-
injectProxy(tabId);
128+
if (extensionPortAlreadyConnected) {
129+
const proxyPort = ports[tabId].proxy;
130+
131+
// Avoid re-injecting the content script, we might end up in a situation
132+
// where we would have multiple proxy ports opened and trying to reconnect
133+
if (proxyPort) {
134+
clearReconnectionTimeout(proxyPort);
135+
reconnectProxyPort(proxyPort, tabId);
136+
}
137+
} else {
138+
injectProxy(tabId);
139+
}
123140

124141
return;
125142
}

packages/react-devtools-extensions/src/main/index.js

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -323,8 +323,6 @@ function createBridgeAndStore() {
323323
}),
324324
);
325325
};
326-
327-
render();
328326
}
329327

330328
const viewUrlSourceFunction = (url, line, col) => {
@@ -364,14 +362,14 @@ function createComponentsPanel() {
364362
}
365363
});
366364

367-
// TODO: we should listen to extension.onHidden to unmount some listeners
365+
// TODO: we should listen to createdPanel.onHidden to unmount some listeners
368366
// and potentially stop highlighting
369367
},
370368
);
371369
}
372370

373371
function createProfilerPanel() {
374-
if (componentsPortalContainer) {
372+
if (profilerPortalContainer) {
375373
render('profiler');
376374

377375
return;
@@ -398,6 +396,9 @@ function createProfilerPanel() {
398396
}
399397

400398
function performInTabNavigationCleanup() {
399+
// Potentially, if react hasn't loaded yet and user performs in-tab navigation
400+
clearReactPollingInterval();
401+
401402
if (store !== null) {
402403
// Store profiling data, so it can be used later
403404
profilingData = store.profilerStore.profilingData;
@@ -435,6 +436,9 @@ function performInTabNavigationCleanup() {
435436
}
436437

437438
function performFullCleanup() {
439+
// Potentially, if react hasn't loaded yet and user closed the browser DevTools
440+
clearReactPollingInterval();
441+
438442
if ((componentsPortalContainer || profilerPortalContainer) && root) {
439443
// This should also emit bridge.shutdown, but only if this root was mounted
440444
flushSync(() => root.unmount());
@@ -455,14 +459,24 @@ function performFullCleanup() {
455459
port = null;
456460
}
457461

458-
function mountReactDevTools() {
459-
registerEventsLogger();
460-
462+
function connectExtensionPort() {
461463
const tabId = chrome.devtools.inspectedWindow.tabId;
462464
port = chrome.runtime.connect({
463465
name: String(tabId),
464466
});
465467

468+
// This port may be disconnected by Chrome at some point, this callback
469+
// will be executed only if this port was disconnected from the other end
470+
// so, when we call `port.disconnect()` from this script,
471+
// this should not trigger this callback and port reconnection
472+
port.onDisconnect.addListener(connectExtensionPort);
473+
}
474+
475+
function mountReactDevTools() {
476+
registerEventsLogger();
477+
478+
connectExtensionPort();
479+
466480
createBridgeAndStore();
467481

468482
setReactSelectionFromBrowser(bridge);
@@ -477,18 +491,20 @@ function mountReactDevToolsWhenReactHasLoaded() {
477491
const checkIfReactHasLoaded = () => executeIfReactHasLoaded(onReactReady);
478492

479493
// Check to see if React has loaded in case React is added after page load
480-
const reactPollingIntervalId = setInterval(() => {
494+
reactPollingIntervalId = setInterval(() => {
481495
checkIfReactHasLoaded();
482496
}, 500);
483497

484498
function onReactReady() {
485-
clearInterval(reactPollingIntervalId);
499+
clearReactPollingInterval();
486500
mountReactDevTools();
487501
}
488502

489503
checkIfReactHasLoaded();
490504
}
491505

506+
let reactPollingIntervalId = null;
507+
492508
let bridge = null;
493509
let store = null;
494510

@@ -509,6 +525,8 @@ chrome.devtools.network.onNavigated.addListener(syncSavedPreferences);
509525

510526
// Cleanup previous page state and remount everything
511527
chrome.devtools.network.onNavigated.addListener(() => {
528+
clearReactPollingInterval();
529+
512530
performInTabNavigationCleanup();
513531
mountReactDevToolsWhenReactHasLoaded();
514532
});
@@ -521,5 +539,10 @@ if (IS_FIREFOX) {
521539
window.addEventListener('beforeunload', performFullCleanup);
522540
}
523541

542+
function clearReactPollingInterval() {
543+
clearInterval(reactPollingIntervalId);
544+
reactPollingIntervalId = null;
545+
}
546+
524547
syncSavedPreferences();
525548
mountReactDevToolsWhenReactHasLoaded();

0 commit comments

Comments
 (0)