Skip to content

Commit 4225133

Browse files
author
Brian Vaughn
authored
DevTools: Scheduling profiler (#22006)
1 parent e3b76a8 commit 4225133

File tree

13 files changed

+100
-44
lines changed

13 files changed

+100
-44
lines changed

packages/react-devtools-scheduling-profiler/src/CanvasPage.js

+18-13
Original file line numberDiff line numberDiff line change
@@ -230,18 +230,21 @@ function AutoSizedCanvas({data, height, width}: AutoSizedCanvasProps) {
230230
schedulingEventsViewRef.current = schedulingEventsView;
231231
const schedulingEventsViewWrapper = createViewHelper(schedulingEventsView);
232232

233-
const suspenseEventsView = new SuspenseEventsView(
234-
surface,
235-
defaultFrame,
236-
data,
237-
);
238-
suspenseEventsViewRef.current = suspenseEventsView;
239-
const suspenseEventsViewWrapper = createViewHelper(
240-
suspenseEventsView,
241-
'suspense',
242-
true,
243-
true,
244-
);
233+
let suspenseEventsViewWrapper = null;
234+
if (data.suspenseEvents.length > 0) {
235+
const suspenseEventsView = new SuspenseEventsView(
236+
surface,
237+
defaultFrame,
238+
data,
239+
);
240+
suspenseEventsViewRef.current = suspenseEventsView;
241+
suspenseEventsViewWrapper = createViewHelper(
242+
suspenseEventsView,
243+
'suspense',
244+
true,
245+
true,
246+
);
247+
}
245248

246249
const reactMeasuresView = new ReactMeasuresView(
247250
surface,
@@ -286,7 +289,9 @@ function AutoSizedCanvas({data, height, width}: AutoSizedCanvasProps) {
286289
}
287290
rootView.addSubview(nativeEventsViewWrapper);
288291
rootView.addSubview(schedulingEventsViewWrapper);
289-
rootView.addSubview(suspenseEventsViewWrapper);
292+
if (suspenseEventsViewWrapper !== null) {
293+
rootView.addSubview(suspenseEventsViewWrapper);
294+
}
290295
rootView.addSubview(reactMeasuresViewWrapper);
291296
rootView.addSubview(flamechartViewWrapper);
292297

packages/react-devtools-scheduling-profiler/src/content-views/NativeEventsView.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@
1010
import type {NativeEvent, ReactProfilerData} from '../types';
1111
import type {
1212
Interaction,
13+
IntrinsicSize,
1314
MouseMoveInteraction,
1415
Rect,
15-
Size,
1616
ViewRefs,
1717
} from '../view-base';
1818

@@ -38,7 +38,7 @@ const ROW_WITH_BORDER_HEIGHT = NATIVE_EVENT_HEIGHT + BORDER_SIZE;
3838
export class NativeEventsView extends View {
3939
_depthToNativeEvent: Map<number, NativeEvent[]>;
4040
_hoveredEvent: NativeEvent | null = null;
41-
_intrinsicSize: Size;
41+
_intrinsicSize: IntrinsicSize;
4242
_maxDepth: number = 0;
4343
_profilerData: ReactProfilerData;
4444

@@ -73,6 +73,7 @@ export class NativeEventsView extends View {
7373
this._intrinsicSize = {
7474
width: duration,
7575
height: (this._maxDepth + 1) * ROW_WITH_BORDER_HEIGHT,
76+
hideScrollBarIfLessThanHeight: ROW_WITH_BORDER_HEIGHT,
7677
};
7778
}
7879

@@ -239,7 +240,6 @@ export class NativeEventsView extends View {
239240
hoverTimestamp <= timestamp + duration
240241
) {
241242
viewRefs.hoveredView = this;
242-
243243
onHover(nativeEvent);
244244
return;
245245
}

packages/react-devtools-scheduling-profiler/src/content-views/ReactMeasuresView.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@
1010
import type {ReactLane, ReactMeasure, ReactProfilerData} from '../types';
1111
import type {
1212
Interaction,
13+
IntrinsicSize,
1314
MouseMoveInteraction,
1415
Rect,
15-
SizeWithMaxHeight,
1616
ViewRefs,
1717
} from '../view-base';
1818

@@ -45,7 +45,7 @@ function getMeasuresForLane(
4545

4646
export class ReactMeasuresView extends View {
4747
_profilerData: ReactProfilerData;
48-
_intrinsicSize: SizeWithMaxHeight;
48+
_intrinsicSize: IntrinsicSize;
4949

5050
_lanesToRender: ReactLane[];
5151
_laneToMeasures: Map<ReactLane, ReactMeasure[]>;
@@ -78,6 +78,7 @@ export class ReactMeasuresView extends View {
7878
this._intrinsicSize = {
7979
width: this._profilerData.duration,
8080
height: this._lanesToRender.length * REACT_LANE_HEIGHT,
81+
hideScrollBarIfLessThanHeight: REACT_LANE_HEIGHT,
8182
maxInitialHeight: MAX_ROWS_TO_SHOW_INITIALLY * REACT_LANE_HEIGHT,
8283
};
8384
}

packages/react-devtools-scheduling-profiler/src/content-views/SuspenseEventsView.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@
1010
import type {SuspenseEvent, ReactProfilerData} from '../types';
1111
import type {
1212
Interaction,
13+
IntrinsicSize,
1314
MouseMoveInteraction,
1415
Rect,
15-
SizeWithMaxHeight,
1616
ViewRefs,
1717
} from '../view-base';
1818

@@ -45,7 +45,7 @@ const MAX_ROWS_TO_SHOW_INITIALLY = 3;
4545
export class SuspenseEventsView extends View {
4646
_depthToSuspenseEvent: Map<number, SuspenseEvent[]>;
4747
_hoveredEvent: SuspenseEvent | null = null;
48-
_intrinsicSize: SizeWithMaxHeight;
48+
_intrinsicSize: IntrinsicSize;
4949
_maxDepth: number = 0;
5050
_profilerData: ReactProfilerData;
5151

packages/react-devtools-scheduling-profiler/src/content-views/UserTimingMarksView.js

-1
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,6 @@ export class UserTimingMarksView extends View {
224224
timestamp - timestampAllowance <= hoverTimestamp &&
225225
hoverTimestamp <= timestamp + timestampAllowance
226226
) {
227-
this.currentCursor = 'context-menu';
228227
viewRefs.hoveredView = this;
229228
onHover(mark);
230229
return;

packages/react-devtools-scheduling-profiler/src/view-base/ResizableView.js

+22-5
Original file line numberDiff line numberDiff line change
@@ -255,12 +255,21 @@ export class ResizableView extends View {
255255
}
256256

257257
desiredSize() {
258-
const resizeBarDesiredSize = this._resizeBar.desiredSize();
258+
const subviewDesiredSize = this._subview.desiredSize();
259259

260-
return {
261-
width: this.frame.size.width,
262-
height: this._layoutState.barOffsetY + resizeBarDesiredSize.height,
263-
};
260+
if (this._shouldRenderResizeBar()) {
261+
const resizeBarDesiredSize = this._resizeBar.desiredSize();
262+
263+
return {
264+
width: this.frame.size.width,
265+
height: this._layoutState.barOffsetY + resizeBarDesiredSize.height,
266+
};
267+
} else {
268+
return {
269+
width: this.frame.size.width,
270+
height: subviewDesiredSize.height,
271+
};
272+
}
264273
}
265274

266275
layoutSubviews() {
@@ -270,6 +279,14 @@ export class ResizableView extends View {
270279
super.layoutSubviews();
271280
}
272281

282+
_shouldRenderResizeBar() {
283+
const subviewDesiredSize = this._subview.desiredSize();
284+
return subviewDesiredSize.hideScrollBarIfLessThanHeight != null
285+
? subviewDesiredSize.height >
286+
subviewDesiredSize.hideScrollBarIfLessThanHeight
287+
: true;
288+
}
289+
273290
_updateLayoutStateAndResizeBar(barOffsetY: number) {
274291
if (barOffsetY <= RESIZE_BAR_WITH_LABEL_HEIGHT - RESIZE_BAR_HEIGHT) {
275292
barOffsetY = 0;

packages/react-devtools-scheduling-profiler/src/view-base/View.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*/
99

1010
import type {Interaction} from './useCanvasInteraction';
11-
import type {Rect, Size, SizeWithMaxHeight} from './geometry';
11+
import type {IntrinsicSize, Rect, Size} from './geometry';
1212
import type {Layouter} from './layouter';
1313
import type {ViewRefs} from './Surface';
1414

@@ -140,7 +140,7 @@ export class View {
140140
*
141141
* Can be overridden by subclasses.
142142
*/
143-
desiredSize(): Size | SizeWithMaxHeight {
143+
desiredSize(): Size | IntrinsicSize {
144144
if (this._needsDisplay) {
145145
this.layoutSubviews();
146146
}

packages/react-devtools-scheduling-profiler/src/view-base/geometry.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,14 @@
99

1010
export type Point = $ReadOnly<{|x: number, y: number|}>;
1111
export type Size = $ReadOnly<{|width: number, height: number|}>;
12-
export type SizeWithMaxHeight = {|
12+
export type IntrinsicSize = {|
1313
...Size,
14+
15+
// If content is this height or less, hide the scrollbar entirely,
16+
// so that it doesn't take up vertical space unnecessarily (e.g. for a single row of content).
17+
hideScrollBarIfLessThanHeight?: number,
18+
19+
// The initial height should be the height of the content, or this, whichever is less.
1420
maxInitialHeight?: number,
1521
|};
1622
export type Rect = $ReadOnly<{|origin: Point, size: Size|}>;

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

+16-8
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,6 @@ export const THEME_STYLES: {[style: Theme | DisplayDensity]: any} = {
165165
'--color-scheduling-profiler-react-suspense-unresolved-hover': '#93959a',
166166
'--color-scheduling-profiler-text-color': '#000000',
167167
'--color-scheduling-profiler-react-work-border': '#ffffff',
168-
'--color-scroll-thumb': '#c2c2c2',
169-
'--color-scroll-track': '#fafafa',
170168
'--color-search-match': 'yellow',
171169
'--color-search-match-current': '#f7923b',
172170
'--color-selected-tree-highlight-active': 'rgba(0, 136, 250, 0.1)',
@@ -180,12 +178,18 @@ export const THEME_STYLES: {[style: Theme | DisplayDensity]: any} = {
180178
'--color-toggle-background-on': '#0088fa',
181179
'--color-toggle-background-off': '#cfd1d5',
182180
'--color-toggle-text': '#ffffff',
183-
'--color-tooltip-background': 'rgba(0, 0, 0, 0.9)',
184-
'--color-tooltip-text': '#ffffff',
185181
'--color-warning-background': '#fb3655',
186182
'--color-warning-background-hover': '#f82042',
187183
'--color-warning-text-color': '#ffffff',
188184
'--color-warning-text-color-inverted': '#fd4d69',
185+
186+
// The styles below should be kept in sync with 'root.css'
187+
// They are repeated there because they're used by e.g. tooltips or context menus
188+
// which get rendered outside of the DOM subtree (where normal theme/styles are written).
189+
'--color-scroll-thumb': '#c2c2c2',
190+
'--color-scroll-track': '#fafafa',
191+
'--color-tooltip-background': 'rgba(0, 0, 0, 0.9)',
192+
'--color-tooltip-text': '#ffffff',
189193
},
190194
dark: {
191195
'--color-attribute-name': '#9d87d2',
@@ -291,8 +295,6 @@ export const THEME_STYLES: {[style: Theme | DisplayDensity]: any} = {
291295
'--color-scheduling-profiler-react-suspense-unresolved-hover': '#93959a',
292296
'--color-scheduling-profiler-text-color': '#000000',
293297
'--color-scheduling-profiler-react-work-border': '#ffffff',
294-
'--color-scroll-thumb': '#afb3b9',
295-
'--color-scroll-track': '#313640',
296298
'--color-search-match': 'yellow',
297299
'--color-search-match-current': '#f7923b',
298300
'--color-selected-tree-highlight-active': 'rgba(23, 143, 185, 0.15)',
@@ -307,12 +309,18 @@ export const THEME_STYLES: {[style: Theme | DisplayDensity]: any} = {
307309
'--color-toggle-background-on': '#178fb9',
308310
'--color-toggle-background-off': '#777d88',
309311
'--color-toggle-text': '#ffffff',
310-
'--color-tooltip-background': 'rgba(255, 255, 255, 0.95)',
311-
'--color-tooltip-text': '#000000',
312312
'--color-warning-background': '#ee1638',
313313
'--color-warning-background-hover': '#da1030',
314314
'--color-warning-text-color': '#ffffff',
315315
'--color-warning-text-color-inverted': '#ee1638',
316+
317+
// The styles below should be kept in sync with 'root.css'
318+
// They are repeated there because they're used by e.g. tooltips or context menus
319+
// which get rendered outside of the DOM subtree (where normal theme/styles are written).
320+
'--color-scroll-thumb': '#afb3b9',
321+
'--color-scroll-track': '#313640',
322+
'--color-tooltip-background': 'rgba(255, 255, 255, 0.95)',
323+
'--color-tooltip-text': '#000000',
316324
},
317325
compact: {
318326
'--font-size-monospace-small': '9px',

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

+9-5
Original file line numberDiff line numberDiff line change
@@ -68,11 +68,15 @@ export default function ContextMenu({children, id}: Props) {
6868
const element = bodyAccessorRef.current;
6969
if (element !== null) {
7070
const ownerDocument = element.ownerDocument;
71-
containerRef.current = ownerDocument.createElement('div');
72-
ownerDocument.body.appendChild(containerRef.current);
73-
return () => {
74-
ownerDocument.body.removeChild(containerRef.current);
75-
};
71+
containerRef.current = ownerDocument.querySelector(
72+
'[data-react-devtools-portal-root]',
73+
);
74+
75+
if (containerRef.current == null) {
76+
console.warn(
77+
'DevTools tooltip root node not found; context menus will be disabled.',
78+
);
79+
}
7680
}
7781
}, []);
7882

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

+4-1
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,10 @@ export default function DevTools({
222222
<ProfilerContextController>
223223
<SchedulingProfilerContextController>
224224
<ThemeProvider>
225-
<div className={styles.DevTools} ref={devToolsRef}>
225+
<div
226+
className={styles.DevTools}
227+
ref={devToolsRef}
228+
data-react-devtools-portal-root={true}>
226229
{showTabBar && (
227230
<div className={styles.TabBar}>
228231
<ReactLogo />

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

+9-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,15 @@ export default function portaledContent(
3232
// The ThemeProvider works by writing DOM style variables to an HTMLDivElement.
3333
// Because Portals render in a different DOM subtree, these variables don't propagate.
3434
// So in this case, we need to re-wrap portaled content in a second ThemeProvider.
35-
children = <ThemeProvider>{children}</ThemeProvider>;
35+
children = (
36+
<ThemeProvider>
37+
<div
38+
data-react-devtools-portal-root={true}
39+
style={{width: '100vw', height: '100vh'}}>
40+
{children}
41+
</div>
42+
</ThemeProvider>
43+
);
3644
}
3745

3846
return portalContainer != null

packages/react-devtools-shared/src/devtools/views/root.css

+5
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
:root {
2+
/**
3+
* The light and dark theme styles below should be kept in sync with 'react-devtools-shared/src/constants'
4+
* They are repeated here because they're used by e.g. tooltips or context menus
5+
* which get rendered outside of the DOM subtree (where normal theme/styles are written).
6+
*/
27

38
/* Light theme */
49
--light-color-scroll-thumb: #c2c2c2;

0 commit comments

Comments
 (0)