@@ -29,10 +29,17 @@ import {localStorageGetItem, localStorageSetItem} from '../storage';
2929import { __DEBUG__ } from '../constants' ;
3030import { printStore } from './utils' ;
3131import ProfilerStore from './ProfilerStore' ;
32+ import {
33+ BRIDGE_PROTOCOL ,
34+ currentBridgeProtocol ,
35+ } from 'react-devtools-shared/src/bridge' ;
3236
3337import type { Element } from './views/Components/types' ;
3438import type { ComponentFilter , ElementType } from '../types' ;
35- import type { FrontendBridge } from 'react-devtools-shared/src/bridge' ;
39+ import type {
40+ FrontendBridge ,
41+ BridgeProtocol ,
42+ } from 'react-devtools-shared/src/bridge' ;
3643
3744const debug = ( methodName , ...args ) => {
3845 if ( __DEBUG__ ) {
@@ -51,6 +58,7 @@ const LOCAL_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY =
5158 'React::DevTools::recordChangeDescriptions' ;
5259
5360type Config = { |
61+ checkBridgeProtocolCompatibility ? : boolean ,
5462 isProfiling ? : boolean ,
5563 supportsNativeInspection ? : boolean ,
5664 supportsReloadAndProfile ? : boolean ,
@@ -76,6 +84,7 @@ export default class Store extends EventEmitter<{|
7684 supportsNativeStyleEditor : [ ] ,
7785 supportsProfiling : [ ] ,
7886 supportsReloadAndProfile : [ ] ,
87+ unsupportedBridgeProtocolDetected : [ ] ,
7988 unsupportedRendererVersionDetected : [ ] ,
8089| } > {
8190 _bridge : FrontendBridge ;
@@ -119,6 +128,10 @@ export default class Store extends EventEmitter<{|
119128
120129 _nativeStyleEditorValidAttributes: $ReadOnlyArray < string > | null = null ;
121130
131+ // Older backends don't support an explicit bridge protocol,
132+ // so we should timeout eventually and show a downgrade message.
133+ _onBridgeProtocolTimeoutID: TimeoutID | null = null ;
134+
122135 // Map of element (id) to the set of elements (ids) it owns.
123136 // This map enables getOwnersListForElement() to avoid traversing the entire tree.
124137 _ownersMap: Map < number , Set < number >> = new Map ( ) ;
@@ -147,6 +160,7 @@ export default class Store extends EventEmitter<{|
147160 _supportsReloadAndProfile: boolean = false ;
148161 _supportsTraceUpdates: boolean = false ;
149162
163+ _unsupportedBridgeProtocol: BridgeProtocol | null = null ;
150164 _unsupportedRendererVersionDetected: boolean = false ;
151165
152166 // Total number of visible elements (within all roots).
@@ -217,6 +231,20 @@ export default class Store extends EventEmitter<{|
217231 ) ;
218232
219233 this . _profilerStore = new ProfilerStore ( bridge , this , isProfiling ) ;
234+
235+ // Verify that the frontend version is compatible with the connected backend.
236+ // See github.com/facebook/react/issues/21326
237+ if ( config != null && config . checkBridgeProtocolCompatibility ) {
238+ // Older backends don't support an explicit bridge protocol,
239+ // so we should timeout eventually and show a downgrade message.
240+ this . _onBridgeProtocolTimeoutID = setTimeout (
241+ this . onBridgeProtocolTimeout ,
242+ 10000 ,
243+ ) ;
244+
245+ bridge . addListener ( 'bridgeProtocol' , this . onBridgeProtocol ) ;
246+ bridge . send ( 'getBridgeProtocol' ) ;
247+ }
220248 }
221249
222250 // This is only used in tests to avoid memory leaks.
@@ -385,6 +413,10 @@ export default class Store extends EventEmitter<{|
385413 return this . _supportsTraceUpdates ;
386414 }
387415
416+ get unsupportedBridgeProtocol ( ) : BridgeProtocol | null {
417+ return this . _unsupportedBridgeProtocol ;
418+ }
419+
388420 get unsupportedRendererVersionDetected ( ) : boolean {
389421 return this . _unsupportedRendererVersionDetected ;
390422 }
@@ -1168,6 +1200,12 @@ export default class Store extends EventEmitter<{|
11681200 'unsupportedRendererVersion' ,
11691201 this . onBridgeUnsupportedRendererVersion ,
11701202 ) ;
1203+ bridge . removeListener ( 'bridgeProtocol' , this . onBridgeProtocol ) ;
1204+
1205+ if ( this . _onBridgeProtocolTimeoutID !== null ) {
1206+ clearTimeout ( this . _onBridgeProtocolTimeoutID ) ;
1207+ this . _onBridgeProtocolTimeoutID = null ;
1208+ }
11711209 } ;
11721210
11731211 onBackendStorageAPISupported = ( isBackendStorageAPISupported : boolean ) => {
@@ -1187,4 +1225,30 @@ export default class Store extends EventEmitter<{|
11871225
11881226 this . emit ( 'unsupportedRendererVersionDetected' ) ;
11891227 } ;
1228+
1229+ onBridgeProtocol = ( bridgeProtocol : BridgeProtocol ) => {
1230+ if ( this . _onBridgeProtocolTimeoutID !== null ) {
1231+ clearTimeout ( this . _onBridgeProtocolTimeoutID ) ;
1232+ this . _onBridgeProtocolTimeoutID = null ;
1233+ }
1234+
1235+ if ( bridgeProtocol . version !== currentBridgeProtocol . version ) {
1236+ this . _unsupportedBridgeProtocol = bridgeProtocol ;
1237+ } else {
1238+ // If we should happen to get a response after timing out...
1239+ this . _unsupportedBridgeProtocol = null ;
1240+ }
1241+
1242+ this . emit ( 'unsupportedBridgeProtocolDetected' ) ;
1243+ } ;
1244+
1245+ onBridgeProtocolTimeout = ( ) => {
1246+ this . _onBridgeProtocolTimeoutID = null ;
1247+
1248+ // If we timed out, that indicates the backend predates the bridge protocol,
1249+ // so we can set a fake version (0) to trigger the downgrade message.
1250+ this . _unsupportedBridgeProtocol = BRIDGE_PROTOCOL [ 0 ] ;
1251+
1252+ this . emit ( 'unsupportedBridgeProtocolDetected' ) ;
1253+ } ;
11901254}
0 commit comments