Skip to content

Commit

Permalink
DevTools: Scheduling profiler (facebook#22006)
Browse files Browse the repository at this point in the history
  • Loading branch information
Brian Vaughn authored and zhengjitf committed Apr 15, 2022
1 parent 7819f89 commit 3f2eddf
Show file tree
Hide file tree
Showing 13 changed files with 100 additions and 44 deletions.
31 changes: 18 additions & 13 deletions packages/react-devtools-scheduling-profiler/src/CanvasPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -230,18 +230,21 @@ function AutoSizedCanvas({data, height, width}: AutoSizedCanvasProps) {
schedulingEventsViewRef.current = schedulingEventsView;
const schedulingEventsViewWrapper = createViewHelper(schedulingEventsView);

const suspenseEventsView = new SuspenseEventsView(
surface,
defaultFrame,
data,
);
suspenseEventsViewRef.current = suspenseEventsView;
const suspenseEventsViewWrapper = createViewHelper(
suspenseEventsView,
'suspense',
true,
true,
);
let suspenseEventsViewWrapper = null;
if (data.suspenseEvents.length > 0) {
const suspenseEventsView = new SuspenseEventsView(
surface,
defaultFrame,
data,
);
suspenseEventsViewRef.current = suspenseEventsView;
suspenseEventsViewWrapper = createViewHelper(
suspenseEventsView,
'suspense',
true,
true,
);
}

const reactMeasuresView = new ReactMeasuresView(
surface,
Expand Down Expand Up @@ -286,7 +289,9 @@ function AutoSizedCanvas({data, height, width}: AutoSizedCanvasProps) {
}
rootView.addSubview(nativeEventsViewWrapper);
rootView.addSubview(schedulingEventsViewWrapper);
rootView.addSubview(suspenseEventsViewWrapper);
if (suspenseEventsViewWrapper !== null) {
rootView.addSubview(suspenseEventsViewWrapper);
}
rootView.addSubview(reactMeasuresViewWrapper);
rootView.addSubview(flamechartViewWrapper);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
import type {NativeEvent, ReactProfilerData} from '../types';
import type {
Interaction,
IntrinsicSize,
MouseMoveInteraction,
Rect,
Size,
ViewRefs,
} from '../view-base';

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

Expand Down Expand Up @@ -73,6 +73,7 @@ export class NativeEventsView extends View {
this._intrinsicSize = {
width: duration,
height: (this._maxDepth + 1) * ROW_WITH_BORDER_HEIGHT,
hideScrollBarIfLessThanHeight: ROW_WITH_BORDER_HEIGHT,
};
}

Expand Down Expand Up @@ -239,7 +240,6 @@ export class NativeEventsView extends View {
hoverTimestamp <= timestamp + duration
) {
viewRefs.hoveredView = this;

onHover(nativeEvent);
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
import type {ReactLane, ReactMeasure, ReactProfilerData} from '../types';
import type {
Interaction,
IntrinsicSize,
MouseMoveInteraction,
Rect,
SizeWithMaxHeight,
ViewRefs,
} from '../view-base';

Expand Down Expand Up @@ -45,7 +45,7 @@ function getMeasuresForLane(

export class ReactMeasuresView extends View {
_profilerData: ReactProfilerData;
_intrinsicSize: SizeWithMaxHeight;
_intrinsicSize: IntrinsicSize;

_lanesToRender: ReactLane[];
_laneToMeasures: Map<ReactLane, ReactMeasure[]>;
Expand Down Expand Up @@ -78,6 +78,7 @@ export class ReactMeasuresView extends View {
this._intrinsicSize = {
width: this._profilerData.duration,
height: this._lanesToRender.length * REACT_LANE_HEIGHT,
hideScrollBarIfLessThanHeight: REACT_LANE_HEIGHT,
maxInitialHeight: MAX_ROWS_TO_SHOW_INITIALLY * REACT_LANE_HEIGHT,
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
import type {SuspenseEvent, ReactProfilerData} from '../types';
import type {
Interaction,
IntrinsicSize,
MouseMoveInteraction,
Rect,
SizeWithMaxHeight,
ViewRefs,
} from '../view-base';

Expand Down Expand Up @@ -45,7 +45,7 @@ const MAX_ROWS_TO_SHOW_INITIALLY = 3;
export class SuspenseEventsView extends View {
_depthToSuspenseEvent: Map<number, SuspenseEvent[]>;
_hoveredEvent: SuspenseEvent | null = null;
_intrinsicSize: SizeWithMaxHeight;
_intrinsicSize: IntrinsicSize;
_maxDepth: number = 0;
_profilerData: ReactProfilerData;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,6 @@ export class UserTimingMarksView extends View {
timestamp - timestampAllowance <= hoverTimestamp &&
hoverTimestamp <= timestamp + timestampAllowance
) {
this.currentCursor = 'context-menu';
viewRefs.hoveredView = this;
onHover(mark);
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -255,12 +255,21 @@ export class ResizableView extends View {
}

desiredSize() {
const resizeBarDesiredSize = this._resizeBar.desiredSize();
const subviewDesiredSize = this._subview.desiredSize();

return {
width: this.frame.size.width,
height: this._layoutState.barOffsetY + resizeBarDesiredSize.height,
};
if (this._shouldRenderResizeBar()) {
const resizeBarDesiredSize = this._resizeBar.desiredSize();

return {
width: this.frame.size.width,
height: this._layoutState.barOffsetY + resizeBarDesiredSize.height,
};
} else {
return {
width: this.frame.size.width,
height: subviewDesiredSize.height,
};
}
}

layoutSubviews() {
Expand All @@ -270,6 +279,14 @@ export class ResizableView extends View {
super.layoutSubviews();
}

_shouldRenderResizeBar() {
const subviewDesiredSize = this._subview.desiredSize();
return subviewDesiredSize.hideScrollBarIfLessThanHeight != null
? subviewDesiredSize.height >
subviewDesiredSize.hideScrollBarIfLessThanHeight
: true;
}

_updateLayoutStateAndResizeBar(barOffsetY: number) {
if (barOffsetY <= RESIZE_BAR_WITH_LABEL_HEIGHT - RESIZE_BAR_HEIGHT) {
barOffsetY = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
*/

import type {Interaction} from './useCanvasInteraction';
import type {Rect, Size, SizeWithMaxHeight} from './geometry';
import type {IntrinsicSize, Rect, Size} from './geometry';
import type {Layouter} from './layouter';
import type {ViewRefs} from './Surface';

Expand Down Expand Up @@ -140,7 +140,7 @@ export class View {
*
* Can be overridden by subclasses.
*/
desiredSize(): Size | SizeWithMaxHeight {
desiredSize(): Size | IntrinsicSize {
if (this._needsDisplay) {
this.layoutSubviews();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,14 @@

export type Point = $ReadOnly<{|x: number, y: number|}>;
export type Size = $ReadOnly<{|width: number, height: number|}>;
export type SizeWithMaxHeight = {|
export type IntrinsicSize = {|
...Size,

// If content is this height or less, hide the scrollbar entirely,
// so that it doesn't take up vertical space unnecessarily (e.g. for a single row of content).
hideScrollBarIfLessThanHeight?: number,

// The initial height should be the height of the content, or this, whichever is less.
maxInitialHeight?: number,
|};
export type Rect = $ReadOnly<{|origin: Point, size: Size|}>;
Expand Down
24 changes: 16 additions & 8 deletions packages/react-devtools-shared/src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,6 @@ export const THEME_STYLES: {[style: Theme | DisplayDensity]: any} = {
'--color-scheduling-profiler-react-suspense-unresolved-hover': '#93959a',
'--color-scheduling-profiler-text-color': '#000000',
'--color-scheduling-profiler-react-work-border': '#ffffff',
'--color-scroll-thumb': '#c2c2c2',
'--color-scroll-track': '#fafafa',
'--color-search-match': 'yellow',
'--color-search-match-current': '#f7923b',
'--color-selected-tree-highlight-active': 'rgba(0, 136, 250, 0.1)',
Expand All @@ -180,12 +178,18 @@ export const THEME_STYLES: {[style: Theme | DisplayDensity]: any} = {
'--color-toggle-background-on': '#0088fa',
'--color-toggle-background-off': '#cfd1d5',
'--color-toggle-text': '#ffffff',
'--color-tooltip-background': 'rgba(0, 0, 0, 0.9)',
'--color-tooltip-text': '#ffffff',
'--color-warning-background': '#fb3655',
'--color-warning-background-hover': '#f82042',
'--color-warning-text-color': '#ffffff',
'--color-warning-text-color-inverted': '#fd4d69',

// The styles below should be kept in sync with 'root.css'
// They are repeated there because they're used by e.g. tooltips or context menus
// which get rendered outside of the DOM subtree (where normal theme/styles are written).
'--color-scroll-thumb': '#c2c2c2',
'--color-scroll-track': '#fafafa',
'--color-tooltip-background': 'rgba(0, 0, 0, 0.9)',
'--color-tooltip-text': '#ffffff',
},
dark: {
'--color-attribute-name': '#9d87d2',
Expand Down Expand Up @@ -291,8 +295,6 @@ export const THEME_STYLES: {[style: Theme | DisplayDensity]: any} = {
'--color-scheduling-profiler-react-suspense-unresolved-hover': '#93959a',
'--color-scheduling-profiler-text-color': '#000000',
'--color-scheduling-profiler-react-work-border': '#ffffff',
'--color-scroll-thumb': '#afb3b9',
'--color-scroll-track': '#313640',
'--color-search-match': 'yellow',
'--color-search-match-current': '#f7923b',
'--color-selected-tree-highlight-active': 'rgba(23, 143, 185, 0.15)',
Expand All @@ -307,12 +309,18 @@ export const THEME_STYLES: {[style: Theme | DisplayDensity]: any} = {
'--color-toggle-background-on': '#178fb9',
'--color-toggle-background-off': '#777d88',
'--color-toggle-text': '#ffffff',
'--color-tooltip-background': 'rgba(255, 255, 255, 0.95)',
'--color-tooltip-text': '#000000',
'--color-warning-background': '#ee1638',
'--color-warning-background-hover': '#da1030',
'--color-warning-text-color': '#ffffff',
'--color-warning-text-color-inverted': '#ee1638',

// The styles below should be kept in sync with 'root.css'
// They are repeated there because they're used by e.g. tooltips or context menus
// which get rendered outside of the DOM subtree (where normal theme/styles are written).
'--color-scroll-thumb': '#afb3b9',
'--color-scroll-track': '#313640',
'--color-tooltip-background': 'rgba(255, 255, 255, 0.95)',
'--color-tooltip-text': '#000000',
},
compact: {
'--font-size-monospace-small': '9px',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,15 @@ export default function ContextMenu({children, id}: Props) {
const element = bodyAccessorRef.current;
if (element !== null) {
const ownerDocument = element.ownerDocument;
containerRef.current = ownerDocument.createElement('div');
ownerDocument.body.appendChild(containerRef.current);
return () => {
ownerDocument.body.removeChild(containerRef.current);
};
containerRef.current = ownerDocument.querySelector(
'[data-react-devtools-portal-root]',
);

if (containerRef.current == null) {
console.warn(
'DevTools tooltip root node not found; context menus will be disabled.',
);
}
}
}, []);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,10 @@ export default function DevTools({
<ProfilerContextController>
<SchedulingProfilerContextController>
<ThemeProvider>
<div className={styles.DevTools} ref={devToolsRef}>
<div
className={styles.DevTools}
ref={devToolsRef}
data-react-devtools-portal-root={true}>
{showTabBar && (
<div className={styles.TabBar}>
<ReactLogo />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,15 @@ export default function portaledContent(
// The ThemeProvider works by writing DOM style variables to an HTMLDivElement.
// Because Portals render in a different DOM subtree, these variables don't propagate.
// So in this case, we need to re-wrap portaled content in a second ThemeProvider.
children = <ThemeProvider>{children}</ThemeProvider>;
children = (
<ThemeProvider>
<div
data-react-devtools-portal-root={true}
style={{width: '100vw', height: '100vh'}}>
{children}
</div>
</ThemeProvider>
);
}

return portalContainer != null
Expand Down
5 changes: 5 additions & 0 deletions packages/react-devtools-shared/src/devtools/views/root.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
:root {
/**
* The light and dark theme styles below should be kept in sync with 'react-devtools-shared/src/constants'
* They are repeated here because they're used by e.g. tooltips or context menus
* which get rendered outside of the DOM subtree (where normal theme/styles are written).
*/

/* Light theme */
--light-color-scroll-thumb: #c2c2c2;
Expand Down

0 comments on commit 3f2eddf

Please sign in to comment.