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.
Files changed (61) hide show
  1. package/config/gni/devtools_grd_files.gni +3 -2
  2. package/front_end/core/common/Revealer.ts +1 -1
  3. package/front_end/core/host/InspectorFrontendHostAPI.ts +2 -0
  4. package/front_end/core/host/UserMetrics.ts +32 -12
  5. package/front_end/core/platform/array-utilities.ts +25 -9
  6. package/front_end/core/sdk/ChildTargetManager.ts +2 -1
  7. package/front_end/core/sdk/FilmStripModel.ts +35 -25
  8. package/front_end/devtools_compatibility.js +2 -0
  9. package/front_end/entrypoints/lighthouse_worker/LighthouseWorkerService.ts +8 -2
  10. package/front_end/entrypoints/worker_app/worker_app.ts +0 -1
  11. package/front_end/generated/InspectorBackendCommands.js +2 -1
  12. package/front_end/generated/protocol.ts +36 -0
  13. package/front_end/models/bindings/PresentationConsoleMessageHelper.ts +90 -73
  14. package/front_end/models/issues_manager/IssuesManager.ts +5 -0
  15. package/front_end/models/issues_manager/SourceFrameIssuesManager.ts +31 -61
  16. package/front_end/models/issues_manager/StylesheetLoadingIssue.ts +69 -0
  17. package/front_end/models/issues_manager/descriptions/stylesheetLateImport.md +4 -0
  18. package/front_end/models/issues_manager/descriptions/stylesheetRequestFailed.md +3 -0
  19. package/front_end/models/issues_manager/issues_manager.ts +2 -0
  20. package/front_end/models/timeline_model/TimelineModel.ts +4 -0
  21. package/front_end/models/trace/ModelImpl.ts +1 -0
  22. package/front_end/models/trace/README.md +73 -17
  23. package/front_end/models/trace/handlers/NetworkRequestsHandler.ts +1 -1
  24. package/front_end/models/trace/handlers/RendererHandler.ts +33 -143
  25. package/front_end/models/trace/handlers/UserTimings.md +1 -1
  26. package/front_end/models/trace/types/TraceEvents.ts +3 -2
  27. package/front_end/panels/application/ApplicationPanelSidebar.ts +9 -6
  28. package/front_end/panels/application/PreloadingTreeElement.ts +25 -7
  29. package/front_end/panels/application/preloading/PreloadingView.ts +64 -31
  30. package/front_end/panels/application/preloading/components/UsedPreloadingView.ts +19 -9
  31. package/front_end/panels/console/ConsoleViewMessage.ts +14 -2
  32. package/front_end/panels/elements/ElementsPanel.ts +2 -3
  33. package/front_end/panels/elements/components/LayoutPane.ts +256 -60
  34. package/front_end/panels/elements/elements-legacy.ts +0 -3
  35. package/front_end/panels/elements/elements-meta.ts +10 -2
  36. package/front_end/panels/elements/elements.ts +0 -2
  37. package/front_end/panels/network/NetworkDataGridNode.ts +8 -0
  38. package/front_end/panels/network/NetworkLogView.ts +2 -1
  39. package/front_end/panels/network/NetworkPanel.ts +12 -1
  40. package/front_end/panels/recorder/components/ExtensionView.ts +1 -1
  41. package/front_end/panels/sources/DebuggerPlugin.ts +7 -4
  42. package/front_end/panels/sources/SourcesPanel.ts +1 -1
  43. package/front_end/panels/sources/components/BreakpointsView.ts +406 -89
  44. package/front_end/panels/sources/sources-meta.ts +13 -4
  45. package/front_end/panels/sources/sources.ts +0 -2
  46. package/front_end/panels/timeline/TimelineFlameChartDataProvider.ts +1 -1
  47. package/front_end/panels/timeline/TimelineFlameChartNetworkDataProvider.ts +106 -95
  48. package/front_end/panels/timeline/TimelineFlameChartView.ts +1 -1
  49. package/front_end/panels/timeline/TimelinePaintProfilerView.ts +5 -0
  50. package/front_end/panels/timeline/TimelinePanel.ts +8 -8
  51. package/front_end/ui/legacy/UIUtils.ts +1 -1
  52. package/front_end/ui/legacy/components/perf_ui/.eslintrc.js +18 -0
  53. package/front_end/ui/legacy/components/perf_ui/FlameChart.ts +5 -1
  54. package/front_end/ui/legacy/components/perf_ui/TimelineOverviewPane.ts +5 -2
  55. package/front_end/ui/legacy/components/utils/Linkifier.ts +10 -0
  56. package/package.json +1 -1
  57. package/scripts/build/generate_deprecations.py +3 -0
  58. package/front_end/panels/elements/LayoutSidebarPane.ts +0 -249
  59. package/front_end/panels/sources/BreakpointsSidebarPane.ts +0 -480
  60. package/front_end/ui/components/docs/layout_pane/basic.html +0 -25
  61. 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(model: SDK.PreloadingModel.PreloadingModel, view: PreloadingView, warningsView: PreloadingWarningsView) {
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
- // +- hsplitUsedPreloading
259
- // +- hsplit
260
- // +- vsplitRuleSets
259
+ // +- hsplit
260
+ // +- vsplitRuleSets
261
+ // +- leftContainer
262
+ // +- RuleSetGrid
263
+ // +- rightContainer
264
+ // +- RuleSetDetailsReportView
265
+ // +- VBox
266
+ // +- toolbar (filtering)
267
+ // +- vsplitPreloadingAttempts
261
268
  // +- leftContainer
262
- // +- RuleSetGrid
269
+ // +- PreloadingGrid
263
270
  // +- rightContainer
264
- // +- RuleSetDetailsReportView
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.hsplitUsedPreloading.show(this.contentElement);
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
- if (prerenderCount > 0) {
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
- <div>
68
- <p>${message}</p>
69
- </div>
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, {columnNumber: column, inlineFrameIndex: 0});
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.sidebarPaneShown(tabId);
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
- LayoutSidebarPane.instance().update();
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
- export class SettingChangedEvent extends Event {
69
- static readonly eventName = 'settingchanged';
70
- data: {setting: string, value: string|boolean};
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
- constructor(setting: string, value: string|boolean) {
73
- super(SettingChangedEvent.eventName, {});
74
- this.data = {setting, value};
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
- export class LayoutPane extends HTMLElement {
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
- #gridElements: Readonly<LayoutElement[]> = [];
101
- #flexContainerElements?: Readonly<LayoutElement[]> = [];
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
- set data(data: LayoutPaneData) {
113
- this.#settings = data.settings;
114
- this.#gridElements = data.gridElements;
115
- this.#flexContainerElements = data.flexContainerElements;
116
- this.#render();
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
- #render(): void {
139
- // Disabled until https://crbug.com/1079231 is fixed.
140
- // clang-format off
141
- render(html`
142
- <details open>
143
- <summary class="header" @keydown=${this.#onSummaryKeyDown}>
144
- ${i18nString(UIStrings.grid)}
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.flexbox)}
339
+ ${i18nString(UIStrings.grid)}
171
340
  </summary>
172
- ${this.#flexContainerElements ?
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
- ${this.#flexContainerElements.length ? i18nString(UIStrings.flexboxOverlays) : i18nString(UIStrings.noFlexboxLayoutsFoundOnThisPage)}
353
+ ${gridElements.length ? i18nString(UIStrings.gridOverlays) : i18nString(UIStrings.noGridLayoutsFoundOnThisPage)}
176
354
  </h3>
177
- ${this.#flexContainerElements.length ?
355
+ ${gridElements.length ?
178
356
  html`<div class="elements">
179
- ${this.#flexContainerElements.map(element => this.#renderElement(element))}
357
+ ${gridElements.map(element => this.#renderElement(element))}
180
358
  </div>` : ''}
181
359
  </div>` : ''}
182
360
  </details>
183
- `
184
- : ''}
185
- `, this.#shadow, {
186
- host: this,
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.dispatchEvent(new SettingChangedEvent(setting.name, event.target.checked));
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.dispatchEvent(new SettingChangedEvent(setting.name, event.target.value));
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.#render();
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