chrome-devtools-frontend 1.0.948295 → 1.0.949424

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 (53) hide show
  1. package/config/gni/all_devtools_files.gni +5 -0
  2. package/config/gni/devtools_grd_files.gni +5 -6
  3. package/front_end/core/i18n/locales/en-US.json +36 -30
  4. package/front_end/core/i18n/locales/en-XL.json +36 -30
  5. package/front_end/core/root/Runtime.ts +1 -0
  6. package/front_end/core/sdk/AccessibilityModel.ts +7 -7
  7. package/front_end/core/sdk/RemoteObject.ts +15 -1
  8. package/front_end/entrypoints/devtools_app/devtools_app.ts +0 -1
  9. package/front_end/entrypoints/visibility.gni +3 -1
  10. package/front_end/entrypoints/worker_app/worker_app.ts +0 -1
  11. package/front_end/generated/InspectorBackendCommands.js +8 -1
  12. package/front_end/generated/protocol-mapping.d.ts +4 -0
  13. package/front_end/generated/protocol-proxy-api.d.ts +6 -0
  14. package/front_end/generated/protocol.d.ts +9 -0
  15. package/front_end/models/issues_manager/AttributionReportingIssue.ts +42 -2
  16. package/front_end/models/issues_manager/descriptions/arInvalidAttributionSourceExpiry.md +4 -0
  17. package/front_end/models/issues_manager/descriptions/arInvalidAttributionSourcePriority.md +4 -0
  18. package/front_end/models/issues_manager/descriptions/arInvalidEventSourceTriggerData.md +9 -0
  19. package/front_end/models/issues_manager/descriptions/arInvalidTriggerDedupKey.md +5 -0
  20. package/front_end/models/issues_manager/descriptions/arInvalidTriggerPriority.md +5 -0
  21. package/front_end/panels/accessibility/AXBreadcrumbsPane.ts +45 -3
  22. package/front_end/panels/accessibility/AccessibilitySidebarView.ts +2 -4
  23. package/front_end/panels/accessibility/axBreadcrumbs.css +4 -0
  24. package/front_end/panels/console/ConsolePrompt.ts +0 -4
  25. package/front_end/panels/elements/AccessibilityTreeUtils.ts +1 -7
  26. package/front_end/panels/elements/AccessibilityTreeView.ts +4 -6
  27. package/front_end/panels/elements/PropertiesWidget.ts +109 -4
  28. package/front_end/panels/elements/propertiesWidget.css +34 -0
  29. package/front_end/panels/emulation/DeviceModeToolbar.ts +5 -1
  30. package/front_end/panels/issues/AttributionReportingIssueDetailsView.ts +44 -0
  31. package/front_end/panels/settings/emulation/components/UserAgentClientHintsForm.ts +1 -0
  32. package/front_end/third_party/codemirror.next/chunk/codemirror.js +1 -1
  33. package/front_end/third_party/codemirror.next/codemirror.next.d.ts +8 -6
  34. package/front_end/third_party/codemirror.next/package.json +3 -3
  35. package/package.json +1 -1
  36. package/scripts/eslint_rules/lib/ban_a_tags_in_lit_html.js +2 -11
  37. package/scripts/eslint_rules/lib/ban_literal_devtools_component_tag_names.js +2 -11
  38. package/scripts/eslint_rules/lib/ban_self_closing_custom_element_tagnames.js +2 -11
  39. package/scripts/eslint_rules/lib/ban_style_tags_in_lit_html.js +2 -11
  40. package/scripts/eslint_rules/lib/lit_html_data_as_type.js +2 -11
  41. package/scripts/eslint_rules/lib/lit_html_no_attribute_quotes.js +1 -13
  42. package/scripts/eslint_rules/lib/lit_no_style_interpolation.js +2 -11
  43. package/scripts/eslint_rules/lib/static_tag_must_be_static_property.js +2 -11
  44. package/scripts/eslint_rules/lib/utils.js +29 -0
  45. package/scripts/eslint_rules/tests/.eslintrc.js +4 -0
  46. package/scripts/eslint_rules/tests/utils_test.js +40 -0
  47. package/front_end/panels/help/HelpImpl.ts +0 -141
  48. package/front_end/panels/help/ReleaseNoteText.ts +0 -1496
  49. package/front_end/panels/help/ReleaseNoteView.ts +0 -107
  50. package/front_end/panels/help/help-meta.ts +0 -145
  51. package/front_end/panels/help/help.ts +0 -13
  52. package/front_end/panels/help/releaseNote.css +0 -115
  53. package/scripts/build/rjsmin.py +0 -484
