chrome-devtools-frontend 1.0.1025631 → 1.0.1027150

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 (57) hide show
  1. package/config/owner/LIGHTHOUSE_OWNERS +1 -1
  2. package/docs/triage_guidelines.md +1 -122
  3. package/front_end/core/common/ResourceType.ts +1 -0
  4. package/front_end/core/host/UserMetrics.ts +2 -1
  5. package/front_end/core/i18n/locales/en-US.json +24 -0
  6. package/front_end/core/i18n/locales/en-XL.json +24 -0
  7. package/front_end/core/root/Runtime.ts +1 -0
  8. package/front_end/core/sdk/DOMModel.ts +5 -0
  9. package/front_end/core/sdk/NetworkManager.ts +24 -3
  10. package/front_end/core/sdk/ResourceTreeModel.ts +1 -1
  11. package/front_end/core/sdk/SourceMap.ts +22 -6
  12. package/front_end/core/sdk/sdk-meta.ts +7 -0
  13. package/front_end/devtools_compatibility.js +1 -0
  14. package/front_end/entrypoints/lighthouse_worker/LighthouseWorkerService.ts +6 -3
  15. package/front_end/entrypoints/main/MainImpl.ts +5 -0
  16. package/front_end/generated/ARIAProperties.js +723 -723
  17. package/front_end/generated/InspectorBackendCommands.js +5 -3
  18. package/front_end/generated/SupportedCSSProperties.js +2843 -2832
  19. package/front_end/generated/protocol-mapping.d.ts +14 -0
  20. package/front_end/generated/protocol-proxy-api.d.ts +10 -0
  21. package/front_end/generated/protocol.ts +67 -0
  22. package/front_end/legacy_test_runner/sources_test_runner/DebuggerTestRunner.js +1 -1
  23. package/front_end/models/bindings/CompilerScriptMapping.ts +1 -1
  24. package/front_end/models/bindings/DebuggerLanguagePlugins.ts +38 -11
  25. package/front_end/models/bindings/DebuggerWorkspaceBinding.ts +7 -1
  26. package/front_end/models/bindings/IgnoreListManager.ts +35 -22
  27. package/front_end/models/issues_manager/descriptions/clientHintMetaTagAllowListInvalidOrigin.md +1 -1
  28. package/front_end/models/issues_manager/descriptions/clientHintMetaTagModifiedHTML.md +1 -1
  29. package/front_end/models/javascript_metadata/NativeFunctions.js +79 -67
  30. package/front_end/models/source_map_scopes/NamesResolver.ts +34 -0
  31. package/front_end/models/text_utils/TextRange.ts +8 -0
  32. package/front_end/models/timeline_model/TimelineModel.ts +18 -1
  33. package/front_end/panels/console/consoleView.css +4 -0
  34. package/front_end/panels/elements/ElementsTreeOutline.ts +18 -34
  35. package/front_end/panels/elements/TopLayerContainer.ts +51 -29
  36. package/front_end/panels/elements/components/ElementsBreadcrumbs.ts +45 -50
  37. package/front_end/panels/elements/elementsTreeOutline.css +1 -1
  38. package/front_end/panels/lighthouse/LighthouseController.ts +3 -0
  39. package/front_end/panels/lighthouse/LighthousePanel.ts +2 -0
  40. package/front_end/panels/network/NetworkDataGridNode.ts +1 -2
  41. package/front_end/panels/security/SecurityPanel.ts +52 -0
  42. package/front_end/panels/security/originView.css +1 -1
  43. package/front_end/panels/settings/FrameworkIgnoreListSettingsTab.ts +16 -0
  44. package/front_end/panels/sources/CallStackSidebarPane.ts +45 -16
  45. package/front_end/panels/sources/DebuggerPlugin.ts +8 -2
  46. package/front_end/panels/sources/callStackSidebarPane.css +15 -9
  47. package/front_end/panels/sources/navigatorTree.css +3 -3
  48. package/front_end/panels/sources/watchExpressionsSidebarPane.css +6 -0
  49. package/front_end/panels/timeline/TimelineFlameChartDataProvider.ts +1 -1
  50. package/front_end/ui/components/docs/building-ui-documentation/CreatingComponents.md +172 -1
  51. package/front_end/ui/components/panel_feedback/PreviewToggle.ts +15 -16
  52. package/front_end/ui/components/panel_feedback/previewToggle.css +13 -15
  53. package/front_end/ui/components/text_editor/TextEditor.ts +3 -0
  54. package/front_end/ui/legacy/ListControl.ts +4 -0
  55. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +13 -3
  56. package/front_end/ui/legacy/components/utils/JSPresentationUtils.ts +5 -4
  57. package/package.json +1 -1
