Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DevTools: Scheduling profiler #22006

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
31 changes: 18 additions & 13 deletions packages/react-devtools-scheduling-profiler/src/CanvasPage.js
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
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
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
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
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
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
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
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
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
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
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
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
@@ -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