@@ -259,7 +259,6 @@ export class ConsolePrompt extends Common.ObjectWrapper.eventMixin<EventTypes, t
259
259
  } else {
260
260
  this.editor.dispatch({scrollIntoView: true});
261
261
  }
262
- this.enterProcessedForTest();
263
262
  return true;
264
263
  }
265
264
 
@@ -283,9 +282,6 @@ export class ConsolePrompt extends Common.ObjectWrapper.eventMixin<EventTypes, t
283
282
  }
284
283
  }
285
284
 
286
- private enterProcessedForTest(): void {
287
- }
288
-
289
285
  private editorUpdate(update: CodeMirror.ViewUpdate): void {
290
286
  if (update.docChanged ||
291
287
  CodeMirror.selectedCompletion(update.state) !== CodeMirror.selectedCompletion(update.startState)) {
@@ -86,13 +86,7 @@ async function getChildren(node: SDK.AccessibilityModel.AccessibilityNode):
86
86
  const localRoot = await getRootNode(frameId);
87
87
  return [localRoot];
88
88
  }
89
- if (node.hasUnloadedChildren()) {
90
- await node.accessibilityModel().requestAXChildren(node.id(), node.getFrameId() || undefined);
91
- if (node.numChildren() !== node.children().length) {
92
- throw new Error('Once loaded, number of children and length of children must match.');
93
- }
94
- }
95
- return node.children();
89
+ return node.accessibilityModel().requestAXChildren(node.id(), node.getFrameId() || undefined);
96
90
  }
97
91
 
