diff --git a/front_end/core/host/RNPerfMetrics.ts b/front_end/core/host/RNPerfMetrics.ts index 1955fd039c4..b8b6b8affa1 100644 --- a/front_end/core/host/RNPerfMetrics.ts +++ b/front_end/core/host/RNPerfMetrics.ts @@ -298,6 +298,15 @@ class RNPerfMetrics { }); } + manualBreakpointSetSucceeded(bpSettingDuration: number): void { + this.sendEvent({ + eventName: 'ManualBreakpointSetSucceeded', + params: { + bpSettingDuration + } + }); + } + panelShown(_panelName: string, _isLaunching?: boolean): void { // no-op // We only care about the "main" and "drawer" panels for now via panelShownInLocation(…) @@ -489,12 +498,19 @@ export type StackTraceFrameUrlResolutionFailed = Readonly<{ }>, }>; +export type ManualBreakpointSetSucceeded = Readonly<{ + eventName: 'ManualBreakpointSetSucceeded', + params: Readonly<{ + bpSettingDuration: number, + }>, +}>; + export type ReactNativeChromeDevToolsEvent = EntrypointLoadingStartedEvent|EntrypointLoadingFinishedEvent|DebuggerReadyEvent|BrowserVisibilityChangeEvent| BrowserErrorEvent|RemoteDebuggingTerminatedEvent|DeveloperResourceLoadingStartedEvent| DeveloperResourceLoadingFinishedEvent|FuseboxSetClientMetadataStartedEvent|FuseboxSetClientMetadataFinishedEvent| MemoryPanelActionStartedEvent|MemoryPanelActionFinishedEvent|PanelShownEvent|PanelClosedEvent| StackTraceSymbolicationSucceeded|StackTraceSymbolicationFailed|StackTraceFrameUrlResolutionSucceeded| - StackTraceFrameUrlResolutionFailed; + StackTraceFrameUrlResolutionFailed|ManualBreakpointSetSucceeded; export type DecoratedReactNativeChromeDevToolsEvent = CommonEventFields&ReactNativeChromeDevToolsEvent; diff --git a/front_end/panels/sources/DebuggerPlugin.ts b/front_end/panels/sources/DebuggerPlugin.ts index 7335d016be4..6a18004a8e1 100644 --- a/front_end/panels/sources/DebuggerPlugin.ts +++ b/front_end/panels/sources/DebuggerPlugin.ts @@ -308,7 +308,37 @@ export class DebuggerPlugin extends Plugin { }), CodeMirror.lineNumbers({ domEventHandlers: { - click: (view, block, event) => this.handleGutterClick(view.state.doc.lineAt(block.from), event as MouseEvent), + click: (view, block, event) => { + if (Host.rnPerfMetrics.isEnabled()) { + const element = (event.target as Element); + const isClickAddingBreakpoint = ( + element.classList && + element.classList.contains('cm-gutterElement') && + !element.classList.contains('cm-breakpoint') + ); + if (isClickAddingBreakpoint) { + const setBreakpointStartMs = Date.now(); + const observer = new MutationObserver(mutations => { + mutations.forEach(mutation => { + if ( + mutation.type === 'attributes' && + mutation.attributeName === 'class' + ) { + if (element.classList.contains('cm-breakpoint')) { + Host.rnPerfMetrics.manualBreakpointSetSucceeded(Date.now() - setBreakpointStartMs); + observer.disconnect(); + } + } + }); + }); + observer.observe(element, { attributes: true }); + // if there's no breakpoint set on the line in 3 seconds, don't track the event. + // this could happen due to a different line getting the breakpoint, or an error. + setTimeout(() => observer.disconnect(), 3000); + } + } + return this.handleGutterClick(view.state.doc.lineAt(block.from), event as MouseEvent); + }, }, }), breakpointMarkers,