chrome-devtools-frontend 1.0.1153166 → 1.0.1155899
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/config/gni/devtools_grd_files.gni +3 -2
- package/front_end/core/common/Revealer.ts +1 -1
- package/front_end/core/host/InspectorFrontendHostAPI.ts +2 -0
- package/front_end/core/host/UserMetrics.ts +32 -12
- package/front_end/core/platform/array-utilities.ts +25 -9
- package/front_end/core/sdk/ChildTargetManager.ts +2 -1
- package/front_end/core/sdk/FilmStripModel.ts +35 -25
- package/front_end/devtools_compatibility.js +2 -0
- package/front_end/entrypoints/lighthouse_worker/LighthouseWorkerService.ts +8 -2
- package/front_end/entrypoints/worker_app/worker_app.ts +0 -1
- package/front_end/generated/InspectorBackendCommands.js +2 -1
- package/front_end/generated/protocol.ts +36 -0
- package/front_end/models/bindings/PresentationConsoleMessageHelper.ts +90 -73
- package/front_end/models/issues_manager/IssuesManager.ts +5 -0
- package/front_end/models/issues_manager/SourceFrameIssuesManager.ts +31 -61
- package/front_end/models/issues_manager/StylesheetLoadingIssue.ts +69 -0
- package/front_end/models/issues_manager/descriptions/stylesheetLateImport.md +4 -0
- package/front_end/models/issues_manager/descriptions/stylesheetRequestFailed.md +3 -0
- package/front_end/models/issues_manager/issues_manager.ts +2 -0
- package/front_end/models/timeline_model/TimelineModel.ts +4 -0
- package/front_end/models/trace/ModelImpl.ts +1 -0
- package/front_end/models/trace/README.md +73 -17
- package/front_end/models/trace/handlers/NetworkRequestsHandler.ts +1 -1
- package/front_end/models/trace/handlers/RendererHandler.ts +33 -143
- package/front_end/models/trace/handlers/UserTimings.md +1 -1
- package/front_end/models/trace/types/TraceEvents.ts +3 -2
- package/front_end/panels/application/ApplicationPanelSidebar.ts +9 -6
- package/front_end/panels/application/PreloadingTreeElement.ts +25 -7
- package/front_end/panels/application/preloading/PreloadingView.ts +64 -31
- package/front_end/panels/application/preloading/components/UsedPreloadingView.ts +19 -9
- package/front_end/panels/console/ConsoleViewMessage.ts +14 -2
- package/front_end/panels/elements/ElementsPanel.ts +2 -3
- package/front_end/panels/elements/components/LayoutPane.ts +256 -60
- package/front_end/panels/elements/elements-legacy.ts +0 -3
- package/front_end/panels/elements/elements-meta.ts +10 -2
- package/front_end/panels/elements/elements.ts +0 -2
- package/front_end/panels/network/NetworkDataGridNode.ts +8 -0
- package/front_end/panels/network/NetworkLogView.ts +2 -1
- package/front_end/panels/network/NetworkPanel.ts +12 -1
- package/front_end/panels/recorder/components/ExtensionView.ts +1 -1
- package/front_end/panels/sources/DebuggerPlugin.ts +7 -4
- package/front_end/panels/sources/SourcesPanel.ts +1 -1
- package/front_end/panels/sources/components/BreakpointsView.ts +406 -89
- package/front_end/panels/sources/sources-meta.ts +13 -4
- package/front_end/panels/sources/sources.ts +0 -2
- package/front_end/panels/timeline/TimelineFlameChartDataProvider.ts +1 -1
- package/front_end/panels/timeline/TimelineFlameChartNetworkDataProvider.ts +106 -95
- package/front_end/panels/timeline/TimelineFlameChartView.ts +1 -1
- package/front_end/panels/timeline/TimelinePaintProfilerView.ts +5 -0
- package/front_end/panels/timeline/TimelinePanel.ts +8 -8
- package/front_end/ui/legacy/UIUtils.ts +1 -1
- package/front_end/ui/legacy/components/perf_ui/.eslintrc.js +18 -0
- package/front_end/ui/legacy/components/perf_ui/FlameChart.ts +5 -1
- package/front_end/ui/legacy/components/perf_ui/TimelineOverviewPane.ts +5 -2
- package/front_end/ui/legacy/components/utils/Linkifier.ts +10 -0
- package/package.json +1 -1
- package/scripts/build/generate_deprecations.py +3 -0
- package/front_end/panels/elements/LayoutSidebarPane.ts +0 -249
- package/front_end/panels/sources/BreakpointsSidebarPane.ts +0 -480
- package/front_end/ui/components/docs/layout_pane/basic.html +0 -25
- package/front_end/ui/components/docs/layout_pane/basic.ts +0 -78
@@ -188,10 +188,12 @@ class PreloadingUIUtils {
|
|
188
188
|
// PreloadingMoedl.onPrimaryPageChanged.
|
189
189
|
class PreloadingModelProxy implements SDK.TargetManager.SDKModelObserver<SDK.PreloadingModel.PreloadingModel> {
|
190
190
|
model: SDK.PreloadingModel.PreloadingModel;
|
191
|
-
private readonly view: PreloadingView;
|
191
|
+
private readonly view: PreloadingView|PreloadingResultView;
|
192
192
|
private readonly warningsView: PreloadingWarningsView;
|
193
193
|
|
194
|
-
constructor(
|
194
|
+
constructor(
|
195
|
+
model: SDK.PreloadingModel.PreloadingModel, view: PreloadingView|PreloadingResultView,
|
196
|
+
warningsView: PreloadingWarningsView) {
|
195
197
|
this.view = view;
|
196
198
|
this.warningsView = warningsView;
|
197
199
|
|
@@ -237,7 +239,6 @@ export class PreloadingView extends UI.Widget.VBox {
|
|
237
239
|
|
238
240
|
private readonly warningsContainer: HTMLDivElement;
|
239
241
|
private readonly warningsView = new PreloadingWarningsView();
|
240
|
-
private readonly hsplitUsedPreloading: UI.SplitWidget.SplitWidget;
|
241
242
|
private readonly hsplit: UI.SplitWidget.SplitWidget;
|
242
243
|
private readonly vsplitRuleSets: UI.SplitWidget.SplitWidget;
|
243
244
|
private readonly ruleSetGrid = new PreloadingComponents.RuleSetGrid.RuleSetGrid();
|
@@ -255,22 +256,19 @@ export class PreloadingView extends UI.Widget.VBox {
|
|
255
256
|
// this (VBox)
|
256
257
|
// +- warningsContainer
|
257
258
|
// +- PreloadingWarningsView
|
258
|
-
// +-
|
259
|
-
// +-
|
260
|
-
// +-
|
259
|
+
// +- hsplit
|
260
|
+
// +- vsplitRuleSets
|
261
|
+
// +- leftContainer
|
262
|
+
// +- RuleSetGrid
|
263
|
+
// +- rightContainer
|
264
|
+
// +- RuleSetDetailsReportView
|
265
|
+
// +- VBox
|
266
|
+
// +- toolbar (filtering)
|
267
|
+
// +- vsplitPreloadingAttempts
|
261
268
|
// +- leftContainer
|
262
|
-
// +-
|
269
|
+
// +- PreloadingGrid
|
263
270
|
// +- rightContainer
|
264
|
-
// +-
|
265
|
-
// +- VBox
|
266
|
-
// +- toolbar (filtering)
|
267
|
-
// +- vsplitPreloadingAttempts
|
268
|
-
// +- leftContainer
|
269
|
-
// +- PreloadingGrid
|
270
|
-
// +- rightContainer
|
271
|
-
// +- PreloadingDetailsReportView
|
272
|
-
// +- VBox
|
273
|
-
// + UsedPreloadingReportView
|
271
|
+
// +- PreloadingDetailsReportView
|
274
272
|
//
|
275
273
|
// - If an row of RuleSetGrid selected, RuleSetDetailsReportView shows details of it.
|
276
274
|
// - If not, RuleSetDetailsReportView hides.
|
@@ -308,19 +306,6 @@ export class PreloadingView extends UI.Widget.VBox {
|
|
308
306
|
);
|
309
307
|
this.hsplit.setSidebarWidget(this.vsplitRuleSets);
|
310
308
|
this.hsplit.setMainWidget(preloadingAttempts);
|
311
|
-
|
312
|
-
const usedPreloadingContainer = new UI.Widget.VBox();
|
313
|
-
usedPreloadingContainer.contentElement.appendChild(this.usedPreloading);
|
314
|
-
this.hsplitUsedPreloading = new UI.SplitWidget.SplitWidget(
|
315
|
-
/* isVertical */ false,
|
316
|
-
/* secondIsSidebar */ true,
|
317
|
-
/* settingName */ undefined,
|
318
|
-
/* defaultSidebarWidth */ undefined,
|
319
|
-
/* defaultSidebarHeight */ 50,
|
320
|
-
/* constraintsInDip */ undefined,
|
321
|
-
);
|
322
|
-
this.hsplitUsedPreloading.setMainWidget(this.hsplit);
|
323
|
-
this.hsplitUsedPreloading.setSidebarWidget(usedPreloadingContainer);
|
324
309
|
}
|
325
310
|
|
326
311
|
private makeVsplit(left: HTMLElement, right: HTMLElement): UI.SplitWidget.SplitWidget {
|
@@ -353,7 +338,7 @@ export class PreloadingView extends UI.Widget.VBox {
|
|
353
338
|
|
354
339
|
this.registerCSSFiles([emptyWidgetStyles, preloadingViewStyles]);
|
355
340
|
|
356
|
-
this.
|
341
|
+
this.hsplit.show(this.contentElement);
|
357
342
|
|
358
343
|
// Lazily initialize PreloadingModelProxy because this triggers a chain
|
359
344
|
//
|
@@ -476,6 +461,54 @@ export class PreloadingView extends UI.Widget.VBox {
|
|
476
461
|
}
|
477
462
|
}
|
478
463
|
|
464
|
+
export class PreloadingResultView extends UI.Widget.VBox {
|
465
|
+
private readonly modelProxy: PreloadingModelProxy;
|
466
|
+
|
467
|
+
private readonly warningsContainer: HTMLDivElement;
|
468
|
+
private readonly warningsView = new PreloadingWarningsView();
|
469
|
+
private readonly usedPreloading = new PreloadingComponents.UsedPreloadingView.UsedPreloadingView();
|
470
|
+
|
471
|
+
constructor(model: SDK.PreloadingModel.PreloadingModel) {
|
472
|
+
super(/* isWebComponent */ true, /* delegatesFocus */ false);
|
473
|
+
|
474
|
+
this.modelProxy = new PreloadingModelProxy(model, this, this.warningsView);
|
475
|
+
|
476
|
+
this.warningsContainer = document.createElement('div');
|
477
|
+
this.warningsContainer.classList.add('flex-none');
|
478
|
+
this.contentElement.insertBefore(this.warningsContainer, this.contentElement.firstChild);
|
479
|
+
this.warningsView.show(this.warningsContainer);
|
480
|
+
|
481
|
+
const usedPreloadingContainer = new UI.Widget.VBox();
|
482
|
+
usedPreloadingContainer.contentElement.appendChild(this.usedPreloading);
|
483
|
+
usedPreloadingContainer.show(this.contentElement);
|
484
|
+
}
|
485
|
+
|
486
|
+
override wasShown(): void {
|
487
|
+
super.wasShown();
|
488
|
+
|
489
|
+
this.registerCSSFiles([emptyWidgetStyles, preloadingViewStyles]);
|
490
|
+
|
491
|
+
// Lazily initialize PreloadingModelProxy because this triggers a chain
|
492
|
+
//
|
493
|
+
// PreloadingModelProxy.initialize()
|
494
|
+
// -> TargetManager.observeModels()
|
495
|
+
// -> PreloadingModelProxy.modelAdded()
|
496
|
+
// -> PreloadingResultView.render()
|
497
|
+
//
|
498
|
+
// , and PreloadingView.onModelAdded() requires all members are
|
499
|
+
// initialized. So, here is the best timing.
|
500
|
+
this.modelProxy.initialize();
|
501
|
+
}
|
502
|
+
|
503
|
+
render(): void {
|
504
|
+
this.usedPreloading.data = this.modelProxy.model.getPreloadingAttemptsOfPreviousPage().map(({value}) => value);
|
505
|
+
}
|
506
|
+
|
507
|
+
getUsedPreloadingForTest(): PreloadingComponents.UsedPreloadingView.UsedPreloadingView {
|
508
|
+
return this.usedPreloading;
|
509
|
+
}
|
510
|
+
}
|
511
|
+
|
479
512
|
export class PreloadingWarningsView extends UI.Widget.VBox {
|
480
513
|
constructor() {
|
481
514
|
super(/* isWebComponent */ false, /* delegatesFocus */ false);
|
@@ -7,9 +7,14 @@ import * as SDK from '../../../../core/sdk/sdk.js';
|
|
7
7
|
import * as Protocol from '../../../../generated/protocol.js';
|
8
8
|
import * as ComponentHelpers from '../../../../ui/components/helpers/helpers.js';
|
9
9
|
import * as Coordinator from '../../../../ui/components/render_coordinator/render_coordinator.js';
|
10
|
+
import * as ReportView from '../../../../ui/components/report_view/report_view.js';
|
10
11
|
import * as LitHtml from '../../../../ui/lit-html/lit-html.js';
|
11
12
|
|
12
13
|
const UIStrings = {
|
14
|
+
/**
|
15
|
+
*@description Title for the panel
|
16
|
+
*/
|
17
|
+
preloadingUsedForThisPage: 'Preloading used for this page',
|
13
18
|
/**
|
14
19
|
*@description Message that reports counts of prefetch that used for this page.
|
15
20
|
*@example {1} PH1
|
@@ -49,13 +54,14 @@ export class UsedPreloadingView extends HTMLElement {
|
|
49
54
|
used.filter(attempt => attempt.key.action === Protocol.Preload.SpeculationAction.Prefetch).length;
|
50
55
|
const prerenderCount = used.length - prefetchCount;
|
51
56
|
|
52
|
-
if (used.length === 0) {
|
53
|
-
LitHtml.render(LitHtml.nothing, this.#shadow, {host: this});
|
54
|
-
return;
|
55
|
-
}
|
56
|
-
|
57
57
|
let message = '';
|
58
|
-
|
58
|
+
|
59
|
+
if (used.length === 0) {
|
60
|
+
// TODO(https://crbug.com/1410709): Remake entire this view.
|
61
|
+
//
|
62
|
+
// For a while, we fill temporary string.
|
63
|
+
message = i18n.i18n.lockedString('No preloading was used for this page.');
|
64
|
+
} else if (prerenderCount > 0) {
|
59
65
|
message = i18nString(UIStrings.prerenderUsed);
|
60
66
|
} else if (prefetchCount > 0) {
|
61
67
|
message = i18nString(UIStrings.prefetchUsed, {PH1: prefetchCount});
|
@@ -64,9 +70,13 @@ export class UsedPreloadingView extends HTMLElement {
|
|
64
70
|
// Disabled until https://crbug.com/1079231 is fixed.
|
65
71
|
// clang-format off
|
66
72
|
LitHtml.render(LitHtml.html`
|
67
|
-
|
68
|
-
|
69
|
-
|
73
|
+
<${ReportView.ReportView.Report.litTagName} .data=${
|
74
|
+
{reportTitle: i18nString(UIStrings.preloadingUsedForThisPage)} as ReportView.ReportView.ReportData
|
75
|
+
}>
|
76
|
+
<${ReportView.ReportView.ReportSection.litTagName}>
|
77
|
+
${message}
|
78
|
+
</${ReportView.ReportView.ReportSection.litTagName}>
|
79
|
+
</${ReportView.ReportView.Report.litTagName}>
|
70
80
|
`, this.#shadow, {host: this});
|
71
81
|
// clang-format on
|
72
82
|
});
|
@@ -40,6 +40,7 @@ import * as Protocol from '../../generated/protocol.js';
|
|
40
40
|
import * as Bindings from '../../models/bindings/bindings.js';
|
41
41
|
import type * as IssuesManager from '../../models/issues_manager/issues_manager.js';
|
42
42
|
import * as Logs from '../../models/logs/logs.js';
|
43
|
+
import * as Host from '../../core/host/host.js';
|
43
44
|
import * as TextUtils from '../../models/text_utils/text_utils.js';
|
44
45
|
import * as Workspace from '../../models/workspace/workspace.js';
|
45
46
|
import * as CodeHighlighter from '../../ui/components/code_highlighter/code_highlighter.js';
|
@@ -466,6 +467,14 @@ export class ConsoleViewMessage implements ConsoleViewportElement {
|
|
466
467
|
return elements;
|
467
468
|
}
|
468
469
|
|
470
|
+
#getLinkifierMetric(): Host.UserMetrics.Action|undefined {
|
471
|
+
const request = Logs.NetworkLog.NetworkLog.requestForConsoleMessage(this.message);
|
472
|
+
if (request?.resourceType().isStyleSheet()) {
|
473
|
+
return Host.UserMetrics.Action.StyleSheetInitiatorLinkClicked;
|
474
|
+
}
|
475
|
+
return undefined;
|
476
|
+
}
|
477
|
+
|
469
478
|
protected buildMessageAnchor(): HTMLElement|null {
|
470
479
|
const runtimeModel = this.message.runtimeModel();
|
471
480
|
if (!runtimeModel) {
|
@@ -474,23 +483,26 @@ export class ConsoleViewMessage implements ConsoleViewportElement {
|
|
474
483
|
|
475
484
|
const linkify = ({stackFrameWithBreakpoint, scriptId, stackTrace, url, line, column}:
|
476
485
|
SDK.ConsoleModel.ConsoleMessage): HTMLElement|null => {
|
486
|
+
const userMetric = this.#getLinkifierMetric();
|
477
487
|
if (stackFrameWithBreakpoint) {
|
478
488
|
return this.linkifier.maybeLinkifyConsoleCallFrame(runtimeModel.target(), stackFrameWithBreakpoint, {
|
479
489
|
inlineFrameIndex: 0,
|
480
490
|
revealBreakpoint: true,
|
491
|
+
userMetric,
|
481
492
|
});
|
482
493
|
}
|
483
494
|
if (scriptId) {
|
484
495
|
return this.linkifier.linkifyScriptLocation(
|
485
496
|
runtimeModel.target(), scriptId, url || Platform.DevToolsPath.EmptyUrlString, line,
|
486
|
-
{columnNumber: column, inlineFrameIndex: 0});
|
497
|
+
{columnNumber: column, inlineFrameIndex: 0, userMetric});
|
487
498
|
}
|
488
499
|
if (stackTrace && stackTrace.callFrames.length) {
|
489
500
|
return this.linkifier.linkifyStackTraceTopFrame(runtimeModel.target(), stackTrace);
|
490
501
|
}
|
491
502
|
if (url && url !== 'undefined') {
|
492
503
|
return this.linkifier.linkifyScriptLocation(
|
493
|
-
runtimeModel.target(), /* scriptId */ null, url, line,
|
504
|
+
runtimeModel.target(), /* scriptId */ null, url, line,
|
505
|
+
{columnNumber: column, inlineFrameIndex: 0, userMetric});
|
494
506
|
}
|
495
507
|
return null;
|
496
508
|
};
|
@@ -57,7 +57,6 @@ import {ElementsTreeElementHighlighter} from './ElementsTreeElementHighlighter.j
|
|
57
57
|
import {ElementsTreeOutline} from './ElementsTreeOutline.js';
|
58
58
|
import {type MarkerDecorator} from './MarkerDecorator.js';
|
59
59
|
import {MetricsSidebarPane} from './MetricsSidebarPane.js';
|
60
|
-
import {LayoutSidebarPane} from './LayoutSidebarPane.js';
|
61
60
|
import {
|
62
61
|
Events as StylesSidebarPaneEvents,
|
63
62
|
StylesSidebarPane,
|
@@ -1030,7 +1029,7 @@ export class ElementsPanel extends UI.Panel.Panel implements UI.SearchableView.S
|
|
1030
1029
|
if (skippedInitialTabSelectedEvent) {
|
1031
1030
|
// We don't log the initially selected sidebar pane to UMA because
|
1032
1031
|
// it will skew the histogram heavily toward the Styles pane
|
1033
|
-
Host.userMetrics.
|
1032
|
+
Host.userMetrics.elementsSidebarTabShown(tabId);
|
1034
1033
|
} else {
|
1035
1034
|
skippedInitialTabSelectedEvent = true;
|
1036
1035
|
}
|
@@ -1151,7 +1150,7 @@ export class ElementsPanel extends UI.Panel.Panel implements UI.SearchableView.S
|
|
1151
1150
|
void treeElement.updateStyleAdorners();
|
1152
1151
|
}
|
1153
1152
|
|
1154
|
-
|
1153
|
+
void ElementsComponents.LayoutPane.LayoutPane.instance().render();
|
1155
1154
|
}
|
1156
1155
|
}
|
1157
1156
|
|
@@ -3,13 +3,15 @@
|
|
3
3
|
// found in the LICENSE file.
|
4
4
|
|
5
5
|
import * as Common from '../../../core/common/common.js';
|
6
|
+
import * as SDK from '../../../core/sdk/sdk.js';
|
6
7
|
import * as ComponentHelpers from '../../../ui/components/helpers/helpers.js';
|
7
8
|
import * as IconButton from '../../../ui/components/icon_button/icon_button.js';
|
9
|
+
import * as LegacyWrapper from '../../../ui/components/legacy_wrapper/legacy_wrapper.js';
|
10
|
+
import * as Coordinator from '../../../ui/components/render_coordinator/render_coordinator.js';
|
8
11
|
import * as UI from '../../../ui/legacy/legacy.js';
|
9
12
|
import * as LitHtml from '../../../ui/lit-html/lit-html.js';
|
10
13
|
|
11
14
|
import {type LayoutElement, type BooleanSetting, type EnumSetting, type Setting} from './LayoutPaneUtils.js';
|
12
|
-
|
13
15
|
import layoutPaneStyles from '../layoutPane.css.js';
|
14
16
|
import * as Input from '../../../ui/components/input/input.js';
|
15
17
|
import * as NodeText from '../../../ui/components/node_text/node_text.js';
|
@@ -65,15 +67,80 @@ export {LayoutElement};
|
|
65
67
|
|
66
68
|
const {render, html} = LitHtml;
|
67
69
|
|
68
|
-
|
69
|
-
|
70
|
-
|
70
|
+
const nodeToLayoutElement = (node: SDK.DOMModel.DOMNode): LayoutElement => {
|
71
|
+
const className = node.getAttribute('class');
|
72
|
+
const nodeId = node.id;
|
73
|
+
return {
|
74
|
+
id: nodeId,
|
75
|
+
color: '#000',
|
76
|
+
name: node.localName(),
|
77
|
+
domId: node.getAttribute('id'),
|
78
|
+
domClasses: className ? className.split(/\s+/).filter(s => Boolean(s)) : undefined,
|
79
|
+
enabled: false,
|
80
|
+
reveal: (): void => {
|
81
|
+
void Common.Revealer.reveal(node);
|
82
|
+
void node.scrollIntoView();
|
83
|
+
},
|
84
|
+
highlight: (): void => {
|
85
|
+
node.highlight();
|
86
|
+
},
|
87
|
+
hideHighlight: (): void => {
|
88
|
+
SDK.OverlayModel.OverlayModel.hideDOMNodeHighlight();
|
89
|
+
},
|
90
|
+
toggle: (_value: boolean): never => {
|
91
|
+
throw new Error('Not implemented');
|
92
|
+
},
|
93
|
+
setColor(_value: string): never {
|
94
|
+
throw new Error('Not implemented');
|
95
|
+
},
|
96
|
+
};
|
97
|
+
};
|
71
98
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
99
|
+
const gridNodesToElements = (nodes: SDK.DOMModel.DOMNode[]): LayoutElement[] => {
|
100
|
+
return nodes.map(node => {
|
101
|
+
const layoutElement = nodeToLayoutElement(node);
|
102
|
+
const nodeId = node.id;
|
103
|
+
return {
|
104
|
+
...layoutElement,
|
105
|
+
color: node.domModel().overlayModel().colorOfGridInPersistentOverlay(nodeId) || '#000',
|
106
|
+
enabled: node.domModel().overlayModel().isHighlightedGridInPersistentOverlay(nodeId),
|
107
|
+
toggle: (value: boolean): void => {
|
108
|
+
if (value) {
|
109
|
+
node.domModel().overlayModel().highlightGridInPersistentOverlay(nodeId);
|
110
|
+
} else {
|
111
|
+
node.domModel().overlayModel().hideGridInPersistentOverlay(nodeId);
|
112
|
+
}
|
113
|
+
},
|
114
|
+
setColor(value: string): void {
|
115
|
+
this.color = value;
|
116
|
+
node.domModel().overlayModel().setColorOfGridInPersistentOverlay(nodeId, value);
|
117
|
+
},
|
118
|
+
};
|
119
|
+
});
|
120
|
+
};
|
121
|
+
|
122
|
+
const flexContainerNodesToElements = (nodes: SDK.DOMModel.DOMNode[]): LayoutElement[] => {
|
123
|
+
return nodes.map(node => {
|
124
|
+
const layoutElement = nodeToLayoutElement(node);
|
125
|
+
const nodeId = node.id;
|
126
|
+
return {
|
127
|
+
...layoutElement,
|
128
|
+
color: node.domModel().overlayModel().colorOfFlexInPersistentOverlay(nodeId) || '#000',
|
129
|
+
enabled: node.domModel().overlayModel().isHighlightedFlexContainerInPersistentOverlay(nodeId),
|
130
|
+
toggle: (value: boolean): void => {
|
131
|
+
if (value) {
|
132
|
+
node.domModel().overlayModel().highlightFlexContainerInPersistentOverlay(nodeId);
|
133
|
+
} else {
|
134
|
+
node.domModel().overlayModel().hideFlexContainerInPersistentOverlay(nodeId);
|
135
|
+
}
|
136
|
+
},
|
137
|
+
setColor(value: string): void {
|
138
|
+
this.color = value;
|
139
|
+
node.domModel().overlayModel().setColorOfFlexInPersistentOverlay(nodeId, value);
|
140
|
+
},
|
141
|
+
};
|
142
|
+
});
|
143
|
+
};
|
77
144
|
|
78
145
|
interface HTMLInputElementEvent extends Event {
|
79
146
|
target: HTMLInputElement;
|
@@ -93,15 +160,21 @@ export interface LayoutPaneData {
|
|
93
160
|
flexContainerElements?: LayoutElement[];
|
94
161
|
}
|
95
162
|
|
96
|
-
|
163
|
+
const coordinator = Coordinator.RenderCoordinator.RenderCoordinator.instance();
|
164
|
+
let layoutPaneWrapperInstance: LegacyWrapper.LegacyWrapper.LegacyWrapper<UI.Widget.Widget, LayoutPane>;
|
165
|
+
|
166
|
+
export class LayoutPane extends LegacyWrapper.LegacyWrapper.WrappableComponent {
|
97
167
|
static readonly litTagName = LitHtml.literal`devtools-layout-pane`;
|
98
168
|
readonly #shadow = this.attachShadow({mode: 'open'});
|
99
169
|
#settings: Readonly<Setting[]> = [];
|
100
|
-
#
|
101
|
-
#
|
170
|
+
readonly #uaShadowDOMSetting: Common.Settings.Setting<boolean>;
|
171
|
+
#domModels: SDK.DOMModel.DOMModel[];
|
102
172
|
|
103
173
|
constructor() {
|
104
174
|
super();
|
175
|
+
this.#settings = this.#makeSettings();
|
176
|
+
this.#uaShadowDOMSetting = Common.Settings.Settings.instance().moduleSetting('showUAShadowDOM');
|
177
|
+
this.#domModels = [];
|
105
178
|
this.#shadow.adoptedStyleSheets = [
|
106
179
|
Input.checkboxStyles,
|
107
180
|
layoutPaneStyles,
|
@@ -109,11 +182,130 @@ export class LayoutPane extends HTMLElement {
|
|
109
182
|
];
|
110
183
|
}
|
111
184
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
185
|
+
static instance(): LayoutPane {
|
186
|
+
if (!layoutPaneWrapperInstance) {
|
187
|
+
layoutPaneWrapperInstance = LegacyWrapper.LegacyWrapper.legacyWrapper(UI.Widget.Widget, new LayoutPane());
|
188
|
+
}
|
189
|
+
|
190
|
+
return layoutPaneWrapperInstance.getComponent();
|
191
|
+
}
|
192
|
+
|
193
|
+
modelAdded(domModel: SDK.DOMModel.DOMModel): void {
|
194
|
+
const overlayModel = domModel.overlayModel();
|
195
|
+
overlayModel.addEventListener(SDK.OverlayModel.Events.PersistentGridOverlayStateChanged, this.render, this);
|
196
|
+
overlayModel.addEventListener(
|
197
|
+
SDK.OverlayModel.Events.PersistentFlexContainerOverlayStateChanged, this.render, this);
|
198
|
+
this.#domModels.push(domModel);
|
199
|
+
}
|
200
|
+
|
201
|
+
modelRemoved(domModel: SDK.DOMModel.DOMModel): void {
|
202
|
+
const overlayModel = domModel.overlayModel();
|
203
|
+
overlayModel.removeEventListener(SDK.OverlayModel.Events.PersistentGridOverlayStateChanged, this.render, this);
|
204
|
+
overlayModel.removeEventListener(
|
205
|
+
SDK.OverlayModel.Events.PersistentFlexContainerOverlayStateChanged, this.render, this);
|
206
|
+
this.#domModels = this.#domModels.filter(model => model !== domModel);
|
207
|
+
}
|
208
|
+
|
209
|
+
async #fetchNodesByStyle(style: {
|
210
|
+
name: string,
|
211
|
+
value: string,
|
212
|
+
}[]): Promise<SDK.DOMModel.DOMNode[]> {
|
213
|
+
const showUAShadowDOM = this.#uaShadowDOMSetting.get();
|
214
|
+
|
215
|
+
const nodes = [];
|
216
|
+
for (const domModel of this.#domModels) {
|
217
|
+
try {
|
218
|
+
const nodeIds = await domModel.getNodesByStyle(style, true /* pierce */);
|
219
|
+
for (const nodeId of nodeIds) {
|
220
|
+
const node = domModel.nodeForId(nodeId);
|
221
|
+
if (node !== null && (showUAShadowDOM || !node.ancestorUserAgentShadowRoot())) {
|
222
|
+
nodes.push(node);
|
223
|
+
}
|
224
|
+
}
|
225
|
+
} catch (error) {
|
226
|
+
// TODO(crbug.com/1167706): Sometimes in E2E tests the layout panel is updated after a DOM node
|
227
|
+
// has been removed. This causes an error that a node has not been found.
|
228
|
+
// We can skip nodes that resulted in an error.
|
229
|
+
console.warn(error);
|
230
|
+
}
|
231
|
+
}
|
232
|
+
|
233
|
+
return nodes;
|
234
|
+
}
|
235
|
+
|
236
|
+
async #fetchGridNodes(): Promise<SDK.DOMModel.DOMNode[]> {
|
237
|
+
return await this.#fetchNodesByStyle([{name: 'display', value: 'grid'}, {name: 'display', value: 'inline-grid'}]);
|
238
|
+
}
|
239
|
+
|
240
|
+
async #fetchFlexContainerNodes(): Promise<SDK.DOMModel.DOMNode[]> {
|
241
|
+
return await this.#fetchNodesByStyle([{name: 'display', value: 'flex'}, {name: 'display', value: 'inline-flex'}]);
|
242
|
+
}
|
243
|
+
|
244
|
+
#makeSettings(): Setting[] {
|
245
|
+
const settings = [];
|
246
|
+
for (const settingName of ['showGridLineLabels', 'showGridTrackSizes', 'showGridAreas', 'extendGridLines']) {
|
247
|
+
const setting = Common.Settings.Settings.instance().moduleSetting(settingName);
|
248
|
+
const settingValue = setting.get();
|
249
|
+
const settingType = setting.type();
|
250
|
+
if (!settingType) {
|
251
|
+
throw new Error('A setting provided to LayoutSidebarPane does not have a setting type');
|
252
|
+
}
|
253
|
+
if (settingType !== Common.Settings.SettingType.BOOLEAN && settingType !== Common.Settings.SettingType.ENUM) {
|
254
|
+
throw new Error('A setting provided to LayoutSidebarPane does not have a supported setting type');
|
255
|
+
}
|
256
|
+
const mappedSetting = {
|
257
|
+
type: settingType,
|
258
|
+
name: setting.name,
|
259
|
+
title: setting.title(),
|
260
|
+
};
|
261
|
+
if (typeof settingValue === 'boolean') {
|
262
|
+
settings.push({
|
263
|
+
...mappedSetting,
|
264
|
+
value: settingValue,
|
265
|
+
options: setting.options().map(opt => ({
|
266
|
+
...opt,
|
267
|
+
value: (opt.value as boolean),
|
268
|
+
})),
|
269
|
+
});
|
270
|
+
} else if (typeof settingValue === 'string') {
|
271
|
+
settings.push({
|
272
|
+
...mappedSetting,
|
273
|
+
value: settingValue,
|
274
|
+
options: setting.options().map(opt => ({
|
275
|
+
...opt,
|
276
|
+
value: (opt.value as string),
|
277
|
+
})),
|
278
|
+
});
|
279
|
+
}
|
280
|
+
}
|
281
|
+
return settings;
|
282
|
+
}
|
283
|
+
|
284
|
+
onSettingChanged(setting: string, value: string|boolean): void {
|
285
|
+
Common.Settings.Settings.instance().moduleSetting(setting).set(value);
|
286
|
+
}
|
287
|
+
|
288
|
+
override wasShown(): void {
|
289
|
+
for (const setting of this.#settings) {
|
290
|
+
Common.Settings.Settings.instance().moduleSetting(setting.name).addChangeListener(this.render, this);
|
291
|
+
}
|
292
|
+
for (const domModel of this.#domModels) {
|
293
|
+
this.modelRemoved(domModel);
|
294
|
+
}
|
295
|
+
this.#domModels = [];
|
296
|
+
SDK.TargetManager.TargetManager.instance().observeModels(SDK.DOMModel.DOMModel, this, {scoped: true});
|
297
|
+
UI.Context.Context.instance().addFlavorChangeListener(SDK.DOMModel.DOMNode, this.render, this);
|
298
|
+
this.#uaShadowDOMSetting.addChangeListener(this.render, this);
|
299
|
+
void this.render();
|
300
|
+
}
|
301
|
+
|
302
|
+
override willHide(): void {
|
303
|
+
for (const setting of this.#settings) {
|
304
|
+
Common.Settings.Settings.instance().moduleSetting(setting.name).removeChangeListener(this.render, this);
|
305
|
+
}
|
306
|
+
SDK.TargetManager.TargetManager.instance().unobserveModels(SDK.DOMModel.DOMModel, this);
|
307
|
+
UI.Context.Context.instance().removeFlavorChangeListener(SDK.DOMModel.DOMNode, this.render, this);
|
308
|
+
this.#uaShadowDOMSetting.removeChangeListener(this.render, this);
|
117
309
|
}
|
118
310
|
|
119
311
|
#onSummaryKeyDown(event: KeyboardEvent): void {
|
@@ -135,57 +327,61 @@ export class LayoutPane extends HTMLElement {
|
|
135
327
|
}
|
136
328
|
}
|
137
329
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
render(
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
</summary>
|
146
|
-
<div class="content-section">
|
147
|
-
<h3 class="content-section-title">${i18nString(UIStrings.overlayDisplaySettings)}</h3>
|
148
|
-
<div class="select-settings">
|
149
|
-
${this.#getEnumSettings().map(setting => this.#renderEnumSetting(setting))}
|
150
|
-
</div>
|
151
|
-
<div class="checkbox-settings">
|
152
|
-
${this.#getBooleanSettings().map(setting => this.#renderBooleanSetting(setting))}
|
153
|
-
</div>
|
154
|
-
</div>
|
155
|
-
${this.#gridElements ?
|
156
|
-
html`<div class="content-section">
|
157
|
-
<h3 class="content-section-title">
|
158
|
-
${this.#gridElements.length ? i18nString(UIStrings.gridOverlays) : i18nString(UIStrings.noGridLayoutsFoundOnThisPage)}
|
159
|
-
</h3>
|
160
|
-
${this.#gridElements.length ?
|
161
|
-
html`<div class="elements">
|
162
|
-
${this.#gridElements.map(element => this.#renderElement(element))}
|
163
|
-
</div>` : ''}
|
164
|
-
</div>` : ''}
|
165
|
-
</details>
|
166
|
-
${this.#flexContainerElements !== undefined ?
|
167
|
-
html`
|
330
|
+
override async render(): Promise<void> {
|
331
|
+
const gridElements = gridNodesToElements(await this.#fetchGridNodes());
|
332
|
+
const flexContainerElements = flexContainerNodesToElements(await this.#fetchFlexContainerNodes());
|
333
|
+
await coordinator.write('LayoutPane render', () => {
|
334
|
+
// Disabled until https://crbug.com/1079231 is fixed.
|
335
|
+
// clang-format off
|
336
|
+
render(html`
|
168
337
|
<details open>
|
169
338
|
<summary class="header" @keydown=${this.#onSummaryKeyDown}>
|
170
|
-
${i18nString(UIStrings.
|
339
|
+
${i18nString(UIStrings.grid)}
|
171
340
|
</summary>
|
172
|
-
|
341
|
+
<div class="content-section">
|
342
|
+
<h3 class="content-section-title">${i18nString(UIStrings.overlayDisplaySettings)}</h3>
|
343
|
+
<div class="select-settings">
|
344
|
+
${this.#getEnumSettings().map(setting => this.#renderEnumSetting(setting))}
|
345
|
+
</div>
|
346
|
+
<div class="checkbox-settings">
|
347
|
+
${this.#getBooleanSettings().map(setting => this.#renderBooleanSetting(setting))}
|
348
|
+
</div>
|
349
|
+
</div>
|
350
|
+
${gridElements ?
|
173
351
|
html`<div class="content-section">
|
174
352
|
<h3 class="content-section-title">
|
175
|
-
${
|
353
|
+
${gridElements.length ? i18nString(UIStrings.gridOverlays) : i18nString(UIStrings.noGridLayoutsFoundOnThisPage)}
|
176
354
|
</h3>
|
177
|
-
${
|
355
|
+
${gridElements.length ?
|
178
356
|
html`<div class="elements">
|
179
|
-
${
|
357
|
+
${gridElements.map(element => this.#renderElement(element))}
|
180
358
|
</div>` : ''}
|
181
359
|
</div>` : ''}
|
182
360
|
</details>
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
361
|
+
${flexContainerElements !== undefined ?
|
362
|
+
html`
|
363
|
+
<details open>
|
364
|
+
<summary class="header" @keydown=${this.#onSummaryKeyDown}>
|
365
|
+
${i18nString(UIStrings.flexbox)}
|
366
|
+
</summary>
|
367
|
+
${flexContainerElements ?
|
368
|
+
html`<div class="content-section">
|
369
|
+
<h3 class="content-section-title">
|
370
|
+
${flexContainerElements.length ? i18nString(UIStrings.flexboxOverlays) : i18nString(UIStrings.noFlexboxLayoutsFoundOnThisPage)}
|
371
|
+
</h3>
|
372
|
+
${flexContainerElements.length ?
|
373
|
+
html`<div class="elements">
|
374
|
+
${flexContainerElements.map(element => this.#renderElement(element))}
|
375
|
+
</div>` : ''}
|
376
|
+
</div>` : ''}
|
377
|
+
</details>
|
378
|
+
`
|
379
|
+
: ''}
|
380
|
+
`, this.#shadow, {
|
381
|
+
host: this,
|
382
|
+
});
|
383
|
+
// clang-format on
|
187
384
|
});
|
188
|
-
// clang-format on
|
189
385
|
}
|
190
386
|
|
191
387
|
#getEnumSettings(): EnumSetting[] {
|
@@ -198,12 +394,12 @@ export class LayoutPane extends HTMLElement {
|
|
198
394
|
|
199
395
|
#onBooleanSettingChange(setting: BooleanSetting, event: HTMLInputElementEvent): void {
|
200
396
|
event.preventDefault();
|
201
|
-
this.
|
397
|
+
this.onSettingChanged(setting.name, event.target.checked);
|
202
398
|
}
|
203
399
|
|
204
400
|
#onEnumSettingChange(setting: EnumSetting, event: HTMLInputElementEvent): void {
|
205
401
|
event.preventDefault();
|
206
|
-
this.
|
402
|
+
this.onSettingChanged(setting.name, event.target.value);
|
207
403
|
}
|
208
404
|
|
209
405
|
#onElementToggle(element: LayoutElement, event: HTMLInputElementEvent): void {
|
@@ -219,7 +415,7 @@ export class LayoutPane extends HTMLElement {
|
|
219
415
|
#onColorChange(element: LayoutElement, event: HTMLInputElementEvent): void {
|
220
416
|
event.preventDefault();
|
221
417
|
element.setColor(event.target.value);
|
222
|
-
this
|
418
|
+
void this.render();
|
223
419
|
}
|
224
420
|
|
225
421
|
#onElementMouseEnter(element: LayoutElement, event: HTMLInputElementEvent): void {
|
@@ -81,9 +81,6 @@ Elements.MarkerDecorator = ElementsModule.MarkerDecorator.MarkerDecorator;
|
|
81
81
|
|
82
82
|
Elements.GenericDecorator = ElementsModule.MarkerDecorator.GenericDecorator;
|
83
83
|
|
84
|
-
/** @constructor */
|
85
|
-
Elements.LayoutSidebarPane = ElementsModule.LayoutSidebarPane.LayoutSidebarPane;
|
86
|
-
|
87
84
|
/** @constructor */
|
88
85
|
Elements.MetricsSidebarPane = ElementsModule.MetricsSidebarPane.MetricsSidebarPane;
|
89
86
|
|