98
92
  export async function sdkNodeToAXTreeNodes(sdkNode: SDK.AccessibilityModel.AccessibilityNode): Promise<AXTreeNode[]> {
@@ -120,16 +120,14 @@ export class AccessibilityTreeView extends UI.Widget.VBox implements
120
120
 
121
121
  // Selected node in the DOM tree has changed.
122
122
  async selectedNodeChanged(inspectedNode: SDK.DOMModel.DOMNode): Promise<void> {
123
- if (this.isShowing()) {
123
+ if (this.isShowing() || (inspectedNode === this.inspectedDOMNode)) {
124
124
  return;
125
125
  }
126
126
  if (inspectedNode.ownerDocument && (inspectedNode.nodeName() === 'HTML' || inspectedNode.nodeName() === 'BODY')) {
127
- inspectedNode = inspectedNode.ownerDocument;
127
+ this.inspectedDOMNode = inspectedNode.ownerDocument;
128
+ } else {
129
+ this.inspectedDOMNode = inspectedNode;
128
130
  }
129
- if (inspectedNode === this.inspectedDOMNode) {
130
- return;
131
- }
132
- this.inspectedDOMNode = inspectedNode;
133
131
  }
134
132
 
135
133
  treeUpdated({data}: Common.EventTarget
@@ -31,25 +31,67 @@
31
31
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
32
  */
33
33
 
34
- import propertiesWidgetStyles from './propertiesWidget.css.js';
35
- import type * as Common from '../../core/common/common.js';
34
+ import * as Common from '../../core/common/common.js';
36
35
  import * as Host from '../../core/host/host.js';
36
+ import * as i18n from '../../core/i18n/i18n.js';
37
37
  import * as SDK from '../../core/sdk/sdk.js';
38
+ import * as Protocol from '../../generated/protocol.js';
38
39
  import * as ObjectUI from '../../ui/legacy/components/object_ui/object_ui.js';
39
40
  import * as UI from '../../ui/legacy/legacy.js';
40
41
 
42
+ import propertiesWidgetStyles from './propertiesWidget.css.js';
43
+ import {StylesSidebarPane} from './StylesSidebarPane.js';
44
+
41
45
  const OBJECT_GROUP_NAME = 'properties-sidebar-pane';
42
46
 
47
+ const UIStrings = {
48
+ /**
49
+ * @description Placeholder text for a text input used to filter which DOM element properties show up in
50
+ * the Properties tab of the Elements panel.
51
+ */
52
+ filter: 'Filter',
53
+ /**
54
+ * @description ARIA accessible name for the text input used to filter which DOM element properties show up
55
+ * in the Properties tab of the Elements panel.
56
+ */
57
+ filterProperties: 'Filter Properties',
58
+ /**
59
+ * @description Text on the checkbox in the Properties tab of the Elements panel, which controls whether
60
+ * all properties of the currently selected DOM element are shown, or only meaningful properties (i.e.
61
+ * excluding properties whose values aren't set for example).
62
+ */
63
+ showAll: 'Show all',
64
+ /**
65
+ * @description Tooltip on the checkbox in the Properties tab of the Elements panel, which controls whether
66
+ * all properties of the currently selected DOM element are shown, or only meaningful properties (i.e.
67
+ * excluding properties whose values aren't set for example).
68
+ */
69
+ showAllTooltip: 'When unchecked, only properties whose values are neither null nor undefined will be shown',
70
+ /**
71
+ * @description Text shown to the user when a filter is applied in the Properties tab of the Elements panel, but
72
+ * no properties matched the filter and thus no results were returned.
73
+ */
74
+ noMatchingProperty: 'No matching property',
75
+ };
76
+ const str_ = i18n.i18n.registerUIStrings('panels/elements/PropertiesWidget.ts', UIStrings);
77
+ const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
78
+
43
79
  let propertiesWidgetInstance: PropertiesWidget;
44
80
 
45
81
  export class PropertiesWidget extends UI.ThrottledWidget.ThrottledWidget {
46
82
  private node: SDK.DOMModel.DOMNode|null;
83
+ private readonly showAllPropertiesSetting: Common.Settings.Setting<boolean>;
84
+ private filterRegex: RegExp|null = null;
85
+ private readonly noMatchesElement: HTMLElement;
47
86
  private readonly treeOutline: ObjectUI.ObjectPropertiesSection.ObjectPropertiesSectionsTreeOutline;
48
87
  private readonly expandController: ObjectUI.ObjectPropertiesSection.ObjectPropertiesSectionsTreeExpandController;
49
88
  private lastRequestedNode?: SDK.DOMModel.DOMNode;
50
89
  constructor() {
51
90
  super(true /* isWebComponent */);
52
91
 
92
+ this.showAllPropertiesSetting = Common.Settings.Settings.instance().createSetting('showAllProperties', false);
93
+ this.showAllPropertiesSetting.addChangeListener(this.filterList.bind(this));
94
+
53
95
  SDK.TargetManager.TargetManager.instance().addModelListener(
54
96
  SDK.DOMModel.DOMModel, SDK.DOMModel.Events.AttrModified, this.onNodeChange, this);
55
97
  SDK.TargetManager.TargetManager.instance().addModelListener(
@@ -61,6 +103,20 @@ export class PropertiesWidget extends UI.ThrottledWidget.ThrottledWidget {
61
103
  UI.Context.Context.instance().addFlavorChangeListener(SDK.DOMModel.DOMNode, this.setNode, this);
62
104
  this.node = UI.Context.Context.instance().flavor(SDK.DOMModel.DOMNode);
63
105
 
106
+ const hbox = this.contentElement.createChild('div', 'hbox properties-widget-toolbar');
107
+ const filterContainerElement = hbox.createChild('div', 'properties-widget-filter-box');
108
+ const filterInput = StylesSidebarPane.createPropertyFilterElement(
109
+ i18nString(UIStrings.filter), hbox, this.filterProperties.bind(this));
110
+ UI.ARIAUtils.setAccessibleName(filterInput, i18nString(UIStrings.filterProperties));
111
+ filterContainerElement.appendChild(filterInput);
112
+
113
+ const toolbar = new UI.Toolbar.Toolbar('styles-pane-toolbar', hbox);
114
+ toolbar.appendToolbarItem(new UI.Toolbar.ToolbarSettingCheckbox(
115
+ this.showAllPropertiesSetting, i18nString(UIStrings.showAllTooltip), i18nString(UIStrings.showAll)));
116
+
117
+ this.noMatchesElement = this.contentElement.createChild('div', 'gray-info-message hidden');
118
+ this.noMatchesElement.textContent = i18nString(UIStrings.noMatchingProperty);
119
+
64
120
  this.treeOutline = new ObjectUI.ObjectPropertiesSection.ObjectPropertiesSectionsTreeOutline({readOnly: true});
65
121
  this.treeOutline.setShowSelectionOnKeyboardFocus(/* show */ true, /* preventTabOrder */ false);
66
122
  this.expandController =
@@ -73,6 +129,7 @@ export class PropertiesWidget extends UI.ThrottledWidget.ThrottledWidget {
73
129
 
74
130
  this.update();
75
131
  }
132
+
76
133
  static instance(opts: {
77
134
  forceNew: boolean|null,
78
135
  }|undefined = {forceNew: null}): PropertiesWidget {
@@ -84,6 +141,46 @@ export class PropertiesWidget extends UI.ThrottledWidget.ThrottledWidget {
84
141
  return propertiesWidgetInstance;
85
142
  }
86
143
 
144
+ private filterProperties(this: PropertiesWidget, regex: RegExp|null): void {
145
+ this.filterRegex = regex;
146
+ this.filterList();
147
+ }
148
+
149
+ private filterList(): void {
150
+ const isHidden = (property: SDK.RemoteObject.RemoteObjectProperty): boolean => {
151
+ if (!this.showAllPropertiesSetting.get()) {
152
+ if (SDK.RemoteObject.RemoteObject.isNullOrUndefined(property.value)) {
153
+ return true;
154
+ }
155
+ if (property.value?.type === Protocol.Runtime.RemoteObjectType.Undefined ||
156
+ (property.value?.type === Protocol.Runtime.RemoteObjectType.Object &&
157
+ property.value.subtype === Protocol.Runtime.RemoteObjectSubtype.Null)) {
158
+ return true;
159
+ }
160
+ }
161
+ if (this.filterRegex !== null) {
162
+ if (this.filterRegex.test(property.name)) {
163
+ return false;
164
+ }
165
+ if (this.filterRegex.test(property.value?.description ?? '')) {
166
+ return false;
167
+ }
168
+ return true;
169
+ }
170
+ return false;
171
+ };
172
+ let noMatches = true;
173
+ for (const element of this.treeOutline.rootElement().children()) {
174
+ const {property} = element as ObjectUI.ObjectPropertiesSection.ObjectPropertyTreeElement;
175
+ const hidden = isHidden(property);
176
+ if (!hidden) {
177
+ noMatches = false;
178
+ }
179
+ element.hidden = hidden;
180
+ }
181
+ this.noMatchesElement.classList.toggle('hidden', !noMatches);
182
+ }
183
+
87
184
  private setNode(event: Common.EventTarget.EventTargetEvent<SDK.DOMModel.DOMNode|null>): void {
88
185
  this.node = event.data;
89
186
  this.update();
@@ -106,8 +203,15 @@ export class PropertiesWidget extends UI.ThrottledWidget.ThrottledWidget {
106
203
  return;
107
204
  }
108
205
 
109
- await ObjectUI.ObjectPropertiesSection.ObjectPropertyTreeElement.populate(
110
- this.treeOutline.rootElement(), object, true, true, undefined, undefined);
206
+ const treeElement = this.treeOutline.rootElement();
207
+ let {properties} = await SDK.RemoteObject.RemoteObject.loadFromObjectPerProto(object, true /* generatePreview */);
208
+ treeElement.removeChildren();
209
+ if (properties === null) {
210
+ properties = [];
211
+ }
212
+ ObjectUI.ObjectPropertiesSection.ObjectPropertyTreeElement.populateWithProperties(
213
+ treeElement, properties, null, true /* skipProto */, true /* skipGettersAndSetters */, object);
214
+ this.filterList();
111
215
  }
112
216
 
113
217
  private onNodeChange(event: Common.EventTarget
@@ -122,6 +226,7 @@ export class PropertiesWidget extends UI.ThrottledWidget.ThrottledWidget {
122
226
  }
123
227
  this.update();
124
228
  }
229
+
125
230
  wasShown(): void {
126
231
  super.wasShown();
127
232
  this.registerCSSFiles([propertiesWidgetStyles]);
@@ -8,3 +8,37 @@
8
8
  padding: 2px 0 2px 5px;
9
9
  flex: none;
10
10
  }
11
+
12
+ .properties-widget-toolbar {
13
+ border-bottom: 1px solid var(--color-details-hairline-light);
14
+ flex-shrink: 0;
15
+ }
16
+
17
+ .properties-widget-filter-box {
18
+ flex: auto;
19
+ display: flex;
20
+ }
21
+
22
+ .properties-widget-filter-box > input {
23
+ outline: none !important; /* stylelint-disable-line declaration-no-important */
24
+ border: none;
25
+ width: 100%;
26
+ background: var(--color-background);
27
+ padding-left: 4px;
28
+ margin: 3px;
29
+ }
30
+
31
+ .properties-widget-filter-box > input:focus,
32
+ .properties-widget-filter-box > input:not(:placeholder-shown) {
33
+ box-shadow: var(--legacy-focus-ring-active-shadow);
34
+ }
35
+
36
+ .properties-widget-filter-box > input::placeholder {
37
+ color: var(--color-text-disabled);
38
+ }
39
+
40
+ @media (forced-colors: active) {
41
+ .properties-widget-filter-box > input {
42
+ border: 1px solid ButtonText;
43
+ }
44
+ }
@@ -402,7 +402,7 @@ export class DeviceModeToolbar {
402
402
  private appendScaleMenuItems(contextMenu: UI.ContextMenu.ContextMenu): void {
403
403
  if (this.model.type() === EmulationModel.DeviceModeModel.Type.Device) {
404
404
  contextMenu.footerSection().appendItem(
405
- i18nString(UIStrings.fitToWindowF, {PH1: this.getPrettyZoomPercentage()}),
405
+ i18nString(UIStrings.fitToWindowF, {PH1: this.getPrettyFitZoomPercentage()}),
406
406
  this.onScaleMenuChanged.bind(this, this.model.fitScale()), false);
407
407
  }
408
408
  contextMenu.footerSection().appendCheckboxItem(
@@ -693,6 +693,10 @@ export class DeviceModeToolbar {
693
693
  }
694
694
  }
695
695
 
696
+ private getPrettyFitZoomPercentage(): string {
697
+ return `${(this.model.fitScale() * 100).toFixed(0)}`;
698
+ }
699
+
696
700
  private getPrettyZoomPercentage(): string {
697
701
  return `${(this.model.scale() * 100).toFixed(0)}`;
698
702
  }
@@ -32,6 +32,16 @@ const UIStrings = {
32
32
  * on an anchor HTML element ("a link").
33
33
  */
34
34
  invalidSourceEventId: 'Invalid `attributionsourceeventid`',
35
+ /**
36
+ * @description Label for the column showing the invalid value used as the 'attributionexpiry' attribute
37
+ * on an anchor HTML element ("a link").
38
+ */
39
+ invalidSourceExpiry: 'Invalid `attributionexpiry`',
40
+ /**
41
+ * @description Label for the column showing the invalid value used as the 'attributionpriority' attribute
42
+ * on an anchor HTML element ("a link").
43
+ */
44
+ invalidSourcePriority: 'Invalid `attributionsourcepriority`',
35
45
  /**
36
46
  * @description Label for the column showing the invalid URL used in an HTML anchor element ("a link").
37
47
  * A origin is (roughly said) the front part of a URL.
@@ -47,6 +57,16 @@ const UIStrings = {
47
57
  * 'event-source-trigger-data' query parameter.
48
58
  */
49
59
  invalidEventSourceTriggerData: 'Invalid `event-source-trigger-data`',
60
+ /**
61
+ * @description Label for the column showing the invalid value used for the
62
+ * 'priority' query parameter.
63
+ */
64
+ invalidTriggerPriority: 'Invalid `priority`',
65
+ /**
66
+ * @description Label for the column showing the invalid value used for the
67
+ * 'dedup-key' query parameter.
68
+ */
69
+ invalidTriggerDedupKey: 'Invalid `dedup-key`',
50
70
  };
51
71
  const str_ = i18n.i18n.registerUIStrings('panels/issues/AttributionReportingIssueDetailsView.ts', UIStrings);
52
72
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
@@ -94,15 +114,34 @@ export class AttributionReportingIssueDetailsView extends AffectedResourcesView
94
114
  this.appendColumnTitle(header, i18nString(UIStrings.request));
95
115
  this.appendColumnTitle(header, i18nString(UIStrings.invalidTriggerData));
96
116
  break;
117
+ case IssuesManager.AttributionReportingIssue.IssueCode.InvalidEventSourceTriggerData:
97
118
  case IssuesManager.AttributionReportingIssue.IssueCode.AttributionEventSourceTriggerDataTooLarge:
98
119
  this.appendColumnTitle(header, i18nString(UIStrings.request));
99
120
  this.appendColumnTitle(header, i18nString(UIStrings.invalidEventSourceTriggerData));
100
121
  break;
122
+ case IssuesManager.AttributionReportingIssue.IssueCode.InvalidTriggerPriority:
123
+ this.appendColumnTitle(header, i18nString(UIStrings.request));
124
+ this.appendColumnTitle(header, i18nString(UIStrings.invalidTriggerPriority));
125
+ break;
126
+ case IssuesManager.AttributionReportingIssue.IssueCode.InvalidTriggerDedupKey:
127
+ this.appendColumnTitle(header, i18nString(UIStrings.request));
128
+ this.appendColumnTitle(header, i18nString(UIStrings.invalidTriggerDedupKey));
129
+ break;
101
130
  case IssuesManager.AttributionReportingIssue.IssueCode.InvalidAttributionSourceEventId:
102
131
  this.appendColumnTitle(header, i18nString(UIStrings.frame));
103
132
  this.appendColumnTitle(header, i18nString(UIStrings.element));
104
133
  this.appendColumnTitle(header, i18nString(UIStrings.invalidSourceEventId));
105
134
  break;
135
+ case IssuesManager.AttributionReportingIssue.IssueCode.InvalidAttributionSourceExpiry:
136
+ this.appendColumnTitle(header, i18nString(UIStrings.frame));
137
+ this.appendColumnTitle(header, i18nString(UIStrings.element));
138
+ this.appendColumnTitle(header, i18nString(UIStrings.invalidSourceExpiry));
139
+ break;
140
+ case IssuesManager.AttributionReportingIssue.IssueCode.InvalidAttributionSourcePriority:
141
+ this.appendColumnTitle(header, i18nString(UIStrings.frame));
142
+ this.appendColumnTitle(header, i18nString(UIStrings.element));
143
+ this.appendColumnTitle(header, i18nString(UIStrings.invalidSourcePriority));
144
+ break;
106
145
  case IssuesManager.AttributionReportingIssue.IssueCode.MissingAttributionData:
107
146
  this.appendColumnTitle(header, i18nString(UIStrings.request));
108
147
  break;
@@ -144,11 +183,16 @@ export class AttributionReportingIssueDetailsView extends AffectedResourcesView
144
183
  case IssuesManager.AttributionReportingIssue.IssueCode.AttributionEventSourceTriggerDataTooLarge:
145
184
  case IssuesManager.AttributionReportingIssue.IssueCode.AttributionUntrustworthyOrigin:
146
185
  case IssuesManager.AttributionReportingIssue.IssueCode.InvalidAttributionData:
186
+ case IssuesManager.AttributionReportingIssue.IssueCode.InvalidEventSourceTriggerData:
187
+ case IssuesManager.AttributionReportingIssue.IssueCode.InvalidTriggerPriority:
188
+ case IssuesManager.AttributionReportingIssue.IssueCode.InvalidTriggerDedupKey:
147
189
  this.appendRequestOrEmptyCell(element, details.request);
148
190
  this.appendIssueDetailCell(element, details.invalidParameter || '');
149
191
  break;
150
192
  case IssuesManager.AttributionReportingIssue.IssueCode.AttributionSourceUntrustworthyFrameOrigin:
151
193
  case IssuesManager.AttributionReportingIssue.IssueCode.InvalidAttributionSourceEventId:
194
+ case IssuesManager.AttributionReportingIssue.IssueCode.InvalidAttributionSourceExpiry:
195
+ case IssuesManager.AttributionReportingIssue.IssueCode.InvalidAttributionSourcePriority:
152
196
  this.appendFrameOrEmptyCell(element, issue);
153
197
  await this.appendElementOrEmptyCell(element, issue);
154
198
  this.appendIssueDetailCell(element, details.invalidParameter || '');
@@ -554,6 +554,7 @@ export class UserAgentClientHintsForm extends HTMLElement {
554
554
  aria-controls="form-container"
555
555
  @disabled=${this.isFormDisabled}
556
556
  aria-disabled=${this.isFormDisabled}
557
+ aria-label=${i18nString(UIStrings.title)}
557
558
  >
558
559
  <${IconButton.Icon.Icon.litTagName}
559
560
  class=${this.isFormOpened ? '' : 'rotate-icon'}