Skip to content

Commit 4a065bf

Browse files
Brian Vaughnzhengjitf
authored andcommitted
Disable unsupported Bridge protocol version dialog and add workaround for old protocol operations format (facebook#24093)
Rationale: The only case where the unsupported dialog really matters is React Naive. That's the case where the frontend and backend versions are most likely to mismatch. In React Native, the backend is likely to send the bridge protocol version before sending operations– since the agent does this proactively during initialization. I've tested the React Native starter app– after forcefully downgrading the backend version to 4.19.1 (see facebook#23307 (comment)) and verified that this change "fixes" things. Not only does DevTools no longer throw an error that causes the UI to be hidden– it works (meaning that the Components tree can be inspected and interacted with).
1 parent 52f74fc commit 4a065bf

File tree

5 files changed

+43
-25
lines changed

5 files changed

+43
-25
lines changed

packages/react-devtools-core/src/standalone.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {createElement} from 'react';
1111
import {
1212
// $FlowFixMe Flow does not yet know about flushSync()
1313
flushSync,
14-
} from 'react-dom/client';
14+
} from 'react-dom';
1515
import {createRoot} from 'react-dom/client';
1616
import Bridge from 'react-devtools-shared/src/bridge';
1717
import Store from 'react-devtools-shared/src/devtools/store';
@@ -106,9 +106,9 @@ function safeUnmount() {
106106
flushSync(() => {
107107
if (root !== null) {
108108
root.unmount();
109+
root = null;
109110
}
110111
});
111-
root = null;
112112
}
113113

114114
function reload() {

packages/react-devtools-shared/src/bridge.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ export const BRIDGE_PROTOCOL: Array<BridgeProtocol> = [
5454
minNpmVersion: '"<4.11.0"',
5555
maxNpmVersion: '"<4.11.0"',
5656
},
57+
// Versions 4.11.x – 4.12.x contained the backwards breaking change,
58+
// but we didn't add the "fix" of checking the protocol version until 4.13,
59+
// so we don't recommend downgrading to 4.11 or 4.12.
5760
{
5861
version: 1,
5962
minNpmVersion: '4.13.0',

packages/react-devtools-shared/src/devtools/store.js

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,8 @@ export default class Store extends EventEmitter<{|
180180
_rootSupportsBasicProfiling: boolean = false;
181181
_rootSupportsTimelineProfiling: boolean = false;
182182

183-
_unsupportedBridgeProtocol: BridgeProtocol | null = null;
183+
_bridgeProtocol: BridgeProtocol | null = null;
184+
_unsupportedBridgeProtocolDetected: boolean = false;
184185
_unsupportedRendererVersionDetected: boolean = false;
185186

186187
// Total number of visible elements (within all roots).
@@ -375,6 +376,10 @@ export default class Store extends EventEmitter<{|
375376
this.emit('componentFilters');
376377
}
377378

379+
get bridgeProtocol(): BridgeProtocol | null {
380+
return this._bridgeProtocol;
381+
}
382+
378383
get errorCount(): number {
379384
return this._cachedErrorCount;
380385
}
@@ -466,8 +471,8 @@ export default class Store extends EventEmitter<{|
466471
return this._supportsTraceUpdates;
467472
}
468473

469-
get unsupportedBridgeProtocol(): BridgeProtocol | null {
470-
return this._unsupportedBridgeProtocol;
474+
get unsupportedBridgeProtocolDetected(): boolean {
475+
return this._unsupportedBridgeProtocolDetected;
471476
}
472477

473478
get unsupportedRendererVersionDetected(): boolean {
@@ -942,11 +947,21 @@ export default class Store extends EventEmitter<{|
942947
(operations[i] & PROFILING_FLAG_TIMELINE_SUPPORT) !== 0;
943948
i++;
944949

945-
const supportsStrictMode = operations[i] > 0;
946-
i++;
950+
let supportsStrictMode = false;
951+
let hasOwnerMetadata = false;
947952

948-
const hasOwnerMetadata = operations[i] > 0;
949-
i++;
953+
// If we don't know the bridge protocol, guess that we're dealing with the latest.
954+
// If we do know it, we can take it into consideration when parsing operations.
955+
if (
956+
this._bridgeProtocol === null ||
957+
this._bridgeProtocol.version >= 2
958+
) {
959+
supportsStrictMode = operations[i] > 0;
960+
i++;
961+
962+
hasOwnerMetadata = operations[i] > 0;
963+
i++;
964+
}
950965

951966
this._roots = this._roots.concat(id);
952967
this._rootIDToRendererID.set(id, rendererID);
@@ -1383,22 +1398,21 @@ export default class Store extends EventEmitter<{|
13831398
this._onBridgeProtocolTimeoutID = null;
13841399
}
13851400

1401+
this._bridgeProtocol = bridgeProtocol;
1402+
13861403
if (bridgeProtocol.version !== currentBridgeProtocol.version) {
1387-
this._unsupportedBridgeProtocol = bridgeProtocol;
1388-
} else {
1389-
// If we should happen to get a response after timing out...
1390-
this._unsupportedBridgeProtocol = null;
1404+
// Technically newer versions of the frontend can, at least for now,
1405+
// gracefully handle older versions of the backend protocol.
1406+
// So for now we don't need to display the unsupported dialog.
13911407
}
1392-
1393-
this.emit('unsupportedBridgeProtocolDetected');
13941408
};
13951409

13961410
onBridgeProtocolTimeout = () => {
13971411
this._onBridgeProtocolTimeoutID = null;
13981412

13991413
// If we timed out, that indicates the backend predates the bridge protocol,
14001414
// so we can set a fake version (0) to trigger the downgrade message.
1401-
this._unsupportedBridgeProtocol = BRIDGE_PROTOCOL[0];
1415+
this._bridgeProtocol = BRIDGE_PROTOCOL[0];
14021416

14031417
this.emit('unsupportedBridgeProtocolDetected');
14041418
};

packages/react-devtools-shared/src/devtools/views/UnsupportedBridgeProtocolDialog.js

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,20 +33,18 @@ export default function UnsupportedBridgeProtocolDialog(_: {||}) {
3333
useEffect(() => {
3434
const updateDialog = () => {
3535
if (!isVisible) {
36-
if (store.unsupportedBridgeProtocol !== null) {
36+
if (store.unsupportedBridgeProtocolDetected) {
3737
dispatch({
3838
canBeDismissed: false,
3939
id: MODAL_DIALOG_ID,
4040
type: 'SHOW',
4141
content: (
42-
<DialogContent
43-
unsupportedBridgeProtocol={store.unsupportedBridgeProtocol}
44-
/>
42+
<DialogContent unsupportedBridgeProtocol={store.bridgeProtocol} />
4543
),
4644
});
4745
}
4846
} else {
49-
if (store.unsupportedBridgeProtocol === null) {
47+
if (!store.unsupportedBridgeProtocolDetected) {
5048
dispatch({
5149
type: 'HIDE',
5250
id: MODAL_DIALOG_ID,

packages/react-devtools-shared/src/devtools/views/hooks.js

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -239,10 +239,13 @@ export function useModalDismissSignal(
239239
// It's important to listen to the ownerDocument to support the browser extension.
240240
// Here we use portals to render individual tabs (e.g. Profiler),
241241
// and the root document might belong to a different window.
242-
ownerDocument = ((modalRef.current: any): HTMLDivElement).ownerDocument;
243-
ownerDocument.addEventListener('keydown', handleDocumentKeyDown);
244-
if (dismissOnClickOutside) {
245-
ownerDocument.addEventListener('click', handleDocumentClick, true);
242+
const div = modalRef.current;
243+
if (div != null) {
244+
ownerDocument = div.ownerDocument;
245+
ownerDocument.addEventListener('keydown', handleDocumentKeyDown);
246+
if (dismissOnClickOutside) {
247+
ownerDocument.addEventListener('click', handleDocumentClick, true);
248+
}
246249
}
247250
}, 0);
248251

0 commit comments

Comments
 (0)