@@ -78,9 +78,11 @@ Each component defines a `#render` method which is responsible for invoking LitH
78
78
  The `#render` method calls `LitHtml.render`, building up a template with `LitHtml.html`:
79
79
 
80
80
  ```ts
81
- LitHtml.render(LitHtml.html``, this.#shadow, {host: this});
81
+ LitHtml.render(LitHtml.html`<p>hello world!</p>`, this.#shadow, {host: this});
82
82
  ```
83
83
 
84
+ The third argument (`{host: this}`) tells LitHtml to automatically bind event listeners to the component. This can save you some painful debugging where event listeners do not have the right `this` reference; so we enforce the use of `{host: this}` via a custom ESLint rule.
85
+
84
86
  There is unfortunately a [clang-format bug](crbug.com/1079231) which makes its auto-formatting of LitHtml templates very unreadable, so we usually disable clang-format round the call:
85
87
 
86
88
  ```ts
@@ -110,3 +112,172 @@ void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#boundRender);
110
112
  ```
111
113
 
112
114
  > `scheduleRender` returns a promise; we use the `void` keyword to instruct TypeScript that we are purposefully not using `await` to wait for the promise to resolve. When scheduling a render it's most common to "fire and forget".
115
+
116
+ To summarise, most components start off life looking like:
117
+
118
+ ```ts
119
+ export class ElementsBreadcrumbs extends HTMLElement {
120
+ static readonly litTagName = LitHtml.literal`devtools-chrome-link`;
121
+ readonly #shadow = this.attachShadow({mode: 'open'});
122
+ readonly #boundRender = this.#render.bind(this);
123
+
124
+ #render(): void {
125
+ LitHtml.render(LitHtml.html``, this.#shadow, {host:this});
126
+ }
127
+ }
128
+ ```
129
+
130
+ ## Triggering a render
131
+
132
+ One of the most important aspects to understand about our component system is that **rendering does not happen automatically**.
133
+
134
+ To ensure we trigger a render once the component is added to the DOM, we can define [`connectedCallback`](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements#:~:text=lifecycle.%20For%20example%2C-,connectedCallback,-is%20invoked%20each):
135
+
136
+ ```ts
137
+ export class ElementsBreadcrumbs extends HTMLElement {
138
+ static readonly litTagName = LitHtml.literal`devtools-chrome-link`;
139
+ readonly #shadow = this.attachShadow({mode: 'open'});
140
+ readonly #boundRender = this.#render.bind(this);
141
+
142
+ connectedCallback(): void {
143
+ void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#boundRender);
144
+ }
145
+
146
+ #render(): void {
147
+ LitHtml.render(LitHtml.html``, this.#shadow, {host:this});
148
+ }
149
+ }
150
+ ```
151
+
152
+ ## Passing data into a component
153
+
154
+ Most of our components will require data that is passed into them. For example, `ElementsBreadcrumbs` takes in an array of `DOMNode` objects.
155
+
156
+ To provide a component some data, we define a `data` setter. This setter takes an object with any data the component requires. This object should have a TypeScript interface defined for it:
157
+
158
+ ```ts
159
+ export interface ElementsBreadcrumbsData {
160
+ selectedNode: DOMNode|null;
161
+ crumbs: DOMNode[];
162
+ }
163
+
164
+ export class ElementsBreadcrumbs extends HTMLElement {
165
+ // ...
166
+ #crumbs: DOMNode[] = [];
167
+ #selectedNode: DOMNode|null = null;
168
+
169
+ set data(data: ElementsBreadcrumbsData) {
170
+ this.#crumbs = data.crumbs;
171
+ this.#selectedNode = data.selectedNode;
172
+ void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#boundRender);
173
+ }
174
+ }
175
+ ```
176
+
177
+ ## Rendering components
178
+
179
+ Now we know how to create components that can take data, let's render them!
180
+
181
+ ### From a DevTools Widget
182
+
183
+ If you are rendering a component from the DevTools widget system, you should instantiate the component, pass any `data` to it, and then append it into the DOM:
184
+
185
+ ```ts
186
+ // Within a Widget
187
+ const breadcrumbs = new ElementsBreadcrumbs();
188
+ breadcrumbs.data = {selectedNode: node, crumbs: [...]};
189
+ this.appendChild(breadcrumbs);
190
+ ```
191
+
192
+ ### From another component
193
+
194
+ If you are rendering a component from within another component, render it within the call to `LitHtml.html` and use the static `litTagName` property:
195
+
196
+ ```ts
197
+ // Within some component
198
+ LitHtml.render(LitHtml.html`
199
+ <${ElementsBreadcrumbs.litTagName}></${ElementsBreadcrumbs>
200
+ `, this.#shadow, {host: this});
201
+ ```
202
+
203
+ To pass data, we use [LitHtml's dot syntax](https://lit.dev/docs/templates/expressions/#property-expressions) to set the `data` property (and invoke our `set data` setter):
204
+
205
+ ```ts
206
+ // Within some component
207
+ LitHtml.render(LitHtml.html`
208
+ <${ElementsBreadcrumbs.litTagName} .data=${{
209
+ selectedNode: node,
210
+ crumbs: [...],
211
+ }}>
212
+ </${ElementsBreadcrumbs>
213
+ `, this.#shadow, {host: this});
214
+ ```
215
+
216
+ To enforce some type safety, we also use TypeScript's `as` keyword to force the compiler to type-check the `data` object against the interface:
217
+
218
+ ```ts
219
+ // Within some component
220
+ LitHtml.render(LitHtml.html`
221
+ <${ElementsBreadcrumbs.litTagName} .data=${{
222
+ selectedNode: node,
223
+ crumbs: [...],
224
+ } as ElementsBreadcrumbsData}>
225
+ </${ElementsBreadcrumbs>
226
+ `, this.#shadow, {host: this});
227
+ ```
228
+
229
+ This type-checking requirement is enforced by an ESLint rule.
230
+
231
+ ## Performance concerns with data passing
232
+
233
+ The approach of `set data(data)` was chosen because:
234
+
235
+ 1. It requires few lines of code.
236
+ 2. It provides some form of type safety via the `as FooData` check.
237
+ 3. At the time we didn't have a solution for scheduled and batched renders, and didn't want multiple setters to trigger multiple unnecessary renders.
238
+
239
+ However, using `set data(data)` does come with some negative performance costs:
240
+
241
+ 1. LitHtml will always think the value has changed, because it's an object. If a component renders twice with `.data=${{name: 'Jack'}}`, Lit will think that the value has changed because it's a new object, even though we can see it's holding the same data.
242
+ 2. This approach causes these objects to be created (and subsequently garbage collected) on/after each render.
243
+
244
+ For most components in DevTools, these trade-offs are acceptable, and we prefer the type-safety of `set data` and take the usually imperceivable performance hit. However, in rare circumstances, this performance hit is noticeable. A good example of this is in Performance Insights, where we have to constantly re-render components as the user scrolls through their performance timeline. We noticed that this caused a large amount of garbage collection from all the objects being created per-render and then immediately disposed.
245
+
246
+ For these situations, we can instead move to an approach where we set properties individually. We still define the interface as before, and then define an individual setter for each property:
247
+
248
+ ```ts
249
+ interface ElementsBreadcrumbsData {
250
+ selectedNode: DOMNode|null;
251
+ crumbs: DOMNode[];
252
+ }
253
+
254
+ class ElementsBreadcrumbs extends HTMLElement {
255
+ #crumbs: DOMNode[] = [];
256
+ #selectedNode: DOMNode|null = null;
257
+
258
+ set crumbs(crumbs: ElementsBreadcrumbsData['crumbs']) {
259
+ this.#crumbs = crumbs;
260
+ void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#boundRender);
261
+ }
262
+
263
+ set selectedNode(selectedNode: ElementsBreadcrumbsData['selectedNode']) {
264
+ this.#selectedNode = selectedNode;
265
+ void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#boundRender);
266
+ }
267
+ }
268
+ ```
269
+
270
+ Rendering this component within another Lit component would now be done like so:
271
+
272
+ ```ts
273
+ // Within some component
274
+ LitHtml.render(LitHtml.html`
275
+ <${ElementsBreadcrumbs.litTagName} .crumbs=${[...]} .selectedNode=${node}>
276
+ </${ElementsBreadcrumbs>
277
+ `, this.#shadow, {host: this});
278
+ ```
279
+
280
+ This solution is more performant, but less type-safe as TypeScript has no means of checking those values. This is something we may rectify using the `as` pattern, but for now it's preferred to use the `set data` method by default.
281
+
282
+
283
+
@@ -72,23 +72,22 @@ export class PreviewToggle extends HTMLElement {
72
72
  render(
73
73
  html`
74
74
  <div class="container">
75
- <div class="checkbox-line">
76
- <label class="experiment-preview">
77
- <input type="checkbox" ?checked=${checked} @change=${this.#checkboxChanged} aria-label=${this.#name}/>
78
- <${IconButton.Icon.Icon.litTagName} .data=${{
79
- iconName: 'ic_preview_feature',
80
- width: '16px',
81
- height: '16px',
82
- color: 'var(--color-text-secondary)',
83
- } as IconButton.Icon.IconData}>
84
- </${IconButton.Icon.Icon.litTagName}>${this.#name}
85
- </label>
86
- ${this.#feedbackURL && !this.#helperText
87
- ? html`<div class="feedback"><x-link class="x-link" href=${this.#feedbackURL}>${i18nString(UIStrings.shortFeedbackLink)}</x-link></div>`
88
- : nothing}
89
- </div>
75
+ <label class="experiment-preview">
76
+ <input type="checkbox" ?checked=${checked} @change=${this.#checkboxChanged} aria-label=${this.#name}/>
77
+ <${IconButton.Icon.Icon.litTagName} .data=${{
78
+ iconName: 'ic_preview_feature',
79
+ width: '16px',
80
+ height: '16px',
81
+ color: 'var(--color-text-secondary)',
82
+ } as IconButton.Icon.IconData}>
83
+ </${IconButton.Icon.Icon.litTagName}>${this.#name}
84
+ </label>
85
+ <div class="spacer"></div>
86
+ ${this.#feedbackURL && !this.#helperText
87
+ ? html`<div class="feedback"><x-link class="x-link" href=${this.#feedbackURL}>${i18nString(UIStrings.shortFeedbackLink)}</x-link></div>`
88
+ : nothing}
90
89
  ${this.#learnMoreURL
91
- ? html`<x-link class="x-link" href=${this.#learnMoreURL}>${i18nString(UIStrings.learnMoreLink)}</x-link>`
90
+ ? html`<div class="learn-more"><x-link class="x-link" href=${this.#learnMoreURL}>${i18nString(UIStrings.learnMoreLink)}</x-link></div>`
92
91
  : nothing}
93
92
  <div class="helper">
94
93
  ${this.#helperText && this.#feedbackURL
@@ -8,35 +8,33 @@
8
8
  display: block;
9
9
  }
10
10
 
11
- .experiment-preview {
11
+ .container {
12
+ display: flex;
13
+ flex-wrap: wrap;
14
+ padding: 4px;
15
+ }
16
+
17
+ .experiment-preview,
18
+ .feedback,
19
+ .learn-more {
12
20
  display: flex;
13
21
  align-items: center;
14
22
  }
15
23
 
16
24
  .helper {
25
+ flex-basis: 100%;
17
26
  text-align: center;
18
27
  font-style: italic;
19
28
  }
20
29
 
21
- .feedback {
22
- text-align: right;
23
- }
24
-
25
- .checkbox-line {
26
- display: flex;
27
- justify-content: space-between;
28
- align-items: center;
29
- flex-wrap: wrap;
30
- }
31
-
32
- .container {
33
- padding: 4px;
30
+ .spacer {
31
+ flex: 1;
34
32
  }
35
33
 
36
34
  .x-link {
37
35
  color: var(--color-primary);
38
36
  text-decoration-line: underline;
39
- margin: 4px;
37
+ margin: 0 4px;
40
38
  }
41
39
 
42
40
  .feedback .x-link {
@@ -105,6 +105,9 @@ export class TextEditor extends HTMLElement {
105
105
  connectedCallback(): void {
106
106
  if (!this.#activeEditor) {
107
107
  this.#createEditor();
108
+ } else {
109
+ this.#activeEditor.scrollDOM.scrollTop = this.#lastScrollPos.top;
110
+ this.#activeEditor.scrollDOM.scrollLeft = this.#lastScrollPos.left;
108
111
  }
109
112
  }
110
113
 
@@ -428,6 +428,10 @@ export class ListControl<T> {
428
428
  }
429
429
  if (newElement) {
430
430
  ARIAUtils.setSelected(newElement, true);
431
+ const text = newElement.textContent;
432
+ if (text) {
433
+ ARIAUtils.alert(text);
434
+ }
431
435
  }
432
436
  ARIAUtils.setActiveDescendant(this.element, newElement);
433
437
  }
@@ -30,6 +30,7 @@
30
30
 
31
31
  import * as Common from '../../../../core/common/common.js';
32
32
  import type * as Components from '../utils/utils.js';
33
+ import * as Root from '../../../../core/root/root.js';
33
34
  import * as Host from '../../../../core/host/host.js';
34
35
  import * as i18n from '../../../../core/i18n/i18n.js';
35
36
  import * as LinearMemoryInspector from '../../../components/linear_memory_inspector/linear_memory_inspector.js';
@@ -222,8 +223,12 @@ export class ObjectPropertiesSection extends UI.TreeOutline.TreeOutlineInShadow
222
223
  return;
223
224
  }
224
225
 
226
+ const includedWebIdlTypes = webIdlType.includes?.map(className => domPinnedProperties[className]) ?? [];
227
+ const includedWebIdlProps = includedWebIdlTypes.flatMap(webIdlType => Object.entries(webIdlType.props ?? {}));
228
+ const webIdlProps = {...webIdlType.props, ...Object.fromEntries(includedWebIdlProps)};
229
+
225
230
  for (const property of properties) {
226
- const webIdlProperty = webIdlType?.props?.[property.name];
231
+ const webIdlProperty = webIdlProps[property.name];
227
232
  if (webIdlProperty) {
228
233
  property.webIdl = {info: webIdlProperty};
229
234
  }
@@ -1118,10 +1123,12 @@ export class ObjectPropertyTreeElement extends UI.TreeOutline.TreeElement {
1118
1123
  this.expandedValueElement = this.createExpandedValueElement(this.property.value);
1119
1124
  }
1120
1125
 
1126
+ const experiment = Root.Runtime.experiments.isEnabled(Root.Runtime.ExperimentName.IMPORTANT_DOM_PROPERTIES);
1127
+
1121
1128
  let adorner: Element|string = '';
1122
1129
  let container: Element;
1123
1130
 
1124
- if (this.property.webIdl?.applicable) {
1131
+ if (this.property.webIdl?.applicable && experiment) {
1125
1132
  const icon = new IconButton.Icon.Icon();
1126
1133
  icon.data = {
1127
1134
  iconName: 'star_outline',
@@ -1148,7 +1155,10 @@ export class ObjectPropertyTreeElement extends UI.TreeOutline.TreeElement {
1148
1155
  this.listItemElement.removeChildren();
1149
1156
  this.rowContainer = (container as HTMLElement);
1150
1157
  this.listItemElement.appendChild(this.rowContainer);
1151
- this.listItemElement.dataset.webidl = this.property.webIdl?.applicable ? 'true' : 'false';
1158
+
1159
+ if (experiment) {
1160
+ this.listItemElement.dataset.webidl = this.property.webIdl?.applicable ? 'true' : 'false';
1161
+ }
1152
1162
  }
1153
1163
 
1154
1164
  private updatePropertyPath(): void {
@@ -70,7 +70,7 @@ function populateContextMenu(link: Element, event: Event): void {
70
70
  const uiLocation = Linkifier.uiLocation(link);
71
71
  if (uiLocation &&
72
72
  Bindings.IgnoreListManager.IgnoreListManager.instance().canIgnoreListUISourceCode(uiLocation.uiSourceCode)) {
73
- if (Bindings.IgnoreListManager.IgnoreListManager.instance().isIgnoreListedUISourceCode(uiLocation.uiSourceCode)) {
73
+ if (Bindings.IgnoreListManager.IgnoreListManager.instance().isUserIgnoreListedURL(uiLocation.uiSourceCode.url())) {
74
74
  contextMenu.debugSection().appendItem(
75
75
  i18nString(UIStrings.removeFromIgnore),
76
76
  () => Bindings.IgnoreListManager.IgnoreListManager.instance().unIgnoreListUISourceCode(
@@ -127,8 +127,8 @@ export function buildStackTraceRows(
127
127
  // TODO(crbug.com/1183325): fix race condition with uiLocation still being null here
128
128
  const uiLocation = Linkifier.uiLocation(link);
129
129
  if (uiLocation &&
130
- Bindings.IgnoreListManager.IgnoreListManager.instance().isIgnoreListedUISourceCode(
131
- uiLocation.uiSourceCode)) {
130
+ Bindings.IgnoreListManager.IgnoreListManager.instance().isUserIgnoreListedURL(
131
+ uiLocation.uiSourceCode.url())) {
132
132
  ignoreListHide = true;
133
133
  }
134
134
  // Linkifier is using a workaround with the 'zero width space' (\u200b).
@@ -175,7 +175,8 @@ function updateHiddenRows(
175
175
  if ('link' in row && row.link) {
176
176
  const uiLocation = Linkifier.uiLocation(row.link);
177
177
  if (uiLocation &&
178
- Bindings.IgnoreListManager.IgnoreListManager.instance().isIgnoreListedUISourceCode(uiLocation.uiSourceCode)) {
178
+ Bindings.IgnoreListManager.IgnoreListManager.instance().isUserIgnoreListedURL(
179
+ uiLocation.uiSourceCode.url())) {
179
180
  row.ignoreListHide = true;
180
181
  }
181
182
  if (row.rowCountHide || row.ignoreListHide) {
package/package.json CHANGED
@@ -55,5 +55,5 @@
55
55
  "unittest": "scripts/test/run_unittests.py --no-text-coverage",
56
56
  "watch": "vpython third_party/node/node.py --output scripts/watch_build.js"
57
57
  },
58
- "version": "1.0.1025631"
58
+ "version": "1.0.1027150"
59
59
  }