chrome-devtools-frontend 1.0.1558690 → 1.0.1561080
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/front_end/Images/src/container.svg +4 -0
- package/front_end/core/common/Gzip.ts +15 -0
- package/front_end/core/host/InspectorFrontendHostStub.ts +0 -3
- package/front_end/core/platform/ArrayUtilities.ts +13 -0
- package/front_end/core/root/Runtime.ts +0 -5
- package/front_end/core/sdk/CSSMetadata.ts +6 -6
- package/front_end/core/sdk/CSSModel.ts +2 -2
- package/front_end/core/sdk/DOMModel.ts +15 -3
- package/front_end/core/sdk/NetworkManager.ts +4 -0
- package/front_end/core/sdk/NetworkRequest.ts +9 -0
- package/front_end/core/sdk/OverlayModel.ts +20 -9
- package/front_end/entrypoints/main/MainImpl.ts +2 -1
- package/front_end/generated/InspectorBackendCommands.ts +6 -3
- package/front_end/generated/SupportedCSSProperties.js +64 -32
- package/front_end/generated/protocol-mapping.d.ts +16 -0
- package/front_end/generated/protocol-proxy-api.d.ts +12 -0
- package/front_end/generated/protocol.ts +38 -1
- package/front_end/models/ai_assistance/agents/StylingAgent.ts +1 -1
- package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.ts +11 -7
- package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +23 -22
- package/front_end/models/badges/UserBadges.ts +48 -16
- package/front_end/models/greendev/Prototypes.ts +6 -1
- package/front_end/models/trace/LanternComputationData.ts +4 -3
- package/front_end/models/trace/Processor.ts +6 -5
- package/front_end/models/trace/Styles.ts +10 -1
- package/front_end/models/trace/extras/TraceTree.ts +1 -1
- package/front_end/models/trace/handlers/LargestImagePaintHandler.ts +2 -2
- package/front_end/models/trace/handlers/MetaHandler.ts +14 -0
- package/front_end/models/trace/handlers/PageLoadMetricsHandler.ts +59 -34
- package/front_end/models/trace/helpers/Timing.ts +8 -1
- package/front_end/models/trace/insights/Common.ts +1 -1
- package/front_end/models/trace/insights/LCPBreakdown.ts +4 -4
- package/front_end/models/trace/insights/LCPDiscovery.ts +3 -3
- package/front_end/models/trace/insights/RenderBlocking.ts +1 -1
- package/front_end/models/trace/insights/types.ts +1 -1
- package/front_end/models/trace/types/TraceEvents.ts +62 -10
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +11 -142
- package/front_end/panels/ai_assistance/PatchWidget.ts +90 -72
- package/front_end/panels/ai_assistance/ai_assistance.ts +1 -0
- package/front_end/panels/ai_assistance/components/ChatInput.ts +701 -0
- package/front_end/panels/ai_assistance/components/ChatView.ts +71 -1268
- package/front_end/panels/ai_assistance/components/UserActionRow.ts +514 -31
- package/front_end/panels/ai_assistance/components/chatInput.css +387 -0
- package/front_end/panels/ai_assistance/components/chatView.css +38 -599
- package/front_end/panels/ai_assistance/components/userActionRow.css +230 -0
- package/front_end/panels/autofill/AutofillView.ts +2 -2
- package/front_end/panels/changes/ChangesView.ts +15 -1
- package/front_end/panels/changes/changesView.css +6 -0
- package/front_end/panels/common/AiCodeGenerationTeaser.ts +48 -12
- package/front_end/panels/common/BadgeNotification.ts +44 -58
- package/front_end/panels/common/CopyChangesToPrompt.ts +233 -0
- package/front_end/panels/common/aiCodeGenerationTeaser.css +14 -0
- package/front_end/panels/common/common.ts +2 -1
- package/front_end/panels/console/consoleView.css +1 -1
- package/front_end/panels/elements/CSSRuleValidator.ts +38 -0
- package/front_end/panels/elements/ElementsTreeElement.ts +222 -377
- package/front_end/panels/elements/ElementsTreeOutline.ts +0 -23
- package/front_end/panels/elements/ShortcutTreeElement.ts +57 -50
- package/front_end/panels/elements/StylePropertiesSection.ts +1 -3
- package/front_end/panels/elements/StylesSidebarPane.ts +15 -4
- package/front_end/panels/elements/components/AdornerManager.ts +5 -149
- package/front_end/panels/issues/HiddenIssuesRow.ts +1 -2
- package/front_end/panels/issues/IssueKindView.ts +2 -4
- package/front_end/panels/issues/IssueView.ts +2 -4
- package/front_end/panels/network/NetworkDataGridNode.ts +65 -1
- package/front_end/panels/network/NetworkLogView.ts +2 -4
- package/front_end/panels/network/NetworkLogViewColumns.ts +9 -0
- package/front_end/panels/screencast/ScreencastApp.ts +1 -0
- package/front_end/panels/settings/SettingsScreen.ts +3 -2
- package/front_end/panels/timeline/CompatibilityTracksAppender.ts +14 -1
- package/front_end/panels/timeline/StatusDialog.ts +4 -3
- package/front_end/panels/timeline/ThirdPartyTreeView.ts +1 -4
- package/front_end/panels/timeline/TimelineController.ts +185 -3
- package/front_end/panels/timeline/TimelineFlameChartDataProvider.ts +52 -25
- package/front_end/panels/timeline/TimelineFlameChartNetworkDataProvider.ts +3 -16
- package/front_end/panels/timeline/TimelineFlameChartView.ts +65 -21
- package/front_end/panels/timeline/TimelinePanel.ts +86 -126
- package/front_end/panels/timeline/TimelineTreeView.ts +1 -0
- package/front_end/panels/timeline/TimelineUIUtils.ts +28 -2
- package/front_end/panels/timeline/TimingsTrackAppender.ts +3 -1
- package/front_end/panels/timeline/components/SidebarSingleInsightSet.ts +1 -1
- package/front_end/panels/timeline/components/insights/BaseInsightComponent.ts +2 -2
- package/front_end/panels/timeline/components/insights/RenderBlocking.ts +6 -4
- package/front_end/panels/timeline/components/insights/Table.ts +3 -3
- package/front_end/panels/timeline/overlays/OverlaysImpl.ts +4 -0
- package/front_end/panels/timeline/timelinePanel.css +8 -1
- package/front_end/panels/timeline/utils/EntryNodes.ts +2 -1
- package/front_end/panels/whats_new/ReleaseNoteText.ts +15 -9
- package/front_end/panels/whats_new/resources/WNDT.md +6 -6
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/third_party/codemirror.next/rebuild.sh +1 -1
- package/front_end/third_party/lit/rebuild.sh +1 -1
- package/front_end/third_party/puppeteer/README.chromium +2 -2
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Page.d.ts +2 -3
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Page.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/api/Page.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/HTTPRequest.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/HTTPRequest.js +9 -0
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/HTTPRequest.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/HTTPResponse.d.ts +3 -0
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/HTTPResponse.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/HTTPResponse.js +9 -0
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/HTTPResponse.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/Request.d.ts +3 -0
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/Request.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/Request.js +10 -0
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/Request.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.js +8 -4
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/generated/injected.d.ts +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/generated/injected.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/generated/injected.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/generated/injected.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/injected/injected.d.ts +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.d.ts +3 -3
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js +3 -3
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Mutex.d.ts +2 -2
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.d.ts +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.d.ts +10 -1
- package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +13 -7
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Page.d.ts +2 -3
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Page.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Page.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/HTTPRequest.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/HTTPRequest.js +9 -0
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/HTTPRequest.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/HTTPResponse.d.ts +3 -0
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/HTTPResponse.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/HTTPResponse.js +9 -0
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/HTTPResponse.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/core/Request.d.ts +3 -0
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/core/Request.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/core/Request.js +10 -0
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/core/Request.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.js +8 -4
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/injected.d.ts +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/injected.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/injected.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/injected.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.d.ts +3 -3
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js +3 -3
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.d.ts +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/types.d.ts +10 -1
- package/front_end/third_party/puppeteer/package/package.json +3 -3
- package/front_end/third_party/puppeteer/package/src/api/Page.ts +2 -3
- package/front_end/third_party/puppeteer/package/src/bidi/HTTPRequest.ts +13 -0
- package/front_end/third_party/puppeteer/package/src/bidi/HTTPResponse.ts +10 -0
- package/front_end/third_party/puppeteer/package/src/bidi/core/Request.ts +15 -0
- package/front_end/third_party/puppeteer/package/src/cdp/Browser.ts +9 -4
- package/front_end/third_party/puppeteer/package/src/generated/injected.ts +1 -1
- package/front_end/third_party/puppeteer/package/src/revisions.ts +3 -3
- package/front_end/third_party/puppeteer/package/src/util/version.ts +1 -1
- package/front_end/ui/components/adorners/Adorner.ts +8 -68
- package/front_end/ui/components/text_editor/AiCodeGenerationProvider.ts +70 -28
- package/front_end/ui/legacy/SearchableView.ts +11 -5
- package/front_end/ui/legacy/SplitWidget.ts +1 -1
- package/front_end/ui/legacy/TabbedPane.ts +1 -1
- package/front_end/ui/legacy/components/perf_ui/FlameChart.ts +43 -9
- package/front_end/ui/visual_logging/KnownContextValues.ts +16 -0
- package/package.json +2 -1
|
@@ -646,7 +646,7 @@ const GREENDEV_VIEW: View = (input, _output, target) => {
|
|
|
646
646
|
<span>${i18nString(UIStrings.greenDevUnstable)}</span>
|
|
647
647
|
</div>
|
|
648
648
|
<div class="settings-experiments-block">
|
|
649
|
-
${renderPrototypeCheckboxes(input.settings, ['aiAnnotations', 'inDevToolsFloaty'])}
|
|
649
|
+
${renderPrototypeCheckboxes(input.settings, ['aiAnnotations', 'inDevToolsFloaty', 'copyToGemini'])}
|
|
650
650
|
</div>
|
|
651
651
|
</devtools-card>
|
|
652
652
|
|
|
@@ -668,7 +668,8 @@ const GREENDEV_PROTOTYPE_NAMES: Record<keyof GreenDev.GreenDevSettings, string>
|
|
|
668
668
|
inDevToolsFloaty: 'In DevTools context picker',
|
|
669
669
|
aiAnnotations: 'AI auto-annotations',
|
|
670
670
|
inlineWidgets: 'Inline widgets in AI Assistance',
|
|
671
|
-
artifactViewer: 'Widgets in the Artifact viewer'
|
|
671
|
+
artifactViewer: 'Widgets in the Artifact viewer',
|
|
672
|
+
copyToGemini: 'Copy changes to AI Prompt'
|
|
672
673
|
};
|
|
673
674
|
|
|
674
675
|
function renderWidgetOptions(settings: GreenDev.GreenDevSettings): TemplateResult {
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
/* eslint-disable @devtools/no-imperative-dom-api */
|
|
5
5
|
|
|
6
6
|
import * as Common from '../../core/common/common.js';
|
|
7
|
+
import * as Host from '../../core/host/host.js';
|
|
7
8
|
import * as Platform from '../../core/platform/platform.js';
|
|
8
9
|
import * as Root from '../../core/root/root.js';
|
|
9
10
|
import * as Trace from '../../models/trace/trace.js';
|
|
@@ -409,6 +410,7 @@ export class CompatibilityTracksAppender {
|
|
|
409
410
|
if (trackStartLevel === null || trackEndLevel === null) {
|
|
410
411
|
throw new Error(`Could not find events for track: ${trackAppender}`);
|
|
411
412
|
}
|
|
413
|
+
|
|
412
414
|
const entryLevels = this.#flameChartData.entryLevels;
|
|
413
415
|
const events = [];
|
|
414
416
|
for (let i = 0; i < entryLevels.length; i++) {
|
|
@@ -416,7 +418,13 @@ export class CompatibilityTracksAppender {
|
|
|
416
418
|
events.push(this.#entryData[i]);
|
|
417
419
|
}
|
|
418
420
|
}
|
|
419
|
-
|
|
421
|
+
|
|
422
|
+
// TODO(crbug.com/457866795): callers expect this to be sorted, but #entryData
|
|
423
|
+
// currently isn't guaranteed to be sorted because of appendEventsAtLevel and
|
|
424
|
+
// appendEventAtLevel. Also, see
|
|
425
|
+
// TimelineFlameChartDataProvider#insertEventToEntryData. This method is cached
|
|
426
|
+
// in eventsForTreeView, so it doesn't impact performance much.
|
|
427
|
+
events.sort((a, b) => a.ts - b.ts);
|
|
420
428
|
|
|
421
429
|
this.#eventsForTrack.set(trackAppender, events);
|
|
422
430
|
return events;
|
|
@@ -541,6 +549,11 @@ export class CompatibilityTracksAppender {
|
|
|
541
549
|
appendEventsAtLevel<T extends Trace.Types.Events.Event>(
|
|
542
550
|
events: readonly T[], trackStartLevel: number, appender: TrackAppender,
|
|
543
551
|
eventAppendedCallback?: (event: T, index: number) => void): number {
|
|
552
|
+
// Usage of getEventLevel below requires `events` to be sorted.
|
|
553
|
+
if (Host.InspectorFrontendHost.isUnderTest()) {
|
|
554
|
+
Platform.ArrayUtilities.assertArrayIsSorted(events, (a, b) => a.ts - b.ts);
|
|
555
|
+
}
|
|
556
|
+
|
|
544
557
|
const lastTimestampByLevel: LastTimestampByLevel = [];
|
|
545
558
|
for (let i = 0; i < events.length; ++i) {
|
|
546
559
|
const event = events[i];
|
|
@@ -145,14 +145,15 @@ export class StatusDialog extends UI.Widget.VBox {
|
|
|
145
145
|
}
|
|
146
146
|
|
|
147
147
|
remove(): void {
|
|
148
|
-
(this.element.parentNode as HTMLElement)?.classList.remove('tinted');
|
|
148
|
+
(this.element.parentNode as HTMLElement)?.classList.remove('opaque', 'tinted');
|
|
149
149
|
this.stopTimer();
|
|
150
150
|
this.element.remove();
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
-
showPane(parent: Element): void {
|
|
153
|
+
showPane(parent: Element, mode: 'tinted'|'opaque' = 'opaque'): void {
|
|
154
154
|
this.show(parent);
|
|
155
|
-
parent.classList.
|
|
155
|
+
parent.classList.toggle('tinted', mode === 'tinted');
|
|
156
|
+
parent.classList.toggle('opaque', mode === 'opaque');
|
|
156
157
|
}
|
|
157
158
|
|
|
158
159
|
enableAndFocusButton(): void {
|
|
@@ -80,9 +80,6 @@ export class ThirdPartyTreeViewWidget extends TimelineTreeView.TimelineTreeView
|
|
|
80
80
|
});
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
-
// const events = this.#thirdPartySummaries.entityByEvent.keys();
|
|
84
|
-
const relatedEvents = this.selectedEvents().sort(Trace.Helpers.Trace.eventTimeComparator);
|
|
85
|
-
|
|
86
83
|
// The filters for this view are slightly different; we want to use the set
|
|
87
84
|
// of visible event types, but also include network events, which by
|
|
88
85
|
// default are not in the set of visible entries (as they are not shown on
|
|
@@ -90,7 +87,7 @@ export class ThirdPartyTreeViewWidget extends TimelineTreeView.TimelineTreeView
|
|
|
90
87
|
const filter = new Trace.Extras.TraceFilter.VisibleEventsFilter(
|
|
91
88
|
Trace.Styles.visibleTypes().concat([Trace.Types.Events.Name.SYNTHETIC_NETWORK_REQUEST]));
|
|
92
89
|
|
|
93
|
-
const node = new Trace.Extras.TraceTree.BottomUpRootNode(
|
|
90
|
+
const node = new Trace.Extras.TraceTree.BottomUpRootNode(this.selectedEvents(), {
|
|
94
91
|
textFilter: this.textFilter(),
|
|
95
92
|
filters: [filter],
|
|
96
93
|
startTime: this.startTime,
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
// Use of this source code is governed by a BSD-style license that can be
|
|
3
3
|
// found in the LICENSE file.
|
|
4
4
|
|
|
5
|
+
import type * as Common from '../../core/common/common.js';
|
|
5
6
|
import * as i18n from '../../core/i18n/i18n.js';
|
|
7
|
+
import type * as Platform from '../../core/platform/platform.js';
|
|
6
8
|
import * as Root from '../../core/root/root.js';
|
|
7
9
|
import * as SDK from '../../core/sdk/sdk.js';
|
|
8
10
|
import type * as Protocol from '../../generated/protocol.js';
|
|
@@ -15,15 +17,82 @@ import * as Tracing from '../../services/tracing/tracing.js';
|
|
|
15
17
|
import * as RecordingMetadata from './RecordingMetadata.js';
|
|
16
18
|
|
|
17
19
|
const UIStrings = {
|
|
20
|
+
/**
|
|
21
|
+
* @description Text in Timeline Panel of the Performance panel
|
|
22
|
+
*/
|
|
23
|
+
initializingTracing: 'Initializing tracing…',
|
|
18
24
|
/**
|
|
19
25
|
* @description Text in Timeline Controller of the Performance panel indicating that the Performance Panel cannot
|
|
20
26
|
* record a performance trace because the type of target (where possible types are page, service worker and shared
|
|
21
27
|
* worker) doesn't support it.
|
|
22
28
|
*/
|
|
23
29
|
tracingNotSupported: 'Performance trace recording not supported for this type of target',
|
|
30
|
+
/**
|
|
31
|
+
* @description Text in a status dialog shown during a performance trace of a web page. It indicates to the user what the tracing is currently waiting on.
|
|
32
|
+
*/
|
|
33
|
+
waitingForLoadEvent: 'Waiting for load event…',
|
|
34
|
+
/**
|
|
35
|
+
* @description Text in a status dialog shown during a performance trace of a web page. It indicates to the user what the tracing is currently waiting on.
|
|
36
|
+
*/
|
|
37
|
+
waitingForLoadEventPlus5Seconds: 'Waiting for load event (+5s)…',
|
|
24
38
|
} as const;
|
|
25
39
|
const str_ = i18n.i18n.registerUIStrings('panels/timeline/TimelineController.ts', UIStrings);
|
|
26
40
|
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
|
41
|
+
|
|
42
|
+
type StatusUpdate = string|null;
|
|
43
|
+
type Listener = (status: StatusUpdate) => void;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Accepts promises with a text label, and reports to a listener as promises resolve.
|
|
47
|
+
* Only returns the label of the first incomplete promise. When no more promises
|
|
48
|
+
* remain, the updated status is null.
|
|
49
|
+
*/
|
|
50
|
+
class StatusChecker {
|
|
51
|
+
#checkers: Array<{title: string, complete: boolean}> = [];
|
|
52
|
+
#listener: Listener|null = null;
|
|
53
|
+
#currentStatus: StatusUpdate = null;
|
|
54
|
+
|
|
55
|
+
add(title: string, promise: Promise<unknown>): void {
|
|
56
|
+
const item = {title, complete: false};
|
|
57
|
+
this.#checkers.push(item);
|
|
58
|
+
|
|
59
|
+
void promise.finally(() => {
|
|
60
|
+
item.complete = true;
|
|
61
|
+
this.#evaluate();
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
setListener(listener: Listener): void {
|
|
66
|
+
this.#listener = null;
|
|
67
|
+
this.#evaluate();
|
|
68
|
+
this.#listener = listener;
|
|
69
|
+
listener(this.#currentStatus);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
removeListener(): void {
|
|
73
|
+
this.#listener = null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
#evaluate(): void {
|
|
77
|
+
let nextStatus: StatusUpdate = null;
|
|
78
|
+
|
|
79
|
+
// Only report the status of the first incomplete checker.
|
|
80
|
+
for (const checker of this.#checkers) {
|
|
81
|
+
if (!checker.complete) {
|
|
82
|
+
nextStatus = checker.title;
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (nextStatus !== this.#currentStatus) {
|
|
88
|
+
this.#currentStatus = nextStatus;
|
|
89
|
+
if (this.#listener) {
|
|
90
|
+
this.#listener(nextStatus);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
27
96
|
export class TimelineController implements Tracing.TracingManager.TracingManagerClient {
|
|
28
97
|
readonly primaryPageTarget: SDK.Target.Target;
|
|
29
98
|
readonly rootTarget: SDK.Target.Target;
|
|
@@ -35,6 +104,10 @@ export class TimelineController implements Tracing.TracingManager.TracingManager
|
|
|
35
104
|
private readonly client: Client;
|
|
36
105
|
private tracingCompletePromise: PromiseWithResolvers<void>|null = null;
|
|
37
106
|
|
|
107
|
+
// These properties are only used for "Reload and record".
|
|
108
|
+
#statusChecker: StatusChecker|null = null;
|
|
109
|
+
#loadEventFiredCb: (() => void)|null = null;
|
|
110
|
+
|
|
38
111
|
/**
|
|
39
112
|
* We always need to profile against the DevTools root target, which is
|
|
40
113
|
* the target that DevTools is attached to.
|
|
@@ -76,11 +149,72 @@ export class TimelineController implements Tracing.TracingManager.TracingManager
|
|
|
76
149
|
}
|
|
77
150
|
}
|
|
78
151
|
|
|
79
|
-
async
|
|
152
|
+
async #navigateToAboutBlank(): Promise<void> {
|
|
153
|
+
const aboutBlankNavigationComplete = new Promise<void>(async (resolve, reject) => {
|
|
154
|
+
const target = this.primaryPageTarget;
|
|
155
|
+
const resourceModel = target.model(SDK.ResourceTreeModel.ResourceTreeModel);
|
|
156
|
+
if (!resourceModel) {
|
|
157
|
+
reject('Could not load resourceModel');
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* To clear out the page and any state from prior test runs, we
|
|
163
|
+
* navigate to about:blank before initiating the trace recording.
|
|
164
|
+
* Once we have navigated to about:blank, we start recording and
|
|
165
|
+
* then navigate to the original page URL, to ensure we profile the
|
|
166
|
+
* page load.
|
|
167
|
+
**/
|
|
168
|
+
function waitForAboutBlank(event: Common.EventTarget.EventTargetEvent<SDK.ResourceTreeModel.ResourceTreeFrame>):
|
|
169
|
+
void {
|
|
170
|
+
if (event.data.url === 'about:blank') {
|
|
171
|
+
resolve();
|
|
172
|
+
} else {
|
|
173
|
+
reject(`Unexpected navigation to ${event.data.url}`);
|
|
174
|
+
}
|
|
175
|
+
resourceModel?.removeEventListener(SDK.ResourceTreeModel.Events.FrameNavigated, waitForAboutBlank);
|
|
176
|
+
}
|
|
177
|
+
resourceModel.addEventListener(SDK.ResourceTreeModel.Events.FrameNavigated, waitForAboutBlank);
|
|
178
|
+
await resourceModel.navigate('about:blank' as Platform.DevToolsPath.UrlString);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
await aboutBlankNavigationComplete;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
async #navigateWithSDK(url: Platform.DevToolsPath.UrlString): Promise<void> {
|
|
185
|
+
const resourceModel = this.primaryPageTarget.model(SDK.ResourceTreeModel.ResourceTreeModel);
|
|
186
|
+
if (!resourceModel) {
|
|
187
|
+
throw new Error('expected to find ResourceTreeModel');
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const loadPromiseWithResolvers = Promise.withResolvers<void>();
|
|
191
|
+
this.#loadEventFiredCb = loadPromiseWithResolvers.resolve;
|
|
192
|
+
SDK.TargetManager.TargetManager.instance().addModelListener(
|
|
193
|
+
SDK.ResourceTreeModel.ResourceTreeModel, SDK.ResourceTreeModel.Events.Load, this.#onLoadEventFired, this);
|
|
194
|
+
|
|
195
|
+
// We don't need to await this because we are purposefully showing UI
|
|
196
|
+
// progress as the page loads & tracing is underway.
|
|
197
|
+
void resourceModel.navigate(url);
|
|
198
|
+
|
|
199
|
+
await loadPromiseWithResolvers.promise;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
async startRecording(options: RecordingOptions): Promise<void> {
|
|
80
203
|
function disabledByDefault(category: string): string {
|
|
81
204
|
return 'disabled-by-default-' + category;
|
|
82
205
|
}
|
|
83
206
|
|
|
207
|
+
this.client.recordingStatus(i18nString(UIStrings.initializingTracing));
|
|
208
|
+
|
|
209
|
+
// If we are doing "Reload & record", we first navigate the page to
|
|
210
|
+
// about:blank. This is to ensure any data on the timeline from any
|
|
211
|
+
// previous performance recording is lost, avoiding the problem where a
|
|
212
|
+
// timeline will show data & screenshots from a previous page load that
|
|
213
|
+
// was not relevant.
|
|
214
|
+
if (options.navigateToUrl) {
|
|
215
|
+
await this.#navigateToAboutBlank();
|
|
216
|
+
}
|
|
217
|
+
|
|
84
218
|
// The following categories are also used in other tools, but this panel
|
|
85
219
|
// offers the possibility of turning them off (see below).
|
|
86
220
|
// 'disabled-by-default-devtools.screenshot'
|
|
@@ -144,11 +278,40 @@ export class TimelineController implements Tracing.TracingManager.TracingManager
|
|
|
144
278
|
this.#navigationUrls = [];
|
|
145
279
|
this.#fieldData = null;
|
|
146
280
|
this.#recordingStartTime = Date.now();
|
|
281
|
+
|
|
147
282
|
const response = await this.startRecordingWithCategories(categoriesArray.join(','));
|
|
148
283
|
if (response.getError()) {
|
|
149
284
|
await SDK.TargetManager.TargetManager.instance().resumeAllTargets();
|
|
285
|
+
throw new Error(response.getError());
|
|
150
286
|
}
|
|
151
|
-
|
|
287
|
+
|
|
288
|
+
if (!options.navigateToUrl) {
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// If the user hit "Reload & record", by this point we have:
|
|
293
|
+
// 1. Navigated to about:blank
|
|
294
|
+
// 2. Initiated tracing.
|
|
295
|
+
// We therefore now should navigate back to the original URL that the user wants to profile.
|
|
296
|
+
|
|
297
|
+
// Setup a status checker so we can wait long enough for the page to settle,
|
|
298
|
+
// and to let users know what is going on.
|
|
299
|
+
this.#statusChecker?.removeListener();
|
|
300
|
+
this.#statusChecker = new StatusChecker();
|
|
301
|
+
|
|
302
|
+
const loadEvent = this.#navigateWithSDK(options.navigateToUrl);
|
|
303
|
+
this.#statusChecker.add(i18nString(UIStrings.waitingForLoadEvent), loadEvent);
|
|
304
|
+
this.#statusChecker.add(
|
|
305
|
+
i18nString(UIStrings.waitingForLoadEventPlus5Seconds),
|
|
306
|
+
loadEvent.then(() => new Promise(resolve => setTimeout(resolve, 5000))));
|
|
307
|
+
|
|
308
|
+
this.#statusChecker.setListener(status => {
|
|
309
|
+
if (status === null) {
|
|
310
|
+
void this.stopRecording();
|
|
311
|
+
} else {
|
|
312
|
+
this.client.recordingStatus(status);
|
|
313
|
+
}
|
|
314
|
+
});
|
|
152
315
|
}
|
|
153
316
|
|
|
154
317
|
async #onFrameNavigated(event: {data: SDK.ResourceTreeModel.ResourceTreeFrame}): Promise<void> {
|
|
@@ -159,7 +322,22 @@ export class TimelineController implements Tracing.TracingManager.TracingManager
|
|
|
159
322
|
this.#navigationUrls.push(event.data.url);
|
|
160
323
|
}
|
|
161
324
|
|
|
325
|
+
async #onLoadEventFired(
|
|
326
|
+
event: Common.EventTarget
|
|
327
|
+
.EventTargetEvent<{resourceTreeModel: SDK.ResourceTreeModel.ResourceTreeModel, loadTime: number}>):
|
|
328
|
+
Promise<void> {
|
|
329
|
+
if (!event.data.resourceTreeModel.mainFrame?.isPrimaryFrame()) {
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
this.#loadEventFiredCb?.();
|
|
334
|
+
}
|
|
335
|
+
|
|
162
336
|
async stopRecording(): Promise<void> {
|
|
337
|
+
this.#statusChecker?.removeListener();
|
|
338
|
+
this.#statusChecker = null;
|
|
339
|
+
this.#loadEventFiredCb = null;
|
|
340
|
+
|
|
163
341
|
if (this.tracingManager) {
|
|
164
342
|
this.tracingManager.stop();
|
|
165
343
|
}
|
|
@@ -167,6 +345,8 @@ export class TimelineController implements Tracing.TracingManager.TracingManager
|
|
|
167
345
|
SDK.TargetManager.TargetManager.instance().removeModelListener(
|
|
168
346
|
SDK.ResourceTreeModel.ResourceTreeModel, SDK.ResourceTreeModel.Events.FrameNavigated, this.#onFrameNavigated,
|
|
169
347
|
this);
|
|
348
|
+
SDK.TargetManager.TargetManager.instance().removeModelListener(
|
|
349
|
+
SDK.ResourceTreeModel.ResourceTreeModel, SDK.ResourceTreeModel.Events.Load, this.#onLoadEventFired, this);
|
|
170
350
|
|
|
171
351
|
// When throttling is applied to the main renderer, it can slow down the
|
|
172
352
|
// collection of trace events once tracing has completed. Therefore we
|
|
@@ -286,7 +466,8 @@ export class TimelineController implements Tracing.TracingManager.TracingManager
|
|
|
286
466
|
}
|
|
287
467
|
|
|
288
468
|
export interface Client {
|
|
289
|
-
recordingProgress(
|
|
469
|
+
recordingProgress(bufferUsage: number): void;
|
|
470
|
+
recordingStatus(status: string): void;
|
|
290
471
|
loadingStarted(): void;
|
|
291
472
|
processingStarted(): void;
|
|
292
473
|
loadingProgress(progress?: number): void;
|
|
@@ -300,4 +481,5 @@ export interface RecordingOptions {
|
|
|
300
481
|
capturePictures?: boolean;
|
|
301
482
|
captureFilmStrip?: boolean;
|
|
302
483
|
captureSelectorStats?: boolean;
|
|
484
|
+
navigateToUrl?: Platform.DevToolsPath.UrlString;
|
|
303
485
|
}
|
|
@@ -453,6 +453,20 @@ export class TimelineFlameChartDataProvider extends Common.ObjectWrapper.ObjectW
|
|
|
453
453
|
return this.compatibilityTracksAppender;
|
|
454
454
|
}
|
|
455
455
|
|
|
456
|
+
#insertEventToEntryData(event: Trace.Types.Events.Event): number {
|
|
457
|
+
// TODO(crbug.com/457866795): We don't actually need to keep this sorted yet, because we sort
|
|
458
|
+
// in CompatibilityTracksAppender.eventsInTrack. But if we ever wanted to remove that sort,
|
|
459
|
+
// the following code will be needed.
|
|
460
|
+
|
|
461
|
+
// const index = Platform.ArrayUtilities.lowerBound(this.entryData, event, (a, b) => a.ts - b.ts);
|
|
462
|
+
// this.entryData.splice(index, 0, event);
|
|
463
|
+
// return index;
|
|
464
|
+
|
|
465
|
+
// For now, just keep it simple and slightly faster.
|
|
466
|
+
this.entryData.push(event);
|
|
467
|
+
return this.entryData.length - 1;
|
|
468
|
+
}
|
|
469
|
+
|
|
456
470
|
/**
|
|
457
471
|
* Returns the instance of the timeline flame chart data, without
|
|
458
472
|
* adding data to it. In case the timeline data hasn't been instanced
|
|
@@ -546,7 +560,7 @@ export class TimelineFlameChartDataProvider extends Common.ObjectWrapper.ObjectW
|
|
|
546
560
|
this.entryData = [];
|
|
547
561
|
this.entryTypeByLevel = [];
|
|
548
562
|
this.entryIndexToTitle = [];
|
|
549
|
-
this.#eventIndexByEvent = new
|
|
563
|
+
this.#eventIndexByEvent = new WeakMap();
|
|
550
564
|
|
|
551
565
|
if (this.#timelineData) {
|
|
552
566
|
this.compatibilityTracksAppender?.setFlameChartDataAndEntryData(
|
|
@@ -568,7 +582,7 @@ export class TimelineFlameChartDataProvider extends Common.ObjectWrapper.ObjectW
|
|
|
568
582
|
this.entryData = [];
|
|
569
583
|
this.entryTypeByLevel = [];
|
|
570
584
|
this.entryIndexToTitle = [];
|
|
571
|
-
this.#eventIndexByEvent = new
|
|
585
|
+
this.#eventIndexByEvent = new WeakMap();
|
|
572
586
|
this.#minimumBoundary = 0;
|
|
573
587
|
this.timeSpan = 0;
|
|
574
588
|
|
|
@@ -750,9 +764,14 @@ export class TimelineFlameChartDataProvider extends Common.ObjectWrapper.ObjectW
|
|
|
750
764
|
* because then when it comes to drawing we can decorate them differently.
|
|
751
765
|
**/
|
|
752
766
|
#appendFramesAndScreenshotsTrack(): void {
|
|
767
|
+
if (this.entryData.length) {
|
|
768
|
+
throw new Error('expected this.entryData to be empty');
|
|
769
|
+
}
|
|
770
|
+
|
|
753
771
|
if (!this.parsedTrace) {
|
|
754
772
|
return;
|
|
755
773
|
}
|
|
774
|
+
|
|
756
775
|
const filmStrip = Trace.Extras.FilmStrip.fromHandlerData(this.parsedTrace.data);
|
|
757
776
|
const hasScreenshots = filmStrip.frames.length > 0;
|
|
758
777
|
const hasFrames = this.parsedTrace.data.Frames.frames.length > 0;
|
|
@@ -782,27 +801,27 @@ export class TimelineFlameChartDataProvider extends Common.ObjectWrapper.ObjectW
|
|
|
782
801
|
if (!this.#timelineData || !this.parsedTrace) {
|
|
783
802
|
return;
|
|
784
803
|
}
|
|
804
|
+
|
|
785
805
|
this.appendHeader('', this.screenshotsGroupStyle, false /* selectable */);
|
|
786
806
|
this.entryTypeByLevel[this.currentLevel] = EntryType.SCREENSHOT;
|
|
787
|
-
let prevTimestamp: Trace.Types.Timing.Milli|undefined = undefined;
|
|
788
|
-
|
|
789
|
-
for (const filmStripFrame of filmStrip.frames) {
|
|
790
|
-
const screenshotTimeInMilliSeconds = Trace.Helpers.Timing.microToMilli(filmStripFrame.screenshotEvent.ts);
|
|
791
|
-
this.entryData.push(filmStripFrame.screenshotEvent);
|
|
792
|
-
(this.#timelineData.entryLevels as number[]).push(this.currentLevel);
|
|
793
|
-
(this.#timelineData.entryStartTimes as number[]).push(screenshotTimeInMilliSeconds);
|
|
794
|
-
if (prevTimestamp) {
|
|
795
|
-
(this.#timelineData.entryTotalTimes as number[]).push(screenshotTimeInMilliSeconds - prevTimestamp);
|
|
796
|
-
}
|
|
797
|
-
prevTimestamp = screenshotTimeInMilliSeconds;
|
|
798
|
-
}
|
|
799
|
-
if (filmStrip.frames.length && prevTimestamp !== undefined) {
|
|
800
|
-
const maxRecordTimeMillis =
|
|
801
|
-
Trace.Helpers.Timing.traceWindowMilliSeconds(this.parsedTrace.data.Meta.traceBounds).max;
|
|
802
807
|
|
|
803
|
-
|
|
804
|
-
|
|
808
|
+
const traceEnd = Trace.Helpers.Timing.traceWindowMilliSeconds(this.parsedTrace.data.Meta.traceBounds).max;
|
|
809
|
+
|
|
810
|
+
for (let i = 0; i < filmStrip.frames.length; ++i) {
|
|
811
|
+
const currentFrame = filmStrip.frames[i];
|
|
812
|
+
const nextFrame = filmStrip.frames[i + 1];
|
|
813
|
+
const startTimeMillis = Trace.Helpers.Timing.microToMilli(currentFrame.screenshotEvent.ts);
|
|
814
|
+
// If there is no next frame, use the end of the trace.
|
|
815
|
+
const endTimeMillis = nextFrame ? Trace.Helpers.Timing.microToMilli(nextFrame.screenshotEvent.ts) : traceEnd;
|
|
816
|
+
const durationMillis = endTimeMillis - startTimeMillis;
|
|
817
|
+
|
|
818
|
+
const index = this.#insertEventToEntryData(currentFrame.screenshotEvent);
|
|
819
|
+
(this.#timelineData.entryLevels as number[]).splice(index, 0, this.currentLevel);
|
|
820
|
+
(this.#timelineData.entryStartTimes as number[]).splice(index, 0, startTimeMillis);
|
|
821
|
+
(this.#timelineData.entryTotalTimes as number[]).splice(index, 0, durationMillis);
|
|
822
|
+
this.entryIndexToTitle.splice(index, 0, '');
|
|
805
823
|
}
|
|
824
|
+
|
|
806
825
|
++this.currentLevel;
|
|
807
826
|
}
|
|
808
827
|
|
|
@@ -1175,16 +1194,24 @@ export class TimelineFlameChartDataProvider extends Common.ObjectWrapper.ObjectW
|
|
|
1175
1194
|
}
|
|
1176
1195
|
|
|
1177
1196
|
#appendFrame(frame: Trace.Types.Events.LegacyTimelineFrame): void {
|
|
1178
|
-
const index = this
|
|
1179
|
-
this.entryData.push(frame);
|
|
1197
|
+
const index = this.#insertEventToEntryData(frame);
|
|
1180
1198
|
const durationMilliseconds = Trace.Helpers.Timing.microToMilli(frame.duration);
|
|
1181
|
-
this.entryIndexToTitle
|
|
1199
|
+
this.entryIndexToTitle.splice(index, 0, i18n.TimeUtilities.millisToString(durationMilliseconds, true));
|
|
1200
|
+
|
|
1182
1201
|
if (!this.#timelineData) {
|
|
1183
1202
|
return;
|
|
1184
1203
|
}
|
|
1185
|
-
|
|
1186
|
-
this.#timelineData.entryTotalTimes
|
|
1187
|
-
|
|
1204
|
+
|
|
1205
|
+
if (Array.isArray(this.#timelineData.entryLevels) && Array.isArray(this.#timelineData.entryTotalTimes) &&
|
|
1206
|
+
Array.isArray(this.#timelineData.entryStartTimes)) {
|
|
1207
|
+
this.#timelineData.entryLevels.splice(index, 0, this.currentLevel);
|
|
1208
|
+
this.#timelineData.entryTotalTimes.splice(index, 0, durationMilliseconds);
|
|
1209
|
+
this.#timelineData.entryStartTimes.splice(index, 0, Trace.Helpers.Timing.microToMilli(frame.startTime));
|
|
1210
|
+
} else {
|
|
1211
|
+
this.#timelineData.entryLevels[index] = this.currentLevel;
|
|
1212
|
+
this.#timelineData.entryTotalTimes[index] = durationMilliseconds;
|
|
1213
|
+
this.#timelineData.entryStartTimes[index] = Trace.Helpers.Timing.microToMilli(frame.startTime);
|
|
1214
|
+
}
|
|
1188
1215
|
}
|
|
1189
1216
|
|
|
1190
1217
|
createSelection(entryIndex: number): TimelineSelection|null {
|
|
@@ -423,28 +423,15 @@ export class TimelineFlameChartNetworkDataProvider implements PerfUI.FlameChart.
|
|
|
423
423
|
|
|
424
424
|
/**
|
|
425
425
|
* When users zoom in the flamechart, we only want to show them the network
|
|
426
|
-
* requests between startTime and endTime.
|
|
427
|
-
* trackAppender to update the timeline data, and then force to create a new
|
|
428
|
-
* PerfUI.FlameChart.FlameChartTimelineData instance to force the flamechart
|
|
429
|
-
* to re-render.
|
|
426
|
+
* requests between startTime and endTime.
|
|
430
427
|
*/
|
|
431
428
|
#updateTimelineData(startTime: Trace.Types.Timing.Milli, endTime: Trace.Types.Timing.Milli): void {
|
|
432
429
|
if (!this.#networkTrackAppender || !this.#timelineData) {
|
|
433
430
|
return;
|
|
434
431
|
}
|
|
432
|
+
// This also has the side-effect of updating this.#timelineData with new
|
|
433
|
+
// information.
|
|
435
434
|
this.#maxLevel = this.#networkTrackAppender.relayoutEntriesWithinBounds(this.#events, startTime, endTime);
|
|
436
|
-
|
|
437
|
-
// TODO(crbug.com/1459225): Remove this recreating code.
|
|
438
|
-
// Force to create a new PerfUI.FlameChart.FlameChartTimelineData instance
|
|
439
|
-
// to force the flamechart to re-render. This also causes crbug.com/1459225.
|
|
440
|
-
this.#timelineData = PerfUI.FlameChart.FlameChartTimelineData.create({
|
|
441
|
-
entryLevels: this.#timelineData?.entryLevels,
|
|
442
|
-
entryTotalTimes: this.#timelineData?.entryTotalTimes,
|
|
443
|
-
entryStartTimes: this.#timelineData?.entryStartTimes,
|
|
444
|
-
groups: this.#timelineData?.groups,
|
|
445
|
-
initiatorsData: this.#timelineData.initiatorsData,
|
|
446
|
-
entryDecorations: this.#timelineData.entryDecorations,
|
|
447
|
-
});
|
|
448
435
|
}
|
|
449
436
|
|
|
450
437
|
/**
|
|
@@ -66,10 +66,12 @@ const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
|
|
66
66
|
*/
|
|
67
67
|
export const SORT_ORDER_PAGE_LOAD_MARKERS: Readonly<Record<string, number>> = {
|
|
68
68
|
[Trace.Types.Events.Name.NAVIGATION_START]: 0,
|
|
69
|
-
[Trace.Types.Events.Name.
|
|
70
|
-
[Trace.Types.Events.Name.
|
|
71
|
-
[Trace.Types.Events.Name.
|
|
72
|
-
[Trace.Types.Events.Name.
|
|
69
|
+
[Trace.Types.Events.Name.SOFT_NAVIGATION_START]: 1,
|
|
70
|
+
[Trace.Types.Events.Name.MARK_LOAD]: 2,
|
|
71
|
+
[Trace.Types.Events.Name.MARK_FCP]: 3,
|
|
72
|
+
[Trace.Types.Events.Name.MARK_DOM_CONTENT]: 4,
|
|
73
|
+
[Trace.Types.Events.Name.MARK_LCP_CANDIDATE]: 5,
|
|
74
|
+
[Trace.Types.Events.Name.MARK_LCP_CANDIDATE_FOR_SOFT_NAVIGATION]: 6,
|
|
73
75
|
};
|
|
74
76
|
|
|
75
77
|
// Threshold to match up overlay markers that are off by a tiny amount so they aren't rendered
|
|
@@ -123,6 +125,7 @@ export class TimelineFlameChartView extends Common.ObjectWrapper.eventMixin<Even
|
|
|
123
125
|
private readonly onMainEntrySelected: (event: Common.EventTarget.EventTargetEvent<number>) => void;
|
|
124
126
|
private readonly onNetworkEntrySelected: (event: Common.EventTarget.EventTargetEvent<number>) => void;
|
|
125
127
|
readonly #boundRefreshAfterIgnoreList: () => void;
|
|
128
|
+
/** This is sorted by ts. */
|
|
126
129
|
#selectedEvents: Trace.Types.Events.Event[]|null;
|
|
127
130
|
// TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration
|
|
128
131
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -648,6 +651,7 @@ export class TimelineFlameChartView extends Common.ObjectWrapper.eventMixin<Even
|
|
|
648
651
|
fieldMetricResult = fieldMetricResults.fcp;
|
|
649
652
|
} else if (event.name === Trace.Types.Events.Name.MARK_LCP_CANDIDATE) {
|
|
650
653
|
fieldMetricResult = fieldMetricResults.lcp;
|
|
654
|
+
// Ignoring soft-nav LCP on purpose.
|
|
651
655
|
}
|
|
652
656
|
|
|
653
657
|
if (!fieldMetricResult) {
|
|
@@ -669,7 +673,9 @@ export class TimelineFlameChartView extends Common.ObjectWrapper.eventMixin<Even
|
|
|
669
673
|
// Set markers for Navigations, LCP, FCP, DCL, L.
|
|
670
674
|
const markers = markerEvents.filter(
|
|
671
675
|
event => event.name === Trace.Types.Events.Name.NAVIGATION_START ||
|
|
676
|
+
event.name === Trace.Types.Events.Name.SOFT_NAVIGATION_START ||
|
|
672
677
|
event.name === Trace.Types.Events.Name.MARK_LCP_CANDIDATE ||
|
|
678
|
+
event.name === Trace.Types.Events.Name.MARK_LCP_CANDIDATE_FOR_SOFT_NAVIGATION ||
|
|
673
679
|
event.name === Trace.Types.Events.Name.MARK_FCP ||
|
|
674
680
|
event.name === Trace.Types.Events.Name.MARK_DOM_CONTENT ||
|
|
675
681
|
event.name === Trace.Types.Events.Name.MARK_LOAD);
|
|
@@ -681,6 +687,7 @@ export class TimelineFlameChartView extends Common.ObjectWrapper.eventMixin<Even
|
|
|
681
687
|
marker,
|
|
682
688
|
parsedTrace.data.Meta.traceBounds,
|
|
683
689
|
parsedTrace.data.Meta.navigationsByNavigationId,
|
|
690
|
+
parsedTrace.data.Meta.softNavigationsById,
|
|
684
691
|
parsedTrace.data.Meta.navigationsByFrameId,
|
|
685
692
|
);
|
|
686
693
|
// If any of the markers overlap in timing, lets put them on the same marker.
|
|
@@ -732,24 +739,11 @@ export class TimelineFlameChartView extends Common.ObjectWrapper.eventMixin<Even
|
|
|
732
739
|
entries.push(...Overlays.Overlays.entriesForOverlay(overlay));
|
|
733
740
|
}
|
|
734
741
|
|
|
735
|
-
// The insight's `relatedEvents` property likely already includes the events associated with
|
|
736
|
-
// an overlay, but just in case not, include both arrays. Duplicates are fine.
|
|
737
|
-
let relatedEventsList = this.#activeInsight?.model.relatedEvents;
|
|
738
|
-
if (!relatedEventsList) {
|
|
739
|
-
relatedEventsList = [];
|
|
740
|
-
} else if (relatedEventsList instanceof Map) {
|
|
741
|
-
relatedEventsList = Array.from(relatedEventsList.keys());
|
|
742
|
-
}
|
|
743
|
-
this.#dimInsightRelatedEvents([...entries, ...relatedEventsList]);
|
|
744
|
-
|
|
745
742
|
if (options.updateTraceWindow) {
|
|
746
|
-
// We should only expand the entry track when we are updating the trace window
|
|
747
|
-
// (eg. when insight cards are initially opened).
|
|
743
|
+
// We should only expand the entry track when we are updating the trace window (eg. when insight cards are initially opened).
|
|
748
744
|
// Otherwise the track will open when not intending to.
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
this.#expandEntryTrack(entry);
|
|
752
|
-
}
|
|
745
|
+
this.#bulkExpandGroupsForEntries(entries);
|
|
746
|
+
|
|
753
747
|
const overlaysBounds = Overlays.Overlays.traceWindowContainingOverlays(this.#currentInsightOverlays);
|
|
754
748
|
if (overlaysBounds) {
|
|
755
749
|
// Trace window covering all overlays expanded by 50% so that the overlays cover 2/3 (100/150) of the visible window. (Or use provided override)
|
|
@@ -767,11 +761,26 @@ export class TimelineFlameChartView extends Common.ObjectWrapper.eventMixin<Even
|
|
|
767
761
|
}
|
|
768
762
|
}
|
|
769
763
|
|
|
764
|
+
// The insight's `relatedEvents` property likely already includes the events associated with
|
|
765
|
+
// an overlay, but just in case not, include both arrays. Duplicates are fine.
|
|
766
|
+
let relatedEventsList = this.#activeInsight?.model.relatedEvents;
|
|
767
|
+
if (!relatedEventsList) {
|
|
768
|
+
relatedEventsList = [];
|
|
769
|
+
} else if (relatedEventsList instanceof Map) {
|
|
770
|
+
relatedEventsList = Array.from(relatedEventsList.keys());
|
|
771
|
+
}
|
|
772
|
+
this.#dimInsightRelatedEvents([...entries, ...relatedEventsList]);
|
|
773
|
+
|
|
770
774
|
// Reveal entry if we have one.
|
|
775
|
+
// This is wrapped in a rAF to make sure the FlameChart draw from the
|
|
776
|
+
// expansion of any groups is complete - we need all the update() handlers
|
|
777
|
+
// to have run so the FlameChart has been drawn correctly at the right height.
|
|
771
778
|
if (entries.length !== 0) {
|
|
772
779
|
const earliestEntry =
|
|
773
780
|
entries.reduce((earliest, current) => (earliest.ts < current.ts ? earliest : current), entries[0]);
|
|
774
|
-
|
|
781
|
+
requestAnimationFrame(() => {
|
|
782
|
+
this.revealEventVertically(earliestEntry);
|
|
783
|
+
});
|
|
775
784
|
}
|
|
776
785
|
}
|
|
777
786
|
|
|
@@ -823,6 +832,41 @@ export class TimelineFlameChartView extends Common.ObjectWrapper.eventMixin<Even
|
|
|
823
832
|
}
|
|
824
833
|
}
|
|
825
834
|
|
|
835
|
+
/**
|
|
836
|
+
* Bulk expands the tracks (e.g. groups) that the given entries belong to.
|
|
837
|
+
* Will update them all at once and then do a redraw.
|
|
838
|
+
*/
|
|
839
|
+
#bulkExpandGroupsForEntries(entries: Trace.Types.Events.Event[]): void {
|
|
840
|
+
const networkGroupIndexes = new Set<number>();
|
|
841
|
+
const mainGroupIndexes = new Set<number>();
|
|
842
|
+
|
|
843
|
+
for (const entry of entries) {
|
|
844
|
+
const chartName = Overlays.Overlays.chartForEntry(entry);
|
|
845
|
+
const provider = chartName === 'main' ? this.mainDataProvider : this.networkDataProvider;
|
|
846
|
+
const entryIndex = provider.indexForEvent?.(entry) ?? null;
|
|
847
|
+
if (entryIndex === null) {
|
|
848
|
+
continue;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
const group = provider.groupForEvent?.(entryIndex) ?? null;
|
|
852
|
+
if (!group) {
|
|
853
|
+
continue;
|
|
854
|
+
}
|
|
855
|
+
if (group.expanded) {
|
|
856
|
+
continue;
|
|
857
|
+
}
|
|
858
|
+
const groupIndex = provider.timelineData().groups.indexOf(group);
|
|
859
|
+
if (chartName === 'main') {
|
|
860
|
+
mainGroupIndexes.add(groupIndex);
|
|
861
|
+
} else {
|
|
862
|
+
networkGroupIndexes.add(groupIndex);
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
this.mainFlameChart.bulkExpandGroups([...mainGroupIndexes]);
|
|
867
|
+
this.networkFlameChart.bulkExpandGroups([...networkGroupIndexes]);
|
|
868
|
+
}
|
|
869
|
+
|
|
826
870
|
/**
|
|
827
871
|
* Expands the track / group that the given entry is in.
|
|
828
872
|
*/
|