@@ -29,10 +29,14 @@ import {localStorageGetItem, localStorageSetItem} from '../storage';
2929import { __DEBUG__ } from '../constants' ;
3030import { printStore } from './utils' ;
3131import ProfilerStore from './ProfilerStore' ;
32+ import { currentBridgeProtocol } from 'react-devtools-shared/src/bridge' ;
3233
3334import type { Element } from './views/Components/types' ;
3435import type { ComponentFilter , ElementType } from '../types' ;
35- import type { FrontendBridge } from 'react-devtools-shared/src/bridge' ;
36+ import type {
37+ FrontendBridge ,
38+ BridgeProtocol ,
39+ } from 'react-devtools-shared/src/bridge' ;
3640
3741const debug = ( methodName , ...args ) => {
3842 if ( __DEBUG__ ) {
@@ -51,6 +55,7 @@ const LOCAL_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY =
5155 'React::DevTools::recordChangeDescriptions' ;
5256
5357type Config = { |
58+ checkBridgeProtocolCompatibility ? : boolean ,
5459 isProfiling ? : boolean ,
5560 supportsNativeInspection ? : boolean ,
5661 supportsReloadAndProfile ? : boolean ,
@@ -76,6 +81,8 @@ export default class Store extends EventEmitter<{|
7681 supportsNativeStyleEditor : [ ] ,
7782 supportsProfiling : [ ] ,
7883 supportsReloadAndProfile : [ ] ,
84+ unsupportedBridgeProtocolDetected : [ ] ,
85+ unsupportedRendererVersionDetected : [ ] ,
7986 unsupportedRendererVersionDetected : [ ] ,
8087| } > {
8188 _bridge : FrontendBridge ;
@@ -119,6 +126,10 @@ export default class Store extends EventEmitter<{|
119126
120127 _nativeStyleEditorValidAttributes: $ReadOnlyArray < string > | null = null ;
121128
129+ // Older backends don't support an explicit bridge protocol,
130+ // so we should timeout eventually and show a downgrade message.
131+ _onBridgeProtocolTimeoutID: TimeoutID | null = null ;
132+
122133 // Map of element (id) to the set of elements (ids) it owns.
123134 // This map enables getOwnersListForElement() to avoid traversing the entire tree.
124135 _ownersMap: Map < number , Set < number >> = new Map ( ) ;
@@ -147,6 +158,7 @@ export default class Store extends EventEmitter<{|
147158 _supportsReloadAndProfile: boolean = false ;
148159 _supportsTraceUpdates: boolean = false ;
149160
161+ _unsupportedBridgeProtocol: BridgeProtocol | null = null ;
150162 _unsupportedRendererVersionDetected: boolean = false ;
151163
152164 // Total number of visible elements (within all roots).
@@ -217,6 +229,20 @@ export default class Store extends EventEmitter<{|
217229 ) ;
218230
219231 this . _profilerStore = new ProfilerStore ( bridge , this , isProfiling ) ;
232+
233+ // Verify that the frontend version is compatible with the connected backend.
234+ // See github.com/facebook/react/issues/21326
235+ if ( config != null && config . checkBridgeProtocolCompatibility ) {
236+ // Older backends don't support an explicit bridge protocol,
237+ // so we should timeout eventually and show a downgrade message.
238+ this . _onBridgeProtocolTimeoutID = setTimeout (
239+ this . onBridgeProtocolTimeout ,
240+ 10000 ,
241+ ) ;
242+
243+ bridge . addListener ( 'bridgeProtocol' , this . onBridgeProtocol ) ;
244+ bridge . send ( 'getBridgeProtocol' ) ;
245+ }
220246 }
221247
222248 // This is only used in tests to avoid memory leaks.
@@ -385,6 +411,10 @@ export default class Store extends EventEmitter<{|
385411 return this . _supportsTraceUpdates ;
386412 }
387413
414+ get unsupportedBridgeProtocol ( ) : BridgeProtocol | null {
415+ return this . _unsupportedBridgeProtocol ;
416+ }
417+
388418 get unsupportedRendererVersionDetected ( ) : boolean {
389419 return this . _unsupportedRendererVersionDetected ;
390420 }
@@ -1168,6 +1198,12 @@ export default class Store extends EventEmitter<{|
11681198 'unsupportedRendererVersion' ,
11691199 this . onBridgeUnsupportedRendererVersion ,
11701200 ) ;
1201+ bridge . removeListener ( 'bridgeProtocol' , this . onBridgeProtocol ) ;
1202+
1203+ if ( this . _onBridgeProtocolTimeoutID !== null ) {
1204+ clearTimeout ( this . _onBridgeProtocolTimeoutID ) ;
1205+ this . _onBridgeProtocolTimeoutID = null ;
1206+ }
11711207 } ;
11721208
11731209 onBackendStorageAPISupported = ( isBackendStorageAPISupported : boolean ) => {
@@ -1187,4 +1223,34 @@ export default class Store extends EventEmitter<{|
11871223
11881224 this . emit ( 'unsupportedRendererVersionDetected' ) ;
11891225 } ;
1226+
1227+ onBridgeProtocol = ( bridgeProtocol : BridgeProtocol ) => {
1228+ if ( this . _onBridgeProtocolTimeoutID !== null ) {
1229+ clearTimeout ( this . _onBridgeProtocolTimeoutID ) ;
1230+ this . _onBridgeProtocolTimeoutID = null ;
1231+ }
1232+
1233+ this . _onBridgeProtocolTimeoutID = null ;
1234+
1235+ if ( bridgeProtocol . version !== currentBridgeProtocol . version ) {
1236+ this . _unsupportedBridgeProtocol = bridgeProtocol ;
1237+
1238+ this . emit ( 'unsupportedBridgeProtocolDetected' ) ;
1239+ }
1240+ } ;
1241+
1242+ onBridgeProtocolTimeout = ( ) => {
1243+ this . _bridge . removeListener ( 'bridgeProtocol' , this . onBridgeProtocol ) ;
1244+
1245+ // If we timed out, that indicates the backend predates the bridge protocol,
1246+ // so we can set a fake version (0) to trigger the downgrade message.
1247+ // We pin to the latest 4.10 release because 4.11 contained a breaking protocol change.
1248+ this . _unsupportedBridgeProtocol = {
1249+ version : 0 ,
1250+ minNpmVersion : '<4.11.0' ,
1251+ maxNpmVersion : '<4.11.0' ,
1252+ } ;
1253+
1254+ this . emit ( 'unsupportedBridgeProtocolDetected' ) ;
1255+ } ;
11901256}
0 commit comments