Skip to content

Commit daba3d8

Browse files
committed
Add: reload to profile for Fusebox
1 parent d4688df commit daba3d8

File tree

7 files changed

+87
-56
lines changed

7 files changed

+87
-56
lines changed

packages/react-devtools-shared/src/backend/agent.js

+28-7
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import type {
3535
PathMatch,
3636
RendererID,
3737
RendererInterface,
38+
DevToolsHook,
3839
DevToolsHookSettings,
3940
} from './types';
4041
import type {ComponentFilter} from 'react-devtools-shared/src/frontend/types';
@@ -139,6 +140,24 @@ type PersistedSelection = {
139140
path: Array<PathFrame>,
140141
};
141142

143+
function getShouldProfileOnReload(): boolean {
144+
const hook: ?DevToolsHook = window.__REACT_DEVTOOLS_GLOBAL_HOOK__;
145+
146+
return (
147+
sessionStorageGetItem(SESSION_STORAGE_RELOAD_AND_PROFILE_KEY) === 'true' ||
148+
hook?.getShouldProfileOnReload?.() === true
149+
);
150+
}
151+
152+
function getRecordChangeDescriptions(): boolean {
153+
const hook: ?DevToolsHook = window.__REACT_DEVTOOLS_GLOBAL_HOOK__;
154+
155+
return (
156+
sessionStorageGetItem(SESSION_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY) ===
157+
'true' || hook?.getProfileRecordChangeDescriptions?.() === true
158+
);
159+
}
160+
142161
export default class Agent extends EventEmitter<{
143162
hideNativeHighlight: [],
144163
showNativeHighlight: [HostInstance],
@@ -163,17 +182,15 @@ export default class Agent extends EventEmitter<{
163182
constructor(bridge: BackendBridge) {
164183
super();
165184

166-
if (
167-
sessionStorageGetItem(SESSION_STORAGE_RELOAD_AND_PROFILE_KEY) === 'true'
168-
) {
169-
this._recordChangeDescriptions =
170-
sessionStorageGetItem(
171-
SESSION_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY,
172-
) === 'true';
185+
const hook: ?DevToolsHook = window.__REACT_DEVTOOLS_GLOBAL_HOOK__;
186+
if (getShouldProfileOnReload()) {
187+
this._recordChangeDescriptions = getRecordChangeDescriptions();
173188
this._isProfiling = true;
174189

175190
sessionStorageRemoveItem(SESSION_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY);
176191
sessionStorageRemoveItem(SESSION_STORAGE_RELOAD_AND_PROFILE_KEY);
192+
193+
hook?.setShouldProfileOnReload?.(false);
177194
}
178195

179196
const persistedSelectionString = sessionStorageGetItem(
@@ -677,6 +694,10 @@ export default class Agent extends EventEmitter<{
677694

678695
reloadAndProfile: (recordChangeDescriptions: boolean) => void =
679696
recordChangeDescriptions => {
697+
const hook: ?DevToolsHook = window.__REACT_DEVTOOLS_GLOBAL_HOOK__;
698+
hook?.setShouldProfileOnReload?.(true);
699+
hook?.setProfileRecordChangeDescriptions?.(recordChangeDescriptions);
700+
680701
sessionStorageSetItem(SESSION_STORAGE_RELOAD_AND_PROFILE_KEY, 'true');
681702
sessionStorageSetItem(
682703
SESSION_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY,

packages/react-devtools-shared/src/backend/fiber/renderer.js

+5-4
Original file line numberDiff line numberDiff line change
@@ -5193,12 +5193,13 @@ export function attach(
51935193

51945194
// Automatically start profiling so that we don't miss timing info from initial "mount".
51955195
if (
5196-
sessionStorageGetItem(SESSION_STORAGE_RELOAD_AND_PROFILE_KEY) === 'true'
5196+
sessionStorageGetItem(SESSION_STORAGE_RELOAD_AND_PROFILE_KEY) === 'true' ||
5197+
hook?.getShouldProfileOnReload?.() === true
51975198
) {
5198-
startProfiling(
5199+
const shouldRecordChangeDescriptions =
51995200
sessionStorageGetItem(SESSION_STORAGE_RECORD_CHANGE_DESCRIPTIONS_KEY) ===
5200-
'true',
5201-
);
5201+
'true' || hook?.getProfileRecordChangeDescriptions?.() === true;
5202+
startProfiling(shouldRecordChangeDescriptions);
52025203
}
52035204

52045205
function getNearestFiber(devtoolsInstance: DevToolsInstance): null | Fiber {

packages/react-devtools-shared/src/backend/types.js

+7
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,13 @@ export type DevToolsHook = {
516516
didError?: boolean,
517517
) => void,
518518

519+
setShouldProfileOnReload: ?(shouldProfileOnReload: boolean) => void,
520+
getShouldProfileOnReload: ?() => boolean,
521+
setProfileRecordChangeDescriptions: ?(
522+
recordChangeDescriptions: boolean,
523+
) => void,
524+
getProfileRecordChangeDescriptions: ?() => boolean,
525+
519526
// Timeline internal module filtering
520527
getInternalModuleRanges: () => Array<[string, string]>,
521528
registerInternalModuleStart: (moduleStartError: Error) => void,

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

+26-24
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,10 @@ export default class ProfilerStore extends EventEmitter<{
289289
};
290290

291291
onProfilingStatus: (isProfiling: boolean) => void = isProfiling => {
292+
if (this._isProfiling === isProfiling) {
293+
return;
294+
}
295+
292296
if (isProfiling) {
293297
this._dataBackends.splice(0);
294298
this._dataFrontend = null;
@@ -315,36 +319,34 @@ export default class ProfilerStore extends EventEmitter<{
315319
});
316320
}
317321

318-
if (this._isProfiling !== isProfiling) {
319-
this._isProfiling = isProfiling;
322+
this._isProfiling = isProfiling;
320323

321-
// Invalidate suspense cache if profiling data is being (re-)recorded.
322-
// Note that we clear again, in case any views read from the cache while profiling.
323-
// (That would have resolved a now-stale value without any profiling data.)
324-
this._cache.invalidate();
324+
// Invalidate suspense cache if profiling data is being (re-)recorded.
325+
// Note that we clear again, in case any views read from the cache while profiling.
326+
// (That would have resolved a now-stale value without any profiling data.)
327+
this._cache.invalidate();
325328

326-
this.emit('isProfiling');
329+
this.emit('isProfiling');
327330

328-
// If we've just finished a profiling session, we need to fetch data stored in each renderer interface
329-
// and re-assemble it on the front-end into a format (ProfilingDataFrontend) that can power the Profiler UI.
330-
// During this time, DevTools UI should probably not be interactive.
331-
if (!isProfiling) {
332-
this._dataBackends.splice(0);
333-
this._rendererQueue.clear();
331+
// If we've just finished a profiling session, we need to fetch data stored in each renderer interface
332+
// and re-assemble it on the front-end into a format (ProfilingDataFrontend) that can power the Profiler UI.
333+
// During this time, DevTools UI should probably not be interactive.
334+
if (!isProfiling) {
335+
this._dataBackends.splice(0);
336+
this._rendererQueue.clear();
334337

335-
// Only request data from renderers that actually logged it.
336-
// This avoids unnecessary bridge requests and also avoids edge case mixed renderer bugs.
337-
// (e.g. when v15 and v16 are both present)
338-
this._rendererIDsThatReportedProfilingData.forEach(rendererID => {
339-
if (!this._rendererQueue.has(rendererID)) {
340-
this._rendererQueue.add(rendererID);
338+
// Only request data from renderers that actually logged it.
339+
// This avoids unnecessary bridge requests and also avoids edge case mixed renderer bugs.
340+
// (e.g. when v15 and v16 are both present)
341+
this._rendererIDsThatReportedProfilingData.forEach(rendererID => {
342+
if (!this._rendererQueue.has(rendererID)) {
343+
this._rendererQueue.add(rendererID);
341344

342-
this._bridge.send('getProfilingData', {rendererID});
343-
}
344-
});
345+
this._bridge.send('getProfilingData', {rendererID});
346+
}
347+
});
345348

346-
this.emit('isProcessingData');
347-
}
349+
this.emit('isProcessingData');
348350
}
349351
};
350352
}

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

+15-6
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ export type Config = {
7575
supportsInspectMatchingDOMElement?: boolean,
7676
supportsClickToInspect?: boolean,
7777
supportsReloadAndProfile?: boolean,
78+
bypassReloadAndProfileCapabilityChecks?: boolean,
7879
supportsTimeline?: boolean,
7980
supportsTraceUpdates?: boolean,
8081
};
@@ -179,6 +180,7 @@ export default class Store extends EventEmitter<{
179180
_supportsInspectMatchingDOMElement: boolean = false;
180181
_supportsClickToInspect: boolean = false;
181182
_supportsReloadAndProfile: boolean = false;
183+
_bypassReloadAndProfileCapabilityChecks: boolean = false;
182184
_supportsTimeline: boolean = false;
183185
_supportsTraceUpdates: boolean = false;
184186

@@ -222,6 +224,7 @@ export default class Store extends EventEmitter<{
222224
supportsInspectMatchingDOMElement,
223225
supportsClickToInspect,
224226
supportsReloadAndProfile,
227+
bypassReloadAndProfileCapabilityChecks,
225228
supportsTimeline,
226229
supportsTraceUpdates,
227230
checkBridgeProtocolCompatibility,
@@ -235,6 +238,9 @@ export default class Store extends EventEmitter<{
235238
if (supportsReloadAndProfile) {
236239
this._supportsReloadAndProfile = true;
237240
}
241+
if (bypassReloadAndProfileCapabilityChecks) {
242+
this._bypassReloadAndProfileCapabilityChecks = true;
243+
}
238244
if (supportsTimeline) {
239245
this._supportsTimeline = true;
240246
}
@@ -451,14 +457,17 @@ export default class Store extends EventEmitter<{
451457
return this._isNativeStyleEditorSupported;
452458
}
453459

460+
// Does the DevTools shell support reloading and eagerly injecting the renderer interface?
461+
// And if so, can the backend use the localStorage API and sync XHR?
462+
// All of these are currently required for the reload-and-profile feature to work.
454463
get supportsReloadAndProfile(): boolean {
455-
// Does the DevTools shell support reloading and eagerly injecting the renderer interface?
456-
// And if so, can the backend use the localStorage API and sync XHR?
457-
// All of these are currently required for the reload-and-profile feature to work.
464+
if (!this._supportsReloadAndProfile) {
465+
return false;
466+
}
467+
458468
return (
459-
this._supportsReloadAndProfile &&
460-
this._isBackendStorageAPISupported &&
461-
this._isSynchronousXHRSupported
469+
this._bypassReloadAndProfileCapabilityChecks ||
470+
(this._isBackendStorageAPISupported && this._isSynchronousXHRSupported)
462471
);
463472
}
464473

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

+5
Original file line numberDiff line numberDiff line change
@@ -634,6 +634,11 @@ export function installHook(
634634
onPostCommitFiberRoot,
635635
setStrictMode,
636636

637+
setShouldProfileOnReload: undefined,
638+
getShouldProfileOnReload: undefined,
639+
setProfileRecordChangeDescriptions: undefined,
640+
getProfileRecordChangeDescriptions: undefined,
641+
637642
// Schedule Profiler runtime helpers.
638643
// These internal React modules to report their own boundaries
639644
// which in turn enables the profiler to dim or filter internal frames.

packages/shared/ReactVersion.js

+1-15
Original file line numberDiff line numberDiff line change
@@ -1,15 +1 @@
1-
/**
2-
* Copyright (c) Meta Platforms, Inc. and affiliates.
3-
*
4-
* This source code is licensed under the MIT license found in the
5-
* LICENSE file in the root directory of this source tree.
6-
*/
7-
8-
// TODO: this is special because it gets imported during build.
9-
//
10-
// It exists as a placeholder so that DevTools can support work tag changes between releases.
11-
// When we next publish a release, update the matching TODO in backend/renderer.js
12-
// TODO: This module is used both by the release scripts and to expose a version
13-
// at runtime. We should instead inject the version number as part of the build
14-
// process, and use the ReactVersions.js module as the single source of truth.
15-
export default '19.0.0';
1+
export default '19.0.0-rc-bd788b41-20240906';

0 commit comments

Comments
 (0)