chrome-devtools-frontend 1.0.1595925 → 1.0.1596535
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/.stylelintrc.json +3 -1
- package/docs/ui_engineering.md +0 -36
- package/front_end/core/common/Console.ts +6 -6
- package/front_end/core/common/Settings.ts +12 -8
- package/front_end/core/host/UserMetrics.ts +0 -1
- package/front_end/core/root/DevToolsContext.ts +13 -7
- package/front_end/core/root/ExperimentNames.ts +0 -1
- package/front_end/core/sdk/CPUThrottlingManager.ts +12 -9
- package/front_end/core/sdk/DOMModel.ts +206 -0
- package/front_end/core/sdk/FrameManager.ts +7 -8
- package/front_end/core/sdk/SourceMapScopesInfo.ts +1 -1
- package/front_end/entrypoints/greendev_floaty/FloatyEntrypoint.ts +1 -1
- package/front_end/entrypoints/greendev_floaty/floaty.css +2 -2
- package/front_end/entrypoints/main/MainImpl.ts +1 -7
- package/front_end/foundation/Universe.ts +22 -11
- package/front_end/generated/InspectorBackendCommands.ts +7 -0
- package/front_end/generated/protocol-mapping.d.ts +16 -0
- package/front_end/generated/protocol-proxy-api.d.ts +25 -0
- package/front_end/generated/protocol.ts +71 -0
- package/front_end/models/ai_assistance/AiConversation.ts +6 -95
- package/front_end/models/ai_assistance/ConversationHandler.ts +1 -1
- package/front_end/models/ai_assistance/agents/AiAgent.ts +11 -1
- package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +14 -0
- package/front_end/models/ai_code_completion/AiCodeCompletion.ts +1 -1
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +3 -0
- package/front_end/panels/ai_assistance/components/ChatMessage.ts +73 -35
- package/front_end/panels/ai_assistance/components/WalkthroughView.ts +30 -6
- package/front_end/panels/ai_assistance/components/chatMessage.css +4 -2
- package/front_end/panels/application/IndexedDBModel.ts +2 -4
- package/front_end/panels/application/ServiceWorkersView.ts +3 -11
- package/front_end/panels/developer_resources/DeveloperResourcesListView.ts +1 -1
- package/front_end/panels/elements/ComputedStyleWidget.ts +25 -16
- package/front_end/panels/elements/ElementsPanel.ts +0 -1
- package/front_end/panels/elements/StandaloneStylesContainer.ts +48 -12
- package/front_end/panels/elements/StylePropertiesSection.ts +97 -1
- package/front_end/panels/elements/StylePropertyTreeElement.ts +27 -5
- package/front_end/panels/elements/StylesContainer.ts +1 -0
- package/front_end/panels/elements/StylesSidebarPane.ts +48 -32
- package/front_end/panels/elements/nodeStackTraceWidget.css +1 -1
- package/front_end/panels/elements/stylePropertiesTreeOutline.css +1 -1
- package/front_end/panels/emulation/DeviceModeToolbar.ts +171 -89
- package/front_end/panels/greendev/GreenDevPanel.css +2 -2
- package/front_end/panels/issues/AffectedPermissionElementsView.ts +9 -6
- package/front_end/panels/lighthouse/LighthouseController.ts +13 -5
- package/front_end/panels/lighthouse/LighthousePanel.ts +22 -5
- package/front_end/panels/profiler/HeapSnapshotGridNodes.ts +11 -3
- package/front_end/panels/recorder/components/StepView.ts +0 -2
- package/front_end/panels/recorder/components/stepView.css +13 -13
- package/front_end/panels/recorder/components/timelineSection.css +6 -7
- package/front_end/panels/settings/components/SyncSection.ts +4 -13
- package/front_end/panels/timeline/CompatibilityTracksAppender.ts +2 -15
- package/front_end/panels/timeline/RecordingMetadata.ts +1 -1
- package/front_end/panels/timeline/TimelineDetailsView.ts +2 -2
- package/front_end/panels/timeline/TimelinePanel.ts +18 -0
- package/front_end/panels/timeline/TimelineUIUtils.ts +7 -4
- package/front_end/panels/timeline/components/CWVMetrics.ts +339 -0
- package/front_end/panels/timeline/components/Sidebar.ts +17 -0
- package/front_end/panels/timeline/components/SidebarInsightsTab.ts +10 -0
- package/front_end/panels/timeline/components/SidebarSingleInsightSet.ts +25 -267
- package/front_end/panels/timeline/components/Utils.ts +2 -2
- package/front_end/panels/timeline/components/components.ts +2 -0
- package/front_end/panels/timeline/components/cwvMetrics.css +107 -0
- package/front_end/panels/timeline/components/sidebarSingleInsightSet.css +0 -102
- package/front_end/panels/timeline/timeline-meta.ts +11 -0
- package/front_end/panels/timeline/utils/Helpers.ts +5 -1
- package/front_end/panels/webauthn/webauthnPane.css +1 -1
- package/front_end/third_party/chromium/README.chromium +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 +7 -0
- 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/Page.d.ts +1 -0
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Page.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Page.js +3 -0
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/Page.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Browser.d.ts +1 -0
- 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 +6 -0
- 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/cdp/Page.d.ts +1 -0
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.js +5 -0
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/Page.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 +2 -2
- package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js +2 -2
- 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 +7 -0
- package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +14 -3
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/api/Page.d.ts +7 -0
- 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/Page.d.ts +1 -0
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Page.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Page.js +3 -0
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/bidi/Page.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Browser.d.ts +1 -0
- 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 +6 -0
- 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/cdp/Page.d.ts +1 -0
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.js +5 -0
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/Page.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.d.ts +2 -2
- package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js +2 -2
- 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 +7 -0
- package/front_end/third_party/puppeteer/package/package.json +1 -1
- package/front_end/third_party/puppeteer/package/src/api/Page.ts +8 -0
- package/front_end/third_party/puppeteer/package/src/bidi/Page.ts +4 -0
- package/front_end/third_party/puppeteer/package/src/cdp/Browser.ts +7 -0
- package/front_end/third_party/puppeteer/package/src/cdp/Page.ts +6 -0
- package/front_end/third_party/puppeteer/package/src/revisions.ts +2 -2
- package/front_end/third_party/puppeteer/package/src/util/version.ts +1 -1
- package/front_end/ui/components/text_editor/AutocompleteHistory.ts +89 -5
- package/front_end/ui/components/tree_outline/TreeOutline.ts +1 -1
- package/front_end/ui/legacy/ProgressIndicator.ts +1 -1
- package/front_end/ui/legacy/Toolbar.ts +1 -1
- package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +14 -7
- package/front_end/ui/legacy/legacy.ts +0 -2
- package/front_end/ui/visual_logging/KnownContextValues.ts +5 -0
- package/inspector_overlay/loadCSS.rollup.js +2 -2
- package/inspector_overlay/tool_source_order.css +1 -0
- package/package.json +1 -1
- package/front_end/ui/legacy/Fragment.ts +0 -234
|
@@ -3,69 +3,25 @@
|
|
|
3
3
|
// found in the LICENSE file.
|
|
4
4
|
|
|
5
5
|
import * as i18n from '../../../core/i18n/i18n.js';
|
|
6
|
-
import * as Platform from '../../../core/platform/platform.js';
|
|
7
6
|
import * as AIAssistance from '../../../models/ai_assistance/ai_assistance.js';
|
|
8
|
-
import * as CrUXManager from '../../../models/crux-manager/crux-manager.js';
|
|
9
7
|
import * as Trace from '../../../models/trace/trace.js';
|
|
10
|
-
import * as Buttons from '../../../ui/components/buttons/buttons.js';
|
|
11
8
|
import * as UI from '../../../ui/legacy/legacy.js';
|
|
12
9
|
import * as Lit from '../../../ui/lit/lit.js';
|
|
13
|
-
import * as VisualLogging from '../../../ui/visual_logging/visual_logging.js';
|
|
14
10
|
|
|
15
|
-
import {
|
|
11
|
+
import {CWVMetrics, getFieldMetrics} from './CWVMetrics.js';
|
|
12
|
+
import {shouldRenderForCategory} from './insights/Helpers.js';
|
|
16
13
|
import * as Insights from './insights/insights.js';
|
|
17
14
|
import type {ActiveInsight} from './Sidebar.js';
|
|
18
15
|
import sidebarSingleInsightSetStyles from './sidebarSingleInsightSet.css.js';
|
|
19
|
-
import {isFieldWorseThanLocal, NumberWithUnit} from './Utils.js';
|
|
20
16
|
|
|
21
17
|
const {html} = Lit.StaticHtml;
|
|
22
18
|
|
|
23
19
|
const UIStrings = {
|
|
24
|
-
/**
|
|
25
|
-
* @description title used for a metric value to tell the user about its score classification
|
|
26
|
-
* @example {INP} PH1
|
|
27
|
-
* @example {1.2s} PH2
|
|
28
|
-
* @example {poor} PH3
|
|
29
|
-
*/
|
|
30
|
-
metricScore: '{PH1}: {PH2} {PH3} score',
|
|
31
|
-
/**
|
|
32
|
-
* @description title used for a metric value to tell the user that the data is unavailable
|
|
33
|
-
* @example {INP} PH1
|
|
34
|
-
*/
|
|
35
|
-
metricScoreUnavailable: '{PH1}: unavailable',
|
|
36
20
|
/**
|
|
37
21
|
* @description Summary text for an expandable dropdown that contains all insights in a passing state.
|
|
38
22
|
* @example {4} PH1
|
|
39
23
|
*/
|
|
40
24
|
passedInsights: 'Passed insights ({PH1})',
|
|
41
|
-
/**
|
|
42
|
-
* @description Label denoting that metrics were observed in the field, from real use data (CrUX). Also denotes if from URL or Origin dataset.
|
|
43
|
-
* @example {URL} PH1
|
|
44
|
-
*/
|
|
45
|
-
fieldScoreLabel: 'Field ({PH1})',
|
|
46
|
-
/**
|
|
47
|
-
* @description Label for an option that selects the page's specific URL as opposed to it's entire origin/domain.
|
|
48
|
-
*/
|
|
49
|
-
urlOption: 'URL',
|
|
50
|
-
/**
|
|
51
|
-
* @description Label for an option that selects the page's entire origin/domain as opposed to it's specific URL.
|
|
52
|
-
*/
|
|
53
|
-
originOption: 'Origin',
|
|
54
|
-
/**
|
|
55
|
-
* @description Title for button that closes a warning popup.
|
|
56
|
-
*/
|
|
57
|
-
dismissTitle: 'Dismiss',
|
|
58
|
-
/**
|
|
59
|
-
* @description Title shown in a warning dialog when field metrics (collected from real users) is worse than the locally observed metrics.
|
|
60
|
-
*/
|
|
61
|
-
fieldMismatchTitle: 'Field & local metrics mismatch',
|
|
62
|
-
/**
|
|
63
|
-
* @description Text shown in a warning dialog when field metrics (collected from real users) is worse than the locally observed metrics.
|
|
64
|
-
* Asks user to use features such as throttling and device emulation.
|
|
65
|
-
*/
|
|
66
|
-
fieldMismatchNotice:
|
|
67
|
-
'There are many reasons why local and field metrics [may not match](https://web.dev/articles/lab-and-field-data-differences). ' +
|
|
68
|
-
'Adjust [throttling settings and device emulation](https://developer.chrome.com/docs/devtools/device-mode) to analyze traces more similar to the average user\'s environment.',
|
|
69
25
|
} as const;
|
|
70
26
|
|
|
71
27
|
const str_ = i18n.i18n.registerUIStrings('panels/timeline/components/SidebarSingleInsightSet.ts', UIStrings);
|
|
@@ -83,166 +39,36 @@ interface InsightData {
|
|
|
83
39
|
model: Trace.Insights.Types.InsightModel;
|
|
84
40
|
}
|
|
85
41
|
|
|
86
|
-
interface LocalMetrics {
|
|
87
|
-
lcp: {value: Trace.Types.Timing.Micro, event: Trace.Types.Events.AnyLargestContentfulPaintCandidate}|null;
|
|
88
|
-
cls: {value: number, worstClusterEvent: Trace.Types.Events.Event|null};
|
|
89
|
-
inp: {value: Trace.Types.Timing.Micro, event: Trace.Types.Events.SyntheticInteractionPair}|null;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
42
|
interface ViewInput {
|
|
93
43
|
shownInsights: InsightData[];
|
|
94
44
|
passedInsights: InsightData[];
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
activeCategory: Trace.Insights.Types.InsightCategory;
|
|
98
|
-
showFieldMismatchNotice: boolean;
|
|
99
|
-
onDismisFieldMismatchNotice: () => void;
|
|
100
|
-
onClickMetric: (traceEvent: Trace.Types.Events.Event) => void;
|
|
45
|
+
insightSetKey: Trace.Types.Events.NavigationId|null;
|
|
46
|
+
parsedTrace: Trace.TraceModel.ParsedTrace|null;
|
|
101
47
|
renderInsightComponent: (insightData: InsightData) => Lit.LitTemplate;
|
|
102
48
|
}
|
|
103
49
|
|
|
104
50
|
type View = (input: ViewInput, output: undefined, target: HTMLElement) => void;
|
|
105
51
|
|
|
106
|
-
|
|
52
|
+
const DEFAULT_VIEW: View = (input, output, target) => {
|
|
107
53
|
const {
|
|
108
54
|
shownInsights,
|
|
109
55
|
passedInsights,
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
activeCategory,
|
|
113
|
-
showFieldMismatchNotice,
|
|
114
|
-
onDismisFieldMismatchNotice,
|
|
115
|
-
onClickMetric,
|
|
56
|
+
insightSetKey,
|
|
57
|
+
parsedTrace,
|
|
116
58
|
renderInsightComponent,
|
|
117
59
|
} = input;
|
|
118
60
|
|
|
119
|
-
function renderMetrics(): Lit.
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
const clsEl = renderMetricValue('CLS', local?.cls?.value ?? null, local?.cls?.worstClusterEvent ?? null);
|
|
123
|
-
|
|
124
|
-
const localMetricsTemplateResult = html`
|
|
125
|
-
<div class="metrics-row">
|
|
126
|
-
<span>${lcpEl}</span>
|
|
127
|
-
<span>${inpEl}</span>
|
|
128
|
-
<span>${clsEl}</span>
|
|
129
|
-
<span class="row-label">Local</span>
|
|
130
|
-
</div>
|
|
131
|
-
<span class="row-border"></span>
|
|
132
|
-
`;
|
|
133
|
-
|
|
134
|
-
let fieldMetricsTemplateResult;
|
|
135
|
-
if (field) {
|
|
136
|
-
const {lcp, inp, cls} = field;
|
|
137
|
-
|
|
138
|
-
const lcpEl = renderMetricValue('LCP', lcp?.value ?? null, null);
|
|
139
|
-
const inpEl = renderMetricValue('INP', inp?.value ?? null, null);
|
|
140
|
-
const clsEl = renderMetricValue('CLS', cls?.value ?? null, null);
|
|
141
|
-
|
|
142
|
-
let scope = i18nString(UIStrings.originOption);
|
|
143
|
-
if (lcp?.pageScope === 'url' || inp?.pageScope === 'url') {
|
|
144
|
-
scope = i18nString(UIStrings.urlOption);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
// clang-format off
|
|
148
|
-
fieldMetricsTemplateResult = html`
|
|
149
|
-
<div class="metrics-row">
|
|
150
|
-
<span>${lcpEl}</span>
|
|
151
|
-
<span>${inpEl}</span>
|
|
152
|
-
<span>${clsEl}</span>
|
|
153
|
-
<span class="row-label">${i18nString(UIStrings.fieldScoreLabel, {PH1: scope})}</span>
|
|
154
|
-
</div>
|
|
155
|
-
<span class="row-border"></span>
|
|
156
|
-
`;
|
|
157
|
-
// clang-format on
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
let fieldIsDifferentEl;
|
|
161
|
-
if (showFieldMismatchNotice) {
|
|
162
|
-
// clang-format off
|
|
163
|
-
fieldIsDifferentEl = html`
|
|
164
|
-
<div class="field-mismatch-notice" jslog=${VisualLogging.section('timeline.insights.field-mismatch')}>
|
|
165
|
-
<h3>${i18nString(UIStrings.fieldMismatchTitle)}</h3>
|
|
166
|
-
<devtools-button
|
|
167
|
-
title=${i18nString(UIStrings.dismissTitle)}
|
|
168
|
-
.iconName=${'cross'}
|
|
169
|
-
.variant=${Buttons.Button.Variant.ICON}
|
|
170
|
-
.jslogContext=${'timeline.insights.dismiss-field-mismatch'}
|
|
171
|
-
@click=${onDismisFieldMismatchNotice}
|
|
172
|
-
></devtools-button>
|
|
173
|
-
<div class="field-mismatch-notice__body">${md(i18nString(UIStrings.fieldMismatchNotice))}</div>
|
|
174
|
-
</div>
|
|
175
|
-
`;
|
|
176
|
-
// clang-format on
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
const classes = {metrics: true, 'metrics--field': Boolean(fieldMetricsTemplateResult)};
|
|
180
|
-
const metricsTableEl = html`<div class=${Lit.Directives.classMap(classes)}>
|
|
181
|
-
<div class="metrics-row">
|
|
182
|
-
<span class="metric-label">LCP</span>
|
|
183
|
-
<span class="metric-label">INP</span>
|
|
184
|
-
<span class="metric-label">CLS</span>
|
|
185
|
-
<span class="row-label"></span>
|
|
186
|
-
</div>
|
|
187
|
-
${localMetricsTemplateResult}
|
|
188
|
-
${fieldMetricsTemplateResult}
|
|
189
|
-
</div>`;
|
|
190
|
-
|
|
191
|
-
return html`
|
|
192
|
-
${metricsTableEl}
|
|
193
|
-
${fieldIsDifferentEl}
|
|
194
|
-
`;
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
function renderMetricValue(
|
|
198
|
-
metric: 'LCP'|'CLS'|'INP', value: number|null, relevantEvent: Trace.Types.Events.Event|null): Lit.LitTemplate {
|
|
199
|
-
let valueText: string;
|
|
200
|
-
let valueDisplay: HTMLElement|string;
|
|
201
|
-
let classification;
|
|
202
|
-
if (value === null) {
|
|
203
|
-
valueText = valueDisplay = '-';
|
|
204
|
-
classification = Trace.Handlers.ModelHandlers.PageLoadMetrics.ScoreClassification.UNCLASSIFIED;
|
|
205
|
-
} else if (metric === 'LCP') {
|
|
206
|
-
const micros = value as Trace.Types.Timing.Micro;
|
|
207
|
-
const {text, element} = NumberWithUnit.formatMicroSecondsAsSeconds(micros);
|
|
208
|
-
valueText = text;
|
|
209
|
-
valueDisplay = element;
|
|
210
|
-
classification =
|
|
211
|
-
Trace.Handlers.ModelHandlers.PageLoadMetrics.scoreClassificationForLargestContentfulPaint(micros);
|
|
212
|
-
} else if (metric === 'CLS') {
|
|
213
|
-
valueText = valueDisplay = value ? value.toFixed(2) : '0';
|
|
214
|
-
classification = Trace.Handlers.ModelHandlers.LayoutShifts.scoreClassificationForLayoutShift(value);
|
|
215
|
-
} else if (metric === 'INP') {
|
|
216
|
-
const micros = value as Trace.Types.Timing.Micro;
|
|
217
|
-
const {text, element} = NumberWithUnit.formatMicroSecondsAsMillisFixed(micros);
|
|
218
|
-
valueText = text;
|
|
219
|
-
valueDisplay = element;
|
|
220
|
-
classification =
|
|
221
|
-
Trace.Handlers.ModelHandlers.UserInteractions.scoreClassificationForInteractionToNextPaint(micros);
|
|
222
|
-
} else {
|
|
223
|
-
Platform.TypeScriptUtilities.assertNever(metric, `Unexpected metric ${metric}`);
|
|
61
|
+
function renderMetrics(): Lit.LitTemplate {
|
|
62
|
+
if (!insightSetKey || !parsedTrace) {
|
|
63
|
+
return Lit.nothing;
|
|
224
64
|
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
const title = value !== null ?
|
|
233
|
-
i18nString(UIStrings.metricScore, {PH1: metric, PH2: valueText, PH3: classification}) :
|
|
234
|
-
i18nString(UIStrings.metricScoreUnavailable, {PH1: metric});
|
|
235
|
-
|
|
236
|
-
return metricIsVisible(activeCategory, metric) ? html`
|
|
237
|
-
<button class="metric"
|
|
238
|
-
@click=${relevantEvent ? onClickMetric.bind(relevantEvent) : null}
|
|
239
|
-
title=${title}
|
|
240
|
-
aria-label=${title}
|
|
241
|
-
>
|
|
242
|
-
<div class="metric-value metric-value-${classification}">${valueDisplay}</div>
|
|
243
|
-
</button>
|
|
244
|
-
` : Lit.nothing;
|
|
245
|
-
// clang-format on
|
|
65
|
+
const metricsWidgetConfig = UI.Widget.widgetConfig(CWVMetrics, {
|
|
66
|
+
data: {
|
|
67
|
+
insightSetKey,
|
|
68
|
+
parsedTrace,
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
return html`<devtools-widget .widgetConfig=${metricsWidgetConfig}></devtools-widget>`;
|
|
246
72
|
}
|
|
247
73
|
|
|
248
74
|
function renderInsights(): Lit.LitTemplate {
|
|
@@ -275,14 +101,6 @@ export const DEFAULT_VIEW: View = (input, output, target) => {
|
|
|
275
101
|
// clang-format on
|
|
276
102
|
};
|
|
277
103
|
|
|
278
|
-
function metricIsVisible(activeCategory: Trace.Insights.Types.InsightCategory, label: 'LCP'|'CLS'|'INP'): boolean {
|
|
279
|
-
if (activeCategory === Trace.Insights.Types.InsightCategory.ALL) {
|
|
280
|
-
return true;
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
return label === activeCategory;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
104
|
export class SidebarSingleInsightSet extends UI.Widget.Widget {
|
|
287
105
|
#view: View;
|
|
288
106
|
#insightRenderer = new Insights.InsightRenderer.InsightRenderer();
|
|
@@ -296,8 +114,6 @@ export class SidebarSingleInsightSet extends UI.Widget.Widget {
|
|
|
296
114
|
parsedTrace: null,
|
|
297
115
|
};
|
|
298
116
|
|
|
299
|
-
#didDismissFieldMismatchNotice = false;
|
|
300
|
-
|
|
301
117
|
constructor(element?: HTMLElement, view: View = DEFAULT_VIEW) {
|
|
302
118
|
super(element, {useShadowDom: true});
|
|
303
119
|
this.#view = view;
|
|
@@ -330,57 +146,15 @@ export class SidebarSingleInsightSet extends UI.Widget.Widget {
|
|
|
330
146
|
});
|
|
331
147
|
}
|
|
332
148
|
|
|
333
|
-
#onClickMetric(traceEvent: Trace.Types.Events.Event): void {
|
|
334
|
-
this.element.dispatchEvent(new Insights.EventRef.EventReferenceClick(traceEvent));
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
#getLocalMetrics(insightSetKey: string): LocalMetrics|null {
|
|
338
|
-
if (!this.#data.parsedTrace) {
|
|
339
|
-
return null;
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
const insightSet = this.#data.parsedTrace.insights?.get(insightSetKey);
|
|
343
|
-
if (!insightSet) {
|
|
344
|
-
return null;
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
const lcp = Trace.Insights.Common.getLCP(insightSet);
|
|
348
|
-
const cls = Trace.Insights.Common.getCLS(insightSet);
|
|
349
|
-
const inp = Trace.Insights.Common.getINP(insightSet);
|
|
350
|
-
|
|
351
|
-
return {lcp, cls, inp};
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
#getFieldMetrics(insightSetKey: string): Trace.Insights.Common.CrUXFieldMetricResults|null {
|
|
355
|
-
if (!this.#data.parsedTrace) {
|
|
356
|
-
return null;
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
const insightSet = this.#data.parsedTrace.insights?.get(insightSetKey);
|
|
360
|
-
if (!insightSet) {
|
|
361
|
-
return null;
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
const fieldMetricsResults = Trace.Insights.Common.getFieldMetricsForInsightSet(
|
|
365
|
-
insightSet, this.#data.parsedTrace.metadata, CrUXManager.CrUXManager.instance().getSelectedScope());
|
|
366
|
-
if (!fieldMetricsResults) {
|
|
367
|
-
return null;
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
return fieldMetricsResults;
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
#onDismisFieldMismatchNotice(): void {
|
|
374
|
-
this.#didDismissFieldMismatchNotice = true;
|
|
375
|
-
this.requestUpdate();
|
|
376
|
-
}
|
|
377
|
-
|
|
378
149
|
static categorizeInsights(
|
|
379
150
|
insightSets: Trace.Insights.Types.TraceInsightSets|null,
|
|
380
151
|
insightSetKey: string,
|
|
381
152
|
activeCategory: Trace.Insights.Types.InsightCategory,
|
|
382
153
|
): {shownInsights: InsightData[], passedInsights: InsightData[]} {
|
|
383
|
-
|
|
154
|
+
if (!insightSets || !(insightSets instanceof Map)) {
|
|
155
|
+
return {shownInsights: [], passedInsights: []};
|
|
156
|
+
}
|
|
157
|
+
const insightSet = insightSets.get(insightSetKey);
|
|
384
158
|
if (!insightSet) {
|
|
385
159
|
return {shownInsights: [], passedInsights: []};
|
|
386
160
|
}
|
|
@@ -433,7 +207,7 @@ export class SidebarSingleInsightSet extends UI.Widget.Widget {
|
|
|
433
207
|
insightSetKey,
|
|
434
208
|
} = this.#data;
|
|
435
209
|
|
|
436
|
-
if (!parsedTrace?.insights || !insightSetKey) {
|
|
210
|
+
if (!parsedTrace?.insights || !insightSetKey || !(parsedTrace.insights instanceof Map)) {
|
|
437
211
|
return;
|
|
438
212
|
}
|
|
439
213
|
|
|
@@ -442,34 +216,18 @@ export class SidebarSingleInsightSet extends UI.Widget.Widget {
|
|
|
442
216
|
return;
|
|
443
217
|
}
|
|
444
218
|
|
|
445
|
-
const
|
|
446
|
-
const field = this.#getFieldMetrics(insightSetKey);
|
|
219
|
+
const field = getFieldMetrics(parsedTrace, insightSetKey);
|
|
447
220
|
const {shownInsights, passedInsights} = SidebarSingleInsightSet.categorizeInsights(
|
|
448
221
|
parsedTrace.insights,
|
|
449
222
|
insightSetKey,
|
|
450
223
|
this.#data.activeCategory,
|
|
451
224
|
);
|
|
452
225
|
|
|
453
|
-
const localValues = {
|
|
454
|
-
lcp: local?.lcp?.value !== undefined ? Trace.Helpers.Timing.microToMilli(local?.lcp.value) : undefined,
|
|
455
|
-
inp: local?.inp?.value !== undefined ? Trace.Helpers.Timing.microToMilli(local?.inp.value) : undefined,
|
|
456
|
-
};
|
|
457
|
-
const fieldValues = field && {
|
|
458
|
-
lcp: field.lcp?.value !== undefined ? Trace.Helpers.Timing.microToMilli(field.lcp.value) : undefined,
|
|
459
|
-
inp: field.inp?.value !== undefined ? Trace.Helpers.Timing.microToMilli(field.inp.value) : undefined,
|
|
460
|
-
};
|
|
461
|
-
const showFieldMismatchNotice =
|
|
462
|
-
!this.#didDismissFieldMismatchNotice && !!fieldValues && isFieldWorseThanLocal(localValues, fieldValues);
|
|
463
|
-
|
|
464
226
|
const input: ViewInput = {
|
|
465
227
|
shownInsights,
|
|
466
228
|
passedInsights,
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
activeCategory: this.#data.activeCategory,
|
|
470
|
-
showFieldMismatchNotice,
|
|
471
|
-
onDismisFieldMismatchNotice: this.#onDismisFieldMismatchNotice.bind(this),
|
|
472
|
-
onClickMetric: this.#onClickMetric.bind(this),
|
|
229
|
+
insightSetKey,
|
|
230
|
+
parsedTrace,
|
|
473
231
|
renderInsightComponent: insightData => this.#renderInsightComponent(insightSet, insightData, field),
|
|
474
232
|
};
|
|
475
233
|
this.#view(input, undefined, this.contentElement);
|
|
@@ -227,7 +227,7 @@ export namespace NumberWithUnit {
|
|
|
227
227
|
element.append(lastPart);
|
|
228
228
|
}
|
|
229
229
|
|
|
230
|
-
return {text: element.textContent
|
|
230
|
+
return {text: element.textContent, element};
|
|
231
231
|
}
|
|
232
232
|
|
|
233
233
|
export function formatMicroSecondsAsMillisFixed(time: Platform.Timing.MicroSeconds, fractionDigits = 0):
|
|
@@ -253,7 +253,7 @@ export namespace NumberWithUnit {
|
|
|
253
253
|
element.append(lastPart);
|
|
254
254
|
}
|
|
255
255
|
|
|
256
|
-
return {text: element.textContent
|
|
256
|
+
return {text: element.textContent, element};
|
|
257
257
|
}
|
|
258
258
|
}
|
|
259
259
|
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
import * as Breadcrumbs from './Breadcrumbs.js';
|
|
6
6
|
import * as BreadcrumbsUI from './BreadcrumbsUI.js';
|
|
7
7
|
import * as CPUThrottlingSelector from './CPUThrottlingSelector.js';
|
|
8
|
+
import * as CWVMetrics from './CWVMetrics.js';
|
|
8
9
|
import * as DetailsView from './DetailsView.js';
|
|
9
10
|
import * as ExportTraceOptions from './ExportTraceOptions.js';
|
|
10
11
|
import * as FieldSettingsDialog from './FieldSettingsDialog.js';
|
|
@@ -29,6 +30,7 @@ export {
|
|
|
29
30
|
Breadcrumbs,
|
|
30
31
|
BreadcrumbsUI,
|
|
31
32
|
CPUThrottlingSelector,
|
|
33
|
+
CWVMetrics,
|
|
32
34
|
DetailsView,
|
|
33
35
|
ExportTraceOptions,
|
|
34
36
|
FieldSettingsDialog,
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2026 The Chromium Authors
|
|
3
|
+
* Use of this source code is governed by a BSD-style license that can be
|
|
4
|
+
* found in the LICENSE file.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
.metrics {
|
|
8
|
+
display: grid;
|
|
9
|
+
align-items: end;
|
|
10
|
+
grid-template-columns: repeat(3, 1fr) 0.5fr;
|
|
11
|
+
row-gap: 5px;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.row-border {
|
|
15
|
+
grid-column: 1/5;
|
|
16
|
+
border-top: var(--sys-size-1) solid var(--sys-color-divider);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.row-label {
|
|
20
|
+
visibility: hidden;
|
|
21
|
+
font-size: var(--sys-size-7);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.metrics--field .row-label {
|
|
25
|
+
visibility: visible;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
.metrics-row {
|
|
29
|
+
display: contents;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.metric {
|
|
33
|
+
flex: 1;
|
|
34
|
+
user-select: text;
|
|
35
|
+
cursor: pointer;
|
|
36
|
+
/* metric container is a button for a11y reasons, so remove default styles
|
|
37
|
+
* */
|
|
38
|
+
background: none;
|
|
39
|
+
border: none;
|
|
40
|
+
padding: 0;
|
|
41
|
+
display: block;
|
|
42
|
+
text-align: left;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.metric-value {
|
|
46
|
+
font-size: var(--sys-size-10);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.metric-value-bad {
|
|
50
|
+
color: var(--app-color-performance-bad);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.metric-value-ok {
|
|
54
|
+
color: var(--app-color-performance-ok);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.metric-value-good {
|
|
58
|
+
color: var(--app-color-performance-good);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.metric-score-unclassified {
|
|
62
|
+
color: var(--sys-color-token-subtle);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.metric-label {
|
|
66
|
+
font: var(--sys-typescale-body4-medium);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.number-with-unit {
|
|
70
|
+
white-space: nowrap;
|
|
71
|
+
|
|
72
|
+
.unit {
|
|
73
|
+
font-size: 14px;
|
|
74
|
+
padding: 0 1px;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.field-mismatch-notice {
|
|
79
|
+
display: grid;
|
|
80
|
+
grid-template-columns: auto auto;
|
|
81
|
+
align-items: center;
|
|
82
|
+
background-color: var(--sys-color-surface3);
|
|
83
|
+
margin: var(--sys-size-6) 0;
|
|
84
|
+
border-radius: var(--sys-shape-corner-extra-small);
|
|
85
|
+
border: var(--sys-size-1) solid var(--sys-color-divider);
|
|
86
|
+
|
|
87
|
+
h3 {
|
|
88
|
+
margin-block: 3px;
|
|
89
|
+
font: var(--sys-typescale-body4-medium);
|
|
90
|
+
color: var(--sys-color-on-base);
|
|
91
|
+
padding: var(--sys-size-5) var(--sys-size-6) 0 var(--sys-size-6);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.field-mismatch-notice__body {
|
|
95
|
+
padding: var(--sys-size-3) var(--sys-size-6) var(--sys-size-5) var(--sys-size-6);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
button {
|
|
99
|
+
padding: 5px;
|
|
100
|
+
background: unset;
|
|
101
|
+
border: unset;
|
|
102
|
+
font: inherit;
|
|
103
|
+
color: var(--sys-color-primary);
|
|
104
|
+
text-decoration: underline;
|
|
105
|
+
cursor: pointer;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -9,77 +9,6 @@
|
|
|
9
9
|
padding: 5px 8px;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
.metrics {
|
|
13
|
-
display: grid;
|
|
14
|
-
align-items: end;
|
|
15
|
-
grid-template-columns: repeat(3, 1fr) 0.5fr;
|
|
16
|
-
row-gap: 5px;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
.row-border {
|
|
20
|
-
grid-column: 1/5;
|
|
21
|
-
border-top: var(--sys-size-1) solid var(--sys-color-divider);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
.row-label {
|
|
25
|
-
visibility: hidden;
|
|
26
|
-
font-size: var(--sys-size-7);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
.metrics--field .row-label {
|
|
30
|
-
visibility: visible;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
.metrics-row {
|
|
34
|
-
display: contents;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
.metric {
|
|
38
|
-
flex: 1;
|
|
39
|
-
user-select: text;
|
|
40
|
-
cursor: pointer;
|
|
41
|
-
/* metric container is a button for a11y reasons, so remove default styles
|
|
42
|
-
* */
|
|
43
|
-
background: none;
|
|
44
|
-
border: none;
|
|
45
|
-
padding: 0;
|
|
46
|
-
display: block;
|
|
47
|
-
text-align: left;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
.metric-value {
|
|
51
|
-
font-size: var(--sys-size-10);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
.metric-value-bad {
|
|
55
|
-
color: var(--app-color-performance-bad);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
.metric-value-ok {
|
|
59
|
-
color: var(--app-color-performance-ok);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
.metric-value-good {
|
|
63
|
-
color: var(--app-color-performance-good);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
.metric-score-unclassified {
|
|
67
|
-
color: var(--sys-color-token-subtle);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
.metric-label {
|
|
71
|
-
font: var(--sys-typescale-body4-medium);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
.number-with-unit {
|
|
75
|
-
white-space: nowrap;
|
|
76
|
-
|
|
77
|
-
.unit {
|
|
78
|
-
font-size: 14px;
|
|
79
|
-
padding: 0 1px;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
12
|
.passed-insights-section {
|
|
84
13
|
margin-top: var(--sys-size-5);
|
|
85
14
|
|
|
@@ -87,34 +16,3 @@
|
|
|
87
16
|
font-weight: var(--ref-typeface-weight-medium);
|
|
88
17
|
}
|
|
89
18
|
}
|
|
90
|
-
|
|
91
|
-
.field-mismatch-notice {
|
|
92
|
-
display: grid;
|
|
93
|
-
grid-template-columns: auto auto;
|
|
94
|
-
align-items: center;
|
|
95
|
-
background-color: var(--sys-color-surface3);
|
|
96
|
-
margin: var(--sys-size-6) 0;
|
|
97
|
-
border-radius: var(--sys-shape-corner-extra-small);
|
|
98
|
-
border: var(--sys-size-1) solid var(--sys-color-divider);
|
|
99
|
-
|
|
100
|
-
h3 {
|
|
101
|
-
margin-block: 3px;
|
|
102
|
-
font: var(--sys-typescale-body4-medium);
|
|
103
|
-
color: var(--sys-color-on-base);
|
|
104
|
-
padding: var(--sys-size-5) var(--sys-size-6) 0 var(--sys-size-6);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
.field-mismatch-notice__body {
|
|
108
|
-
padding: var(--sys-size-3) var(--sys-size-6) var(--sys-size-5) var(--sys-size-6);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
button {
|
|
112
|
-
padding: 5px;
|
|
113
|
-
background: unset;
|
|
114
|
-
border: unset;
|
|
115
|
-
font: inherit;
|
|
116
|
-
color: var(--sys-color-primary);
|
|
117
|
-
text-decoration: underline;
|
|
118
|
-
cursor: pointer;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
@@ -368,3 +368,14 @@ Common.Revealer.registerRevealer({
|
|
|
368
368
|
return new Timeline.TimelinePanel.InsightRevealer();
|
|
369
369
|
},
|
|
370
370
|
});
|
|
371
|
+
|
|
372
|
+
Common.Revealer.registerRevealer({
|
|
373
|
+
contextTypes() {
|
|
374
|
+
return maybeRetrieveContextTypes(Timeline => [Timeline.Utils.Helpers.RevealableCoreVitals]);
|
|
375
|
+
},
|
|
376
|
+
destination: Common.Revealer.RevealerDestination.TIMELINE_PANEL,
|
|
377
|
+
async loadRevealer() {
|
|
378
|
+
const Timeline = await loadTimelineModule();
|
|
379
|
+
return new Timeline.TimelinePanel.CoreVitalsRevealer();
|
|
380
|
+
},
|
|
381
|
+
});
|
|
@@ -147,8 +147,12 @@ export function formatOriginWithEntity(
|
|
|
147
147
|
return originWithEntity;
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
-
/** Thin wrapper class to enable revealing an individual insight in Timeline panel. **/
|
|
151
150
|
export class RevealableInsight {
|
|
152
151
|
constructor(public insight: Trace.Insights.Types.InsightModel) {
|
|
153
152
|
}
|
|
154
153
|
}
|
|
154
|
+
|
|
155
|
+
export class RevealableCoreVitals {
|
|
156
|
+
constructor(public insightSetKey: string) {
|
|
157
|
+
}
|
|
158
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Name: Dependencies sourced from the upstream `chromium` repository
|
|
2
2
|
URL: Internal
|
|
3
3
|
Version: N/A
|
|
4
|
-
Revision:
|
|
4
|
+
Revision: ce01102937348db7b88c8a4257ee4b3ac702eb1a
|
|
5
5
|
Update Mechanism: Manual (https://crbug.com/428069060)
|
|
6
6
|
License: BSD-3-Clause
|
|
7
7
|
License File: LICENSE
|