@@ -29,10 +29,14 @@ import {localStorageGetItem, localStorageSetItem} from '../storage';
29
29
import { __DEBUG__ } from '../constants' ;
30
30
import { printStore } from './utils' ;
31
31
import ProfilerStore from './ProfilerStore' ;
32
+ import { currentBridgeProtocol } from 'react-devtools-shared/src/bridge' ;
32
33
33
34
import type { Element } from './views/Components/types' ;
34
35
import 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' ;
36
40
37
41
const debug = ( methodName , ...args ) => {
38
42
if ( __DEBUG__ ) {
@@ -51,6 +55,7 @@ const LOCAL_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY =
51
55
'React::DevTools::recordChangeDescriptions' ;
52
56
53
57
type Config = { |
58
+ checkBridgeProtocolCompatibility ? : boolean ,
54
59
isProfiling ? : boolean ,
55
60
supportsNativeInspection ? : boolean ,
56
61
supportsReloadAndProfile ? : boolean ,
@@ -76,6 +81,8 @@ export default class Store extends EventEmitter<{|
76
81
supportsNativeStyleEditor : [ ] ,
77
82
supportsProfiling : [ ] ,
78
83
supportsReloadAndProfile : [ ] ,
84
+ unsupportedBridgeProtocolDetected : [ ] ,
85
+ unsupportedRendererVersionDetected : [ ] ,
79
86
unsupportedRendererVersionDetected : [ ] ,
80
87
| } > {
81
88
_bridge : FrontendBridge ;
@@ -119,6 +126,10 @@ export default class Store extends EventEmitter<{|
119
126
120
127
_nativeStyleEditorValidAttributes: $ReadOnlyArray < string > | null = null ;
121
128
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
+
122
133
// Map of element (id) to the set of elements (ids) it owns.
123
134
// This map enables getOwnersListForElement() to avoid traversing the entire tree.
124
135
_ownersMap: Map < number , Set < number >> = new Map ( ) ;
@@ -147,6 +158,7 @@ export default class Store extends EventEmitter<{|
147
158
_supportsReloadAndProfile: boolean = false ;
148
159
_supportsTraceUpdates: boolean = false ;
149
160
161
+ _unsupportedBridgeProtocol: BridgeProtocol | null = null ;
150
162
_unsupportedRendererVersionDetected: boolean = false ;
151
163
152
164
// Total number of visible elements (within all roots).
@@ -217,6 +229,20 @@ export default class Store extends EventEmitter<{|
217
229
) ;
218
230
219
231
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
+ }
220
246
}
221
247
222
248
// This is only used in tests to avoid memory leaks.
@@ -385,6 +411,10 @@ export default class Store extends EventEmitter<{|
385
411
return this . _supportsTraceUpdates ;
386
412
}
387
413
414
+ get unsupportedBridgeProtocol ( ) : BridgeProtocol | null {
415
+ return this . _unsupportedBridgeProtocol ;
416
+ }
417
+
388
418
get unsupportedRendererVersionDetected ( ) : boolean {
389
419
return this . _unsupportedRendererVersionDetected ;
390
420
}
@@ -1168,6 +1198,12 @@ export default class Store extends EventEmitter<{|
1168
1198
'unsupportedRendererVersion' ,
1169
1199
this . onBridgeUnsupportedRendererVersion ,
1170
1200
) ;
1201
+ bridge . removeListener ( 'bridgeProtocol' , this . onBridgeProtocol ) ;
1202
+
1203
+ if ( this . _onBridgeProtocolTimeoutID !== null ) {
1204
+ clearTimeout ( this . _onBridgeProtocolTimeoutID ) ;
1205
+ this . _onBridgeProtocolTimeoutID = null ;
1206
+ }
1171
1207
} ;
1172
1208
1173
1209
onBackendStorageAPISupported = ( isBackendStorageAPISupported : boolean ) => {
@@ -1187,4 +1223,34 @@ export default class Store extends EventEmitter<{|
1187
1223
1188
1224
this . emit ( 'unsupportedRendererVersionDetected' ) ;
1189
1225
} ;
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
+ } ;
1190
1256
}
0 commit comments