chrome-devtools-frontend 1.0.1652307 → 1.0.1654411
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/.agents/skills/fixing-skipped-tests/SKILL.md +32 -0
- package/.agents/skills/migrate-chromium-test/SKILL.md +144 -0
- package/docs/get_the_code.md +1 -2
- package/docs/ui_engineering.md +3 -1
- package/eslint.config.mjs +0 -5
- package/front_end/core/sdk/CPUThrottlingManager.ts +13 -8
- package/front_end/core/sdk/CSSMetadata.ts +23 -0
- package/front_end/core/sdk/CompilerSourceMappingContentProvider.ts +8 -4
- package/front_end/core/sdk/DOMModel.ts +4 -0
- package/front_end/core/sdk/EmulationModel.ts +4 -0
- package/front_end/core/sdk/ResourceTreeModel.ts +3 -2
- package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshot.ts +459 -74
- package/front_end/entrypoints/main/MainImpl.ts +0 -6
- package/front_end/foundation/Universe.ts +18 -0
- package/front_end/generated/InspectorBackendCommands.ts +6 -5
- package/front_end/generated/SupportedCSSProperties.js +122 -28
- package/front_end/generated/protocol.ts +58 -0
- package/front_end/models/ai_assistance/AiConversation.ts +2 -1
- package/front_end/models/ai_assistance/README.md +4 -3
- package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +1 -1
- package/front_end/models/ai_assistance/agents/PerformanceAgent.snapshot.txt +2 -2
- package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +11 -164
- package/front_end/models/ai_assistance/ai_assistance.ts +6 -2
- package/front_end/models/ai_assistance/contexts/PerformanceTraceContext.ts +295 -0
- package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.snapshot.txt +10 -10
- package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.ts +12 -11
- package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +6 -6
- package/front_end/models/ai_assistance/skills/accessibility.md +4 -2
- package/front_end/models/ai_assistance/tools/GetElementAccessibilityDetails.ts +142 -0
- package/front_end/models/ai_assistance/tools/GetStyles.ts +1 -1
- package/front_end/models/ai_assistance/tools/README.md +21 -1
- package/front_end/models/ai_assistance/tools/{ResolveLighthousePath.ts → ResolveDevtoolsNodePath.ts} +18 -17
- package/front_end/models/ai_assistance/tools/Tool.ts +6 -1
- package/front_end/models/ai_assistance/tools/ToolRegistry.ts +4 -2
- package/front_end/models/autofill_manager/AutofillManager.ts +7 -15
- package/front_end/models/bindings/NetworkProject.ts +4 -8
- package/front_end/models/breakpoints/BreakpointManager.ts +9 -9
- package/front_end/models/emulation/DeviceModeModel.ts +24 -0
- package/front_end/models/emulation/EmulatedDevices.ts +108 -0
- package/front_end/models/heap_snapshot/HeapSnapshotModel.ts +36 -0
- package/front_end/models/heap_snapshot/HeapSnapshotProxy.ts +8 -0
- package/front_end/models/issues_manager/IssuesManager.ts +10 -6
- package/front_end/models/javascript_metadata/JavaScriptMetadata.ts +4 -5
- package/front_end/models/live-metrics/LiveMetrics.ts +6 -6
- package/front_end/models/live-metrics/web-vitals-injected/spec/spec.ts +5 -5
- package/front_end/models/live-metrics/web-vitals-injected/web-vitals-injected.ts +3 -3
- package/front_end/models/logs/LogManager.ts +25 -10
- package/front_end/models/logs/NetworkLog.ts +24 -19
- package/front_end/models/persistence/AutomaticFileSystemWorkspaceBinding.ts +14 -5
- package/front_end/models/persistence/IsolatedFileSystem.ts +9 -11
- package/front_end/models/persistence/IsolatedFileSystemManager.ts +5 -3
- package/front_end/models/persistence/NetworkPersistenceManager.ts +73 -44
- package/front_end/models/persistence/PersistenceImpl.ts +4 -5
- package/front_end/models/trace/EntityMapper.ts +1 -1
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +9 -9
- package/front_end/panels/ai_assistance/ExportConversation.ts +4 -1
- package/front_end/panels/ai_assistance/components/ChatInput.ts +2 -2
- package/front_end/panels/application/IndexedDBViews.ts +38 -36
- package/front_end/panels/application/components/AdsView.ts +4 -0
- package/front_end/panels/autofill/AutofillView.ts +1 -1
- package/front_end/panels/autofill/autofill-meta.ts +3 -2
- package/front_end/panels/elements/AccessibilityTreeUtils.ts +26 -15
- package/front_end/panels/elements/AccessibilityTreeView.ts +12 -9
- package/front_end/panels/elements/ElementsTreeOutline.ts +5 -0
- package/front_end/panels/layer_viewer/LayerTreeOutline.ts +118 -68
- package/front_end/panels/layer_viewer/Layers3DView.ts +1 -1
- package/front_end/panels/layer_viewer/layerTreeOutline.css +20 -13
- package/front_end/panels/layers/LayersPanel.ts +1 -1
- package/front_end/panels/mobile_throttling/ThrottlingSettingsTab.ts +64 -17
- package/front_end/panels/mobile_throttling/throttlingSettingsTab.css +5 -0
- package/front_end/panels/network/LinkPreloadGenerator.ts +101 -0
- package/front_end/panels/network/NetworkLogView.ts +28 -0
- package/front_end/panels/network/network.ts +2 -0
- package/front_end/panels/profiler/HeapSnapshotView.ts +121 -33
- package/front_end/panels/profiler/ModuleUIStrings.ts +4 -0
- package/front_end/panels/timeline/TimelineHistoryManager.ts +38 -37
- package/front_end/panels/timeline/TimelinePanel.ts +32 -8
- package/front_end/panels/timeline/components/CPUThrottlingSelector.ts +7 -6
- package/front_end/panels/timeline/components/LiveMetricsView.ts +38 -38
- package/front_end/panels/timeline/components/MetricCard.ts +18 -18
- package/front_end/panels/timeline/components/liveMetricsView.css +6 -6
- package/front_end/panels/timeline/components/metricCard.css +4 -4
- package/front_end/panels/whats_new/ReleaseNoteText.ts +3 -3
- package/front_end/panels/whats_new/resources/WNDT.md +1 -1
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/ui/legacy/InspectorDrawerView.ts +7 -5
- package/front_end/ui/legacy/ListWidget.ts +12 -0
- package/front_end/ui/legacy/SplitWidget.ts +12 -11
- package/front_end/ui/legacy/components/data_grid/DataGridElement.ts +23 -19
- package/front_end/ui/legacy/components/object_ui/CustomPreviewComponent.ts +8 -1
- package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +110 -17
- package/front_end/ui/legacy/treeoutline.css +1 -1
- package/front_end/ui/visual_logging/KnownContextValues.ts +1 -0
- package/package.json +4 -3
|
@@ -151,6 +151,32 @@ const UIStrings = {
|
|
|
151
151
|
* objects retained by event handlers
|
|
152
152
|
*/
|
|
153
153
|
objectsRetainedByEventHandlers: 'Objects retained by Event Handlers',
|
|
154
|
+
/**
|
|
155
|
+
* @description An option which will filter the heap snapshot to show only
|
|
156
|
+
* objects attributed to a specific native context (roughly, a JavaScript
|
|
157
|
+
* realm such as a frame). PH1 is a name identifying the context
|
|
158
|
+
* (often a URL) which may be empty, PH2 is the id of the native context
|
|
159
|
+
* object, and PH3 is the context's attributed size.
|
|
160
|
+
* @example {https://example.com/ } PH1
|
|
161
|
+
* @example {1234} PH2
|
|
162
|
+
* @example {1.2 MB} PH3
|
|
163
|
+
*/
|
|
164
|
+
objectsAttributedToNativeContextS: 'Native context {PH1}@{PH2} ({PH3})',
|
|
165
|
+
/**
|
|
166
|
+
* @description An option which will filter the heap snapshot to show only
|
|
167
|
+
* objects which are shared between multiple native contexts (roughly,
|
|
168
|
+
* JavaScript realms such as frames). PH1 is their total size.
|
|
169
|
+
* @example {1.2 MB} PH1
|
|
170
|
+
*/
|
|
171
|
+
objectsSharedBetweenNativeContextsS: 'Objects shared between native contexts ({PH1})',
|
|
172
|
+
/**
|
|
173
|
+
* @description An option which will filter the heap snapshot to show only
|
|
174
|
+
* objects which could not be attributed to any single native context
|
|
175
|
+
* (roughly, a JavaScript realm such as a frame). PH1 is their
|
|
176
|
+
* total size.
|
|
177
|
+
* @example {1.2 MB} PH1
|
|
178
|
+
*/
|
|
179
|
+
objectsNotAttributedToNativeContextS: 'Objects not attributed to a native context ({PH1})',
|
|
154
180
|
/**
|
|
155
181
|
* @description Text for the summary view
|
|
156
182
|
*/
|
|
@@ -266,6 +292,19 @@ const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
|
|
266
292
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
267
293
|
const moduleUIstr_ = i18n.i18n.registerUIStrings('panels/profiler/ModuleUIStrings.ts', ModuleUIStrings.UIStrings);
|
|
268
294
|
const moduleI18nString = i18n.i18n.getLocalizedString.bind(undefined, moduleUIstr_);
|
|
295
|
+
|
|
296
|
+
interface NamedFilter {
|
|
297
|
+
uiName: string;
|
|
298
|
+
filterName: string;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
interface FilterOption {
|
|
302
|
+
uiName: string;
|
|
303
|
+
profileIndex: number;
|
|
304
|
+
filterName?: string;
|
|
305
|
+
disabled?: boolean;
|
|
306
|
+
}
|
|
307
|
+
|
|
269
308
|
export class HeapSnapshotView extends UI.View.SimpleView implements DataDisplayDelegate, UI.SearchableView.Searchable {
|
|
270
309
|
searchResults: number[] = [];
|
|
271
310
|
profile: HeapProfileHeader;
|
|
@@ -293,6 +332,8 @@ export class HeapSnapshotView extends UI.View.SimpleView implements DataDisplayD
|
|
|
293
332
|
readonly perspectiveSelect: UI.Toolbar.ToolbarComboBox;
|
|
294
333
|
baseSelect: UI.Toolbar.ToolbarComboBox;
|
|
295
334
|
readonly filterSelect: UI.Toolbar.ToolbarComboBox;
|
|
335
|
+
#filterOptions: FilterOption[] = [];
|
|
336
|
+
#nativeContextFilters: NamedFilter[] = [];
|
|
296
337
|
readonly classNameFilter: UI.Toolbar.ToolbarInput;
|
|
297
338
|
readonly selectedSizeText: UI.Toolbar.ToolbarText;
|
|
298
339
|
readonly resetRetainersButton: UI.Toolbar.ToolbarButton;
|
|
@@ -556,6 +597,7 @@ export class HeapSnapshotView extends UI.View.SimpleView implements DataDisplayD
|
|
|
556
597
|
const heapSnapshotProxy = await this.profile.loadPromise;
|
|
557
598
|
|
|
558
599
|
void this.retrieveStatistics(heapSnapshotProxy);
|
|
600
|
+
void this.updateNativeContextFilters(heapSnapshotProxy);
|
|
559
601
|
if (this.dataGrid) {
|
|
560
602
|
void this.dataGrid.setDataSource(heapSnapshotProxy, 0);
|
|
561
603
|
}
|
|
@@ -611,6 +653,52 @@ export class HeapSnapshotView extends UI.View.SimpleView implements DataDisplayD
|
|
|
611
653
|
return statistics;
|
|
612
654
|
}
|
|
613
655
|
|
|
656
|
+
async updateNativeContextFilters(heapSnapshotProxy: HeapSnapshotModel.HeapSnapshotProxy.HeapSnapshotProxy):
|
|
657
|
+
Promise<void> {
|
|
658
|
+
const sizes = await heapSnapshotProxy.getNativeContextSizes();
|
|
659
|
+
const filters: NamedFilter[] = [];
|
|
660
|
+
|
|
661
|
+
// List the individual native contexts first, sorted by attributed size
|
|
662
|
+
// (largest first), then the shared and unattributed buckets.
|
|
663
|
+
const nativeContexts = sizes.nativeContexts.toSorted((a, b) => b.attributedSize - a.attributedSize);
|
|
664
|
+
for (const nativeContext of nativeContexts) {
|
|
665
|
+
// Drop the "system / NativeContext" boilerplate (and any "Detached "
|
|
666
|
+
// marker) so the label shows just the distinguishing part of the name
|
|
667
|
+
// (e.g. the URL). The remaining name (if any) is followed by the native
|
|
668
|
+
// context object id, e.g. "Native context https://example.com @1234".
|
|
669
|
+
let name = nativeContext.nodeName;
|
|
670
|
+
if (name.startsWith('Detached ')) {
|
|
671
|
+
name = name.substring('Detached '.length);
|
|
672
|
+
}
|
|
673
|
+
if (name.startsWith('system / NativeContext / ')) {
|
|
674
|
+
name = name.substring('system / NativeContext / '.length);
|
|
675
|
+
} else if (name.startsWith('system / NativeContext')) {
|
|
676
|
+
name = name.substring('system / NativeContext'.length);
|
|
677
|
+
}
|
|
678
|
+
filters.push({
|
|
679
|
+
uiName: i18nString(UIStrings.objectsAttributedToNativeContextS, {
|
|
680
|
+
PH1: name ? `${name} ` : '',
|
|
681
|
+
PH2: nativeContext.nodeId,
|
|
682
|
+
PH3: i18n.ByteUtilities.bytesToString(nativeContext.attributedSize),
|
|
683
|
+
}),
|
|
684
|
+
filterName: `nativeContext_${nativeContext.nodeIndex}`,
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
filters.push({
|
|
688
|
+
uiName: i18nString(UIStrings.objectsSharedBetweenNativeContextsS,
|
|
689
|
+
{PH1: i18n.ByteUtilities.bytesToString(sizes.sharedSize)}),
|
|
690
|
+
filterName: 'sharedNativeContext',
|
|
691
|
+
});
|
|
692
|
+
filters.push({
|
|
693
|
+
uiName: i18nString(UIStrings.objectsNotAttributedToNativeContextS,
|
|
694
|
+
{PH1: i18n.ByteUtilities.bytesToString(sizes.noAttributionSize)}),
|
|
695
|
+
filterName: 'noNativeContext',
|
|
696
|
+
});
|
|
697
|
+
|
|
698
|
+
this.#nativeContextFilters = filters;
|
|
699
|
+
this.updateFilterOptions();
|
|
700
|
+
}
|
|
701
|
+
|
|
614
702
|
onIdsRangeChanged(event: Common.EventTarget.EventTargetEvent<IdsRangeChangedEvent>): void {
|
|
615
703
|
const {minId, maxId} = event.data;
|
|
616
704
|
this.selectedSizeText.setText(
|
|
@@ -775,7 +863,7 @@ export class HeapSnapshotView extends UI.View.SimpleView implements DataDisplayD
|
|
|
775
863
|
this.performSearch(this.currentSearch, false);
|
|
776
864
|
}
|
|
777
865
|
|
|
778
|
-
static readonly ALWAYS_AVAILABLE_FILTERS:
|
|
866
|
+
static readonly ALWAYS_AVAILABLE_FILTERS: readonly NamedFilter[] = [
|
|
779
867
|
{uiName: i18nString(UIStrings.duplicatedStrings), filterName: 'duplicatedStrings'},
|
|
780
868
|
{uiName: i18nString(UIStrings.objectsRetainedByDetachedDomNodes), filterName: 'objectsRetainedByDetachedDomNodes'},
|
|
781
869
|
{uiName: i18nString(UIStrings.objectsRetainedByContexts), filterName: 'objectsRetainedByContexts'},
|
|
@@ -784,16 +872,9 @@ export class HeapSnapshotView extends UI.View.SimpleView implements DataDisplayD
|
|
|
784
872
|
];
|
|
785
873
|
|
|
786
874
|
changeFilter(): void {
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
const
|
|
790
|
-
this.filterSelect.size() - HeapSnapshotView.ALWAYS_AVAILABLE_FILTERS.length;
|
|
791
|
-
if (selectedIndex >= indexOfFirstAlwaysAvailableFilter) {
|
|
792
|
-
filterName =
|
|
793
|
-
HeapSnapshotView.ALWAYS_AVAILABLE_FILTERS[selectedIndex - indexOfFirstAlwaysAvailableFilter].filterName;
|
|
794
|
-
selectedIndex = 0;
|
|
795
|
-
}
|
|
796
|
-
const profileIndex = selectedIndex - 1;
|
|
875
|
+
const selectedOption = this.#filterOptions[this.filterSelect.selectedIndex()];
|
|
876
|
+
const profileIndex = selectedOption?.profileIndex ?? -1;
|
|
877
|
+
const filterName = selectedOption?.filterName;
|
|
797
878
|
if (!this.dataGrid) {
|
|
798
879
|
return;
|
|
799
880
|
}
|
|
@@ -1024,10 +1105,20 @@ export class HeapSnapshotView extends UI.View.SimpleView implements DataDisplayD
|
|
|
1024
1105
|
updateFilterOptions(): void {
|
|
1025
1106
|
const list = this.profiles();
|
|
1026
1107
|
const selectedIndex = this.filterSelect.selectedIndex();
|
|
1027
|
-
const
|
|
1108
|
+
const selectedOption = this.#filterOptions[selectedIndex];
|
|
1109
|
+
const filterOptions: FilterOption[] = [];
|
|
1110
|
+
const createOption = (filterOption: FilterOption): HTMLOptionElement => {
|
|
1111
|
+
filterOptions.push(filterOption);
|
|
1112
|
+
const option = this.filterSelect.createOption(filterOption.uiName);
|
|
1113
|
+
option.disabled = Boolean(filterOption.disabled);
|
|
1114
|
+
return option;
|
|
1115
|
+
};
|
|
1116
|
+
const createSeparator = (): HTMLOptionElement => {
|
|
1117
|
+
return createOption({uiName: '\u2014'.repeat(18), profileIndex: -1, disabled: true});
|
|
1118
|
+
};
|
|
1028
1119
|
|
|
1029
1120
|
this.filterSelect.removeOptions();
|
|
1030
|
-
|
|
1121
|
+
createOption({uiName: i18nString(UIStrings.allObjects), profileIndex: -1});
|
|
1031
1122
|
for (let i = 0; i < list.length; ++i) {
|
|
1032
1123
|
let title;
|
|
1033
1124
|
if (!i) {
|
|
@@ -1035,34 +1126,31 @@ export class HeapSnapshotView extends UI.View.SimpleView implements DataDisplayD
|
|
|
1035
1126
|
} else {
|
|
1036
1127
|
title = i18nString(UIStrings.objectsAllocatedBetweenSAndS, {PH1: list[i - 1].title, PH2: list[i].title});
|
|
1037
1128
|
}
|
|
1038
|
-
|
|
1129
|
+
createOption({uiName: title, profileIndex: i});
|
|
1039
1130
|
}
|
|
1040
1131
|
|
|
1041
|
-
|
|
1042
|
-
const dividerIndex = this.filterSelect.size();
|
|
1043
|
-
const divider = this.filterSelect.createOption('\u2014'.repeat(18));
|
|
1044
|
-
(divider).disabled = true;
|
|
1132
|
+
createSeparator();
|
|
1045
1133
|
|
|
1046
1134
|
for (const filter of HeapSnapshotView.ALWAYS_AVAILABLE_FILTERS) {
|
|
1047
|
-
|
|
1135
|
+
createOption({uiName: filter.uiName, profileIndex: -1, filterName: filter.filterName});
|
|
1048
1136
|
}
|
|
1049
1137
|
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
if (distanceFromEnd <= HeapSnapshotView.ALWAYS_AVAILABLE_FILTERS.length) {
|
|
1055
|
-
// If one of the always-available filters was selected, then select the
|
|
1056
|
-
// same filter again even though its index may have changed.
|
|
1057
|
-
this.filterSelect.setSelectedIndex(newSize - distanceFromEnd);
|
|
1058
|
-
} else if (selectedIndex >= dividerIndex) {
|
|
1059
|
-
// If the select list is now shorter than it was, such that we can't
|
|
1060
|
-
// keep the index unchanged, set it to -1, which causes it to be blank.
|
|
1061
|
-
this.filterSelect.setSelectedIndex(-1);
|
|
1062
|
-
} else {
|
|
1063
|
-
this.filterSelect.setSelectedIndex(selectedIndex);
|
|
1138
|
+
if (this.#nativeContextFilters.length > 0) {
|
|
1139
|
+
createSeparator();
|
|
1140
|
+
for (const filter of this.#nativeContextFilters) {
|
|
1141
|
+
createOption({uiName: filter.uiName, profileIndex: -1, filterName: filter.filterName});
|
|
1064
1142
|
}
|
|
1065
1143
|
}
|
|
1144
|
+
|
|
1145
|
+
this.#filterOptions = filterOptions;
|
|
1146
|
+
|
|
1147
|
+
if (selectedOption) {
|
|
1148
|
+
const newSelectedIndex = this.#filterOptions.findIndex(option => {
|
|
1149
|
+
return !option.disabled && option.profileIndex === selectedOption.profileIndex &&
|
|
1150
|
+
option.filterName === selectedOption.filterName;
|
|
1151
|
+
});
|
|
1152
|
+
this.filterSelect.setSelectedIndex(newSelectedIndex);
|
|
1153
|
+
}
|
|
1066
1154
|
}
|
|
1067
1155
|
|
|
1068
1156
|
updateControls(): void {
|
|
@@ -53,6 +53,10 @@ export const UIStrings = {
|
|
|
53
53
|
* @description Text to indicate the status of a heap snapshot in the Performance Pane
|
|
54
54
|
*/
|
|
55
55
|
buildingLocations: 'Building locations…',
|
|
56
|
+
/**
|
|
57
|
+
* @description Text to indicate the status of a heap snapshot in the Performance Pane
|
|
58
|
+
*/
|
|
59
|
+
calculatingNativeContextAttribution: 'Calculating native context attribution…',
|
|
56
60
|
/**
|
|
57
61
|
* @description Text to indicate the status of a heap snapshot in the Performance Pane
|
|
58
62
|
*/
|
|
@@ -7,8 +7,8 @@ import * as Common from '../../core/common/common.js';
|
|
|
7
7
|
import * as i18n from '../../core/i18n/i18n.js';
|
|
8
8
|
import * as Platform from '../../core/platform/platform.js';
|
|
9
9
|
import * as Trace from '../../models/trace/trace.js';
|
|
10
|
-
import {createIcon} from '../../ui/kit/kit.js';
|
|
11
10
|
import * as UI from '../../ui/legacy/legacy.js';
|
|
11
|
+
import {html, type LitTemplate, render} from '../../ui/lit/lit.js';
|
|
12
12
|
import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
|
|
13
13
|
|
|
14
14
|
import {
|
|
@@ -361,39 +361,43 @@ export class TimelineHistoryManager {
|
|
|
361
361
|
};
|
|
362
362
|
parsedTraceIndexToPerformancePreviewData.set(parsedTraceIndex, data);
|
|
363
363
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
364
|
+
// eslint-disable-next-line @devtools/no-lit-render-outside-of-view
|
|
365
|
+
render(html`
|
|
366
|
+
${this.#renderTextDetails(parsedTrace.metadata, domain)}
|
|
367
|
+
<div class="hbox">
|
|
368
|
+
${this.#renderScreenshotThumbnail(filmStrip)}
|
|
369
|
+
${this.#buildOverview(parsedTrace)}
|
|
370
|
+
</div>
|
|
371
|
+
`,
|
|
372
|
+
preview, {host: this});
|
|
373
|
+
|
|
368
374
|
return data.preview;
|
|
369
375
|
}
|
|
370
376
|
|
|
371
|
-
#
|
|
372
|
-
|
|
373
|
-
container.classList.add('text-details');
|
|
374
|
-
container.classList.add('hbox');
|
|
375
|
-
const nameSpan = container.createChild('span', 'name');
|
|
376
|
-
nameSpan.textContent = title;
|
|
377
|
-
UI.ARIAUtils.setLabel(nameSpan, title);
|
|
378
|
-
|
|
377
|
+
#renderTextDetails(metadata: Trace.Types.File.MetaData|null, title: string): LitTemplate {
|
|
378
|
+
let metadataText = '';
|
|
379
379
|
if (metadata) {
|
|
380
380
|
const parts = [
|
|
381
381
|
metadata.emulatedDeviceTitle,
|
|
382
382
|
metadata.cpuThrottling ? i18nString(UIStrings.dSlowdown, {PH1: metadata.cpuThrottling}) : undefined,
|
|
383
383
|
metadata.networkThrottling,
|
|
384
384
|
].filter(Boolean);
|
|
385
|
-
|
|
385
|
+
metadataText = listFormatter.format(parts as string[]);
|
|
386
386
|
}
|
|
387
387
|
|
|
388
|
-
return
|
|
388
|
+
return html`
|
|
389
|
+
<div class="text-details hbox">
|
|
390
|
+
<span class="name">${title}</span>
|
|
391
|
+
${metadataText ? html`<span class="metadata">${metadataText}</span>` : ''}
|
|
392
|
+
</div>
|
|
393
|
+
`;
|
|
389
394
|
}
|
|
390
395
|
|
|
391
|
-
#
|
|
392
|
-
const container = document.createElement('div');
|
|
393
|
-
container.classList.add('screenshot-thumb');
|
|
396
|
+
#renderScreenshotThumbnail(filmStrip: Trace.Extras.FilmStrip.Data|null): LitTemplate {
|
|
394
397
|
const thumbnailAspectRatio = 3 / 2;
|
|
395
|
-
|
|
396
|
-
|
|
398
|
+
const width = this.totalHeight * thumbnailAspectRatio + 'px';
|
|
399
|
+
const height = this.totalHeight + 'px';
|
|
400
|
+
const container = html`<div class="screenshot-thumb" style="width: ${width}; height: ${height}"></div>`;
|
|
397
401
|
if (!filmStrip) {
|
|
398
402
|
return container;
|
|
399
403
|
}
|
|
@@ -403,12 +407,11 @@ export class TimelineHistoryManager {
|
|
|
403
407
|
}
|
|
404
408
|
// TODO(paulirish): Adopt Util.ImageCache
|
|
405
409
|
const uri = Trace.Handlers.ModelHandlers.Screenshots.screenshotImageDataUri(lastFrame.screenshotEvent);
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
return container;
|
|
410
|
+
return html`
|
|
411
|
+
<div class="screenshot-thumb" style="width: ${width}; height: ${height}">
|
|
412
|
+
<img src=${uri}>
|
|
413
|
+
</div>
|
|
414
|
+
`;
|
|
412
415
|
}
|
|
413
416
|
|
|
414
417
|
#buildOverview(parsedTrace: Trace.TraceModel.ParsedTrace): Element {
|
|
@@ -582,14 +585,14 @@ export class DropDown implements UI.ListControl.ListDelegate<number> {
|
|
|
582
585
|
|
|
583
586
|
div.style.width = `${previewWidth}px`;
|
|
584
587
|
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
588
|
+
// eslint-disable-next-line @devtools/no-lit-render-outside-of-view
|
|
589
|
+
render(html`
|
|
590
|
+
<devtools-icon class="back-arrow" title=${
|
|
591
|
+
i18nString(UIStrings.backButtonTooltip)} name="arrow-back"></devtools-icon>
|
|
592
|
+
<span>${this.#landingPageTitle}</span>
|
|
593
|
+
`,
|
|
594
|
+
div, {host: this});
|
|
589
595
|
|
|
590
|
-
const text = document.createElement('span');
|
|
591
|
-
text.innerText = this.#landingPageTitle;
|
|
592
|
-
div.appendChild(text);
|
|
593
596
|
return div;
|
|
594
597
|
}
|
|
595
598
|
|
|
@@ -619,14 +622,11 @@ export class DropDown implements UI.ListControl.ListDelegate<number> {
|
|
|
619
622
|
}
|
|
620
623
|
|
|
621
624
|
export class ToolbarButton extends UI.Toolbar.ToolbarItem {
|
|
622
|
-
private contentElement: HTMLElement;
|
|
623
|
-
|
|
624
625
|
constructor(action: UI.ActionRegistration.Action) {
|
|
625
626
|
const element = document.createElement('button');
|
|
626
627
|
element.classList.add('history-dropdown-button');
|
|
627
628
|
element.setAttribute('jslog', `${VisualLogging.dropDown('history')}`);
|
|
628
629
|
super(element);
|
|
629
|
-
this.contentElement = this.element.createChild('span', 'content');
|
|
630
630
|
this.element.addEventListener('click', () => void action.execute(), false);
|
|
631
631
|
this.setEnabled(action.enabled());
|
|
632
632
|
action.addEventListener(UI.ActionRegistration.Events.ENABLED, event => this.setEnabled(event.data));
|
|
@@ -634,6 +634,7 @@ export class ToolbarButton extends UI.Toolbar.ToolbarItem {
|
|
|
634
634
|
}
|
|
635
635
|
|
|
636
636
|
setText(text: string): void {
|
|
637
|
-
|
|
637
|
+
// eslint-disable-next-line @devtools/no-lit-render-outside-of-view
|
|
638
|
+
render(html`<span class="content">${text}</span>`, this.element, {host: this});
|
|
638
639
|
}
|
|
639
640
|
}
|
|
@@ -2149,14 +2149,21 @@ export class TimelinePanel extends Common.ObjectWrapper.eventMixin<EventTypes, t
|
|
|
2149
2149
|
// Annotation Overlays.
|
|
2150
2150
|
currentManager?.addEventListener(AnnotationModifiedEvent.eventName, this.#onAnnotationModifiedEventBound);
|
|
2151
2151
|
|
|
2152
|
-
// To calculate the activity we might want to zoom in, we use the top-most main-thread track
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2152
|
+
// To calculate the activity we might want to zoom in, we use the top-most main-thread track.
|
|
2153
|
+
// If the trace has an active breadcrumb that is not the initial full trace breadcrumb, we do
|
|
2154
|
+
// not zoom in, but keep the window at the active breadcrumb bounds.
|
|
2155
|
+
const breadcrumbs = currentManager?.getTimelineBreadcrumbs();
|
|
2156
|
+
const hasActiveBreadcrumb = breadcrumbs ? breadcrumbs.activeBreadcrumb !== breadcrumbs.initialBreadcrumb : false;
|
|
2157
|
+
if (!hasActiveBreadcrumb) {
|
|
2158
|
+
const topMostMainThreadAppender =
|
|
2159
|
+
this.flameChart.getMainDataProvider().compatibilityTracksAppenderInstance().threadAppenders().at(0);
|
|
2160
|
+
const zoomWindow = calculateAutoZoomWindow(
|
|
2161
|
+
parsedTrace.data.Meta.traceBounds,
|
|
2162
|
+
topMostMainThreadAppender?.getEntries(),
|
|
2163
|
+
);
|
|
2164
|
+
if (zoomWindow) {
|
|
2165
|
+
TraceBounds.TraceBounds.BoundsManager.instance().setTimelineVisibleWindow(zoomWindow);
|
|
2166
|
+
}
|
|
2160
2167
|
}
|
|
2161
2168
|
|
|
2162
2169
|
// Add overlays for annotations loaded from the trace file
|
|
@@ -3206,3 +3213,20 @@ export interface EventTypes {
|
|
|
3206
3213
|
[Events.IS_VIEWING_TRACE]: boolean;
|
|
3207
3214
|
[Events.RECORDING_COMPLETED]: {traceIndex: number}|{errorText: string};
|
|
3208
3215
|
}
|
|
3216
|
+
|
|
3217
|
+
/**
|
|
3218
|
+
* Calculates the window to auto-zoom into when a trace is loaded.
|
|
3219
|
+
* We only auto-zoom to the main thread activity if there is no active breadcrumb,
|
|
3220
|
+
* as we want to preserve the active breadcrumb's window if one exists.
|
|
3221
|
+
*
|
|
3222
|
+
* @returns the window to zoom into, or null if no auto-zoom should be applied.
|
|
3223
|
+
*/
|
|
3224
|
+
export function calculateAutoZoomWindow(
|
|
3225
|
+
traceBounds: Trace.Types.Timing.TraceWindowMicro,
|
|
3226
|
+
topMostMainThreadAppenderEntries: readonly Trace.Types.Events.Event[]|undefined,
|
|
3227
|
+
): Trace.Types.Timing.TraceWindowMicro|null {
|
|
3228
|
+
if (!topMostMainThreadAppenderEntries || topMostMainThreadAppenderEntries.length === 0) {
|
|
3229
|
+
return null;
|
|
3230
|
+
}
|
|
3231
|
+
return Trace.Extras.MainThreadActivity.calculateWindow(traceBounds, topMostMainThreadAppenderEntries);
|
|
3232
|
+
}
|
|
@@ -139,10 +139,11 @@ export class CPUThrottlingSelector extends UI.Widget.Widget {
|
|
|
139
139
|
#groups: CPUThrottlingGroup[] = [];
|
|
140
140
|
#calibratedThrottlingSetting: Common.Settings.Setting<SDK.CPUThrottlingManager.CalibratedCPUThrottling>;
|
|
141
141
|
readonly #view: View;
|
|
142
|
+
readonly #cpuThrottlingManager = SDK.CPUThrottlingManager.CPUThrottlingManager.instance();
|
|
142
143
|
|
|
143
144
|
constructor(element?: HTMLElement, view: View = DEFAULT_VIEW) {
|
|
144
145
|
super(element);
|
|
145
|
-
this.#currentOption =
|
|
146
|
+
this.#currentOption = this.#cpuThrottlingManager.cpuThrottlingOption();
|
|
146
147
|
this.#calibratedThrottlingSetting =
|
|
147
148
|
Common.Settings.Settings.instance().createSetting<SDK.CPUThrottlingManager.CalibratedCPUThrottling>(
|
|
148
149
|
'calibrated-cpu-throttling', {}, Common.Settings.SettingStorageType.GLOBAL);
|
|
@@ -157,8 +158,8 @@ export class CPUThrottlingSelector extends UI.Widget.Widget {
|
|
|
157
158
|
|
|
158
159
|
override wasShown(): void {
|
|
159
160
|
super.wasShown();
|
|
160
|
-
SDK.CPUThrottlingManager.
|
|
161
|
-
|
|
161
|
+
this.#cpuThrottlingManager.addEventListener(SDK.CPUThrottlingManager.Events.RATE_CHANGED, this.#onOptionChange,
|
|
162
|
+
this);
|
|
162
163
|
this.#calibratedThrottlingSetting.addChangeListener(this.#onCalibratedSettingChanged, this);
|
|
163
164
|
this.#onOptionChange();
|
|
164
165
|
}
|
|
@@ -166,12 +167,12 @@ export class CPUThrottlingSelector extends UI.Widget.Widget {
|
|
|
166
167
|
override willHide(): void {
|
|
167
168
|
super.willHide();
|
|
168
169
|
this.#calibratedThrottlingSetting.removeChangeListener(this.#onCalibratedSettingChanged, this);
|
|
169
|
-
SDK.CPUThrottlingManager.
|
|
170
|
-
|
|
170
|
+
this.#cpuThrottlingManager.removeEventListener(SDK.CPUThrottlingManager.Events.RATE_CHANGED, this.#onOptionChange,
|
|
171
|
+
this);
|
|
171
172
|
}
|
|
172
173
|
|
|
173
174
|
#onOptionChange(): void {
|
|
174
|
-
this.#currentOption =
|
|
175
|
+
this.#currentOption = this.#cpuThrottlingManager.cpuThrottlingOption();
|
|
175
176
|
|
|
176
177
|
this.requestUpdate();
|
|
177
178
|
}
|
|
@@ -236,31 +236,31 @@ const UIStrings = {
|
|
|
236
236
|
*/
|
|
237
237
|
clearCurrentLog: 'Clear the current log',
|
|
238
238
|
/**
|
|
239
|
-
* @description Title for a page load
|
|
239
|
+
* @description Title for a page load subpart that measures the time between when the page load starts and the time when the first byte of the initial document is downloaded.
|
|
240
240
|
*/
|
|
241
241
|
timeToFirstByte: 'Time to first byte',
|
|
242
242
|
/**
|
|
243
|
-
* @description Title for a page load
|
|
243
|
+
* @description Title for a page load subpart that measures the time between when the first byte of the initial document is downloaded and when the request for the largest image content starts.
|
|
244
244
|
*/
|
|
245
245
|
resourceLoadDelay: 'Resource load delay',
|
|
246
246
|
/**
|
|
247
|
-
* @description Title for a page load
|
|
247
|
+
* @description Title for a page load subpart that measures the time between when the request for the largest image content starts and when it finishes.
|
|
248
248
|
*/
|
|
249
249
|
resourceLoadDuration: 'Resource load duration',
|
|
250
250
|
/**
|
|
251
|
-
* @description Title for a page load
|
|
251
|
+
* @description Title for a page load subpart that measures the time between when the request for the largest image content finishes and when the largest image element is rendered on the page.
|
|
252
252
|
*/
|
|
253
253
|
elementRenderDelay: 'Element render delay',
|
|
254
254
|
/**
|
|
255
|
-
* @description Title for a
|
|
255
|
+
* @description Title for a subpart during a user interaction that measures the time between when the interaction starts and when the browser starts running interaction handlers.
|
|
256
256
|
*/
|
|
257
257
|
inputDelay: 'Input delay',
|
|
258
258
|
/**
|
|
259
|
-
* @description Title for a
|
|
259
|
+
* @description Title for a subpart during a user interaction that measures the time between when the browser starts running interaction handlers and when the browser finishes running interaction handlers.
|
|
260
260
|
*/
|
|
261
261
|
processingDuration: 'Processing duration',
|
|
262
262
|
/**
|
|
263
|
-
* @description Title for a
|
|
263
|
+
* @description Title for a subpart during a user interaction that measures the time between when the browser finishes running interaction handlers and when the browser renders the next visual frame that shows the result of the interaction.
|
|
264
264
|
*/
|
|
265
265
|
presentationDelay: 'Presentation delay',
|
|
266
266
|
/**
|
|
@@ -276,11 +276,11 @@ const UIStrings = {
|
|
|
276
276
|
*/
|
|
277
277
|
showClsCluster: 'Go to worst layout shift cluster.',
|
|
278
278
|
/**
|
|
279
|
-
* @description Column header for table cell values representing the
|
|
279
|
+
* @description Column header for table cell values representing the subpart/component/stage/section of a larger duration.
|
|
280
280
|
*/
|
|
281
|
-
|
|
281
|
+
subpart: 'Subpart',
|
|
282
282
|
/**
|
|
283
|
-
* @description Column header for table cell values representing a
|
|
283
|
+
* @description Column header for table cell values representing a subpart duration (in milliseconds) that was measured in the developers local environment.
|
|
284
284
|
*/
|
|
285
285
|
duration: 'Local duration (ms)',
|
|
286
286
|
/**
|
|
@@ -328,7 +328,7 @@ export interface ViewOutput {
|
|
|
328
328
|
|
|
329
329
|
export type View = (input: ViewInput, output: ViewOutput, target: HTMLElement|DocumentFragment) => void;
|
|
330
330
|
|
|
331
|
-
function
|
|
331
|
+
function getLcpFieldSubparts(cruxManager: CrUXManager.CrUXManager): LiveMetrics.LcpValue['subparts']|null {
|
|
332
332
|
const ttfb =
|
|
333
333
|
cruxManager.getSelectedFieldMetricData('largest_contentful_paint_image_time_to_first_byte')?.percentiles?.p75;
|
|
334
334
|
const loadDelay =
|
|
@@ -484,9 +484,9 @@ function renderLcpCard(input: ViewInput): Lit.LitTemplate {
|
|
|
484
484
|
const fieldData = input.cruxManager.getSelectedFieldMetricData('largest_contentful_paint');
|
|
485
485
|
const nodeLink =
|
|
486
486
|
input.lcpValue?.nodeRef && PanelsCommon.DOMLinkifier.Linkifier.instance().linkify(input.lcpValue?.nodeRef);
|
|
487
|
-
const
|
|
487
|
+
const subparts = input.lcpValue?.subparts;
|
|
488
488
|
|
|
489
|
-
const
|
|
489
|
+
const fieldSubparts = getLcpFieldSubparts(input.cruxManager);
|
|
490
490
|
|
|
491
491
|
// clang-format off
|
|
492
492
|
return html`
|
|
@@ -496,11 +496,11 @@ function renderLcpCard(input: ViewInput): Lit.LitTemplate {
|
|
|
496
496
|
fieldValue: fieldData?.percentiles?.p75,
|
|
497
497
|
histogram: fieldData?.histogram,
|
|
498
498
|
warnings: input.lcpValue?.warnings,
|
|
499
|
-
|
|
500
|
-
[i18nString(UIStrings.timeToFirstByte),
|
|
501
|
-
[i18nString(UIStrings.resourceLoadDelay),
|
|
502
|
-
[i18nString(UIStrings.resourceLoadDuration),
|
|
503
|
-
[i18nString(UIStrings.elementRenderDelay),
|
|
499
|
+
subparts: subparts && [
|
|
500
|
+
[i18nString(UIStrings.timeToFirstByte), subparts.timeToFirstByte, fieldSubparts?.timeToFirstByte],
|
|
501
|
+
[i18nString(UIStrings.resourceLoadDelay), subparts.resourceLoadDelay, fieldSubparts?.resourceLoadDelay],
|
|
502
|
+
[i18nString(UIStrings.resourceLoadDuration), subparts.resourceLoadTime, fieldSubparts?.resourceLoadTime],
|
|
503
|
+
[i18nString(UIStrings.elementRenderDelay), subparts.elementRenderDelay, fieldSubparts?.elementRenderDelay],
|
|
504
504
|
],
|
|
505
505
|
})}>
|
|
506
506
|
${nodeLink ? html`
|
|
@@ -551,7 +551,7 @@ function renderClsCard(input: ViewInput): Lit.LitTemplate {
|
|
|
551
551
|
|
|
552
552
|
function renderInpCard(input: ViewInput): Lit.LitTemplate {
|
|
553
553
|
const fieldData = input.cruxManager.getSelectedFieldMetricData('interaction_to_next_paint');
|
|
554
|
-
const
|
|
554
|
+
const subparts = input.inpValue?.subparts;
|
|
555
555
|
const interaction = input.inpValue && input.interactions.get(input.inpValue.interactionId);
|
|
556
556
|
|
|
557
557
|
// clang-format off
|
|
@@ -562,10 +562,10 @@ function renderInpCard(input: ViewInput): Lit.LitTemplate {
|
|
|
562
562
|
fieldValue: fieldData?.percentiles?.p75,
|
|
563
563
|
histogram: fieldData?.histogram,
|
|
564
564
|
warnings: input.inpValue?.warnings,
|
|
565
|
-
|
|
566
|
-
[i18nString(UIStrings.inputDelay),
|
|
567
|
-
[i18nString(UIStrings.processingDuration),
|
|
568
|
-
[i18nString(UIStrings.presentationDelay),
|
|
565
|
+
subparts: subparts && [
|
|
566
|
+
[i18nString(UIStrings.inputDelay), subparts.inputDelay],
|
|
567
|
+
[i18nString(UIStrings.processingDuration), subparts.processingDuration],
|
|
568
|
+
[i18nString(UIStrings.presentationDelay), subparts.presentationDelay],
|
|
569
569
|
],
|
|
570
570
|
})}>
|
|
571
571
|
${interaction ? html`
|
|
@@ -873,30 +873,30 @@ function renderInteractionsLog(input: ViewInput, output: ViewOutput): Lit.LitTem
|
|
|
873
873
|
></devtools-icon>` : nothing}
|
|
874
874
|
<span class="interaction-duration">${metricValue}</span>
|
|
875
875
|
</summary>
|
|
876
|
-
<div class="
|
|
877
|
-
<div class="
|
|
878
|
-
<div role="columnheader">${i18nString(UIStrings.
|
|
876
|
+
<div class="subpart-table" role="table">
|
|
877
|
+
<div class="subpart-table-row subpart-table-header-row" role="row">
|
|
878
|
+
<div role="columnheader">${i18nString(UIStrings.subpart)}</div>
|
|
879
879
|
<div role="columnheader">
|
|
880
880
|
${interaction.longAnimationFrameTimings.length ? html`
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
881
|
+
<button
|
|
882
|
+
class="log-extra-details-button"
|
|
883
|
+
title=${i18nString(UIStrings.logToConsole)}
|
|
884
|
+
@click=${() => input.logExtraInteractionDetails(interaction)}
|
|
885
|
+
>${i18nString(UIStrings.duration)}</button>
|
|
886
|
+
` : i18nString(UIStrings.duration)}
|
|
887
887
|
</div>
|
|
888
888
|
</div>
|
|
889
|
-
<div class="
|
|
889
|
+
<div class="subpart-table-row" role="row">
|
|
890
890
|
<div role="cell">${i18nString(UIStrings.inputDelay)}</div>
|
|
891
|
-
<div role="cell">${Math.round(interaction.
|
|
891
|
+
<div role="cell">${Math.round(interaction.subparts.inputDelay)}</div>
|
|
892
892
|
</div>
|
|
893
|
-
<div class="
|
|
893
|
+
<div class="subpart-table-row" role="row">
|
|
894
894
|
<div role="cell">${i18nString(UIStrings.processingDuration)}</div>
|
|
895
|
-
<div role="cell">${Math.round(interaction.
|
|
895
|
+
<div role="cell">${Math.round(interaction.subparts.processingDuration)}</div>
|
|
896
896
|
</div>
|
|
897
|
-
<div class="
|
|
897
|
+
<div class="subpart-table-row" role="row">
|
|
898
898
|
<div role="cell">${i18nString(UIStrings.presentationDelay)}</div>
|
|
899
|
-
<div role="cell">${Math.round(interaction.
|
|
899
|
+
<div role="cell">${Math.round(interaction.subparts.presentationDelay)}</div>
|
|
900
900
|
</div>
|
|
901
901
|
</div>
|
|
902
902
|
</details>
|