chrome-devtools-frontend 1.0.1698733 → 1.0.1700561

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 (164) hide show
  1. package/.agents/skills/devtools-verification/SKILL.md +19 -0
  2. package/.agents/skills/{repro-flaky-tests → fix-tests}/SKILL.md +2 -2
  3. package/front_end/core/sdk/CSSPropertyParserMatchers.ts +2 -2
  4. package/front_end/core/sdk/DOMModel.ts +3 -5
  5. package/front_end/core/sdk/NetworkManager.ts +2 -2
  6. package/front_end/core/sdk/OverlayModel.ts +11 -12
  7. package/front_end/core/sdk/ServiceWorkerCacheModel.ts +3 -0
  8. package/front_end/core/sdk/SourceMapScopeChainEntry.ts +84 -32
  9. package/front_end/core/sdk/StorageBucketsModel.ts +2 -0
  10. package/front_end/entrypoints/main/GlobalAiButton.ts +2 -6
  11. package/front_end/entrypoints/main/MainImpl.ts +8 -1
  12. package/front_end/generated/InspectorBackendCommands.ts +12 -2
  13. package/front_end/generated/SupportedCSSProperties.ts +14 -0
  14. package/front_end/generated/protocol-mapping.d.ts +48 -1
  15. package/front_end/generated/protocol-proxy-api.d.ts +37 -1
  16. package/front_end/generated/protocol.ts +131 -0
  17. package/front_end/models/bindings/NetworkProject.ts +30 -0
  18. package/front_end/models/bindings/ResourceScriptMapping.ts +62 -0
  19. package/front_end/models/change_tracker/ChangeTracker.ts +1 -1
  20. package/front_end/models/comment_manager/CD4ABridge.ts +2 -2
  21. package/front_end/models/comment_manager/CommentManager.ts +18 -0
  22. package/front_end/models/emulation/DeviceModeModel.ts +1 -1
  23. package/front_end/models/extensions/ExtensionAPI.ts +4 -2
  24. package/front_end/models/issues_manager/CookieIssue.ts +0 -15
  25. package/front_end/models/persistence/NetworkPersistenceManager.ts +29 -4
  26. package/front_end/models/source_map_scopes/NamesResolver.ts +34 -4
  27. package/front_end/models/source_map_scopes/ScopeChainModel.ts +31 -7
  28. package/front_end/panels/accessibility/AccessibilityAnnouncementRecordingView.ts +281 -3
  29. package/front_end/panels/accessibility/accessibilityAnnouncementRecordingView.css +85 -0
  30. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +1 -2
  31. package/front_end/panels/ai_assistance/ai_assistance-meta.ts +5 -8
  32. package/front_end/panels/animation/AnimationTimeline.ts +1 -1
  33. package/front_end/panels/application/AppManifestView.ts +128 -134
  34. package/front_end/panels/application/CrashReportContextView.ts +6 -6
  35. package/front_end/panels/application/DeviceBoundSessionsTreeElement.ts +17 -17
  36. package/front_end/panels/application/DeviceBoundSessionsView.ts +136 -137
  37. package/front_end/panels/application/IndexedDBModel.ts +3 -0
  38. package/front_end/panels/application/IndexedDBViews.ts +1 -1
  39. package/front_end/panels/application/KeyValueStorageItemsView.ts +1 -0
  40. package/front_end/panels/application/OriginTrialTreeView.ts +18 -23
  41. package/front_end/panels/application/ReportingApiTreeElement.ts +3 -3
  42. package/front_end/panels/application/ReportingApiView.ts +6 -14
  43. package/front_end/panels/application/application-meta.ts +7 -7
  44. package/front_end/panels/application/components/AdsView.ts +63 -1
  45. package/front_end/panels/application/components/adsView.css +11 -1
  46. package/front_end/panels/common/CommentThreadWidget.ts +20 -22
  47. package/front_end/panels/common/CommentsOverlayWidget.ts +115 -9
  48. package/front_end/panels/common/CommentsStatusBarPill.ts +13 -1
  49. package/front_end/panels/common/ExtensionServer.ts +43 -34
  50. package/front_end/panels/common/GeminiRebrandPromoDialog.ts +1 -1
  51. package/front_end/panels/common/commentThreadWidget.css +6 -0
  52. package/front_end/panels/common/common-meta.ts +4 -1
  53. package/front_end/panels/elements/CSSRuleValidator.ts +1 -1
  54. package/front_end/panels/elements/ElementsPanel.ts +4 -4
  55. package/front_end/panels/elements/ElementsTreeElement.ts +9 -2
  56. package/front_end/panels/elements/ElementsTreeOutline.ts +25 -33
  57. package/front_end/panels/elements/elementsTreeOutline.css +8 -4
  58. package/front_end/panels/event_listeners/EventListenersView.ts +9 -4
  59. package/front_end/panels/issues/IssuesPane.ts +34 -22
  60. package/front_end/panels/lighthouse/LighthouseController.ts +1 -1
  61. package/front_end/panels/mobile_throttling/NetworkPanelIndicator.ts +1 -1
  62. package/front_end/panels/mobile_throttling/ThrottlingManager.ts +1 -1
  63. package/front_end/panels/mobile_throttling/ThrottlingSettingsTab.ts +1 -1
  64. package/front_end/panels/network/RequestPayloadView.ts +17 -15
  65. package/front_end/panels/recorder/RecordingView.ts +32 -53
  66. package/front_end/panels/recorder/StepView.ts +19 -4
  67. package/front_end/panels/recorder/TimelineSection.ts +1 -1
  68. package/front_end/panels/recorder/controlButton.css +6 -0
  69. package/front_end/panels/recorder/recordingView.css +11 -2
  70. package/front_end/panels/settings/BackendLinkingSettingsTab.ts +2 -1
  71. package/front_end/panels/settings/EditFileSystemView.ts +1 -1
  72. package/front_end/panels/sources/CallStackSidebarPane.ts +1 -1
  73. package/front_end/panels/sources/DebuggerPlugin.ts +137 -56
  74. package/front_end/panels/sources/ScopeChainSidebarPane.ts +17 -15
  75. package/front_end/panels/timeline/components/DetailsView.ts +3 -3
  76. package/front_end/panels/timeline/components/IgnoreListSetting.ts +0 -2
  77. package/front_end/panels/timeline/components/LiveMetricsView.ts +59 -64
  78. package/front_end/panels/timeline/components/OriginMap.ts +1 -1
  79. package/front_end/panels/timeline/components/insights/BaseInsightComponent.ts +2 -3
  80. package/front_end/panels/timeline/components/liveMetricsView.css +1 -0
  81. package/front_end/third_party/chromium/README.chromium +1 -1
  82. package/front_end/third_party/lighthouse/README.chromium +2 -2
  83. package/front_end/third_party/lighthouse/lighthouse-dt-bundle.js +1580 -1524
  84. package/front_end/third_party/lighthouse/locales/ar-XB.json +111 -72
  85. package/front_end/third_party/lighthouse/locales/ar.json +113 -74
  86. package/front_end/third_party/lighthouse/locales/bg.json +112 -73
  87. package/front_end/third_party/lighthouse/locales/ca.json +112 -73
  88. package/front_end/third_party/lighthouse/locales/cs.json +112 -73
  89. package/front_end/third_party/lighthouse/locales/da.json +112 -73
  90. package/front_end/third_party/lighthouse/locales/de.json +112 -73
  91. package/front_end/third_party/lighthouse/locales/el.json +112 -73
  92. package/front_end/third_party/lighthouse/locales/en-GB.json +111 -72
  93. package/front_end/third_party/lighthouse/locales/en-US.json +102 -66
  94. package/front_end/third_party/lighthouse/locales/en-XA.json +111 -72
  95. package/front_end/third_party/lighthouse/locales/en-XL.json +102 -66
  96. package/front_end/third_party/lighthouse/locales/es-419.json +112 -73
  97. package/front_end/third_party/lighthouse/locales/es.json +112 -73
  98. package/front_end/third_party/lighthouse/locales/fi.json +112 -73
  99. package/front_end/third_party/lighthouse/locales/fil.json +112 -73
  100. package/front_end/third_party/lighthouse/locales/fr.json +112 -73
  101. package/front_end/third_party/lighthouse/locales/he.json +112 -73
  102. package/front_end/third_party/lighthouse/locales/hi.json +112 -73
  103. package/front_end/third_party/lighthouse/locales/hr.json +112 -73
  104. package/front_end/third_party/lighthouse/locales/hu.json +112 -73
  105. package/front_end/third_party/lighthouse/locales/id.json +112 -73
  106. package/front_end/third_party/lighthouse/locales/it.json +113 -74
  107. package/front_end/third_party/lighthouse/locales/ja.json +112 -73
  108. package/front_end/third_party/lighthouse/locales/ko.json +112 -73
  109. package/front_end/third_party/lighthouse/locales/lt.json +112 -73
  110. package/front_end/third_party/lighthouse/locales/lv.json +112 -73
  111. package/front_end/third_party/lighthouse/locales/nl.json +112 -73
  112. package/front_end/third_party/lighthouse/locales/no.json +112 -73
  113. package/front_end/third_party/lighthouse/locales/pl.json +112 -73
  114. package/front_end/third_party/lighthouse/locales/pt-PT.json +112 -73
  115. package/front_end/third_party/lighthouse/locales/pt.json +112 -73
  116. package/front_end/third_party/lighthouse/locales/ro.json +112 -73
  117. package/front_end/third_party/lighthouse/locales/ru.json +112 -73
  118. package/front_end/third_party/lighthouse/locales/sk.json +112 -73
  119. package/front_end/third_party/lighthouse/locales/sl.json +112 -73
  120. package/front_end/third_party/lighthouse/locales/sr-Latn.json +112 -73
  121. package/front_end/third_party/lighthouse/locales/sr.json +112 -73
  122. package/front_end/third_party/lighthouse/locales/sv.json +124 -85
  123. package/front_end/third_party/lighthouse/locales/ta.json +112 -73
  124. package/front_end/third_party/lighthouse/locales/te.json +112 -73
  125. package/front_end/third_party/lighthouse/locales/th.json +112 -73
  126. package/front_end/third_party/lighthouse/locales/tr.json +113 -74
  127. package/front_end/third_party/lighthouse/locales/uk.json +112 -73
  128. package/front_end/third_party/lighthouse/locales/vi.json +112 -73
  129. package/front_end/third_party/lighthouse/locales/zh-HK.json +112 -73
  130. package/front_end/third_party/lighthouse/locales/zh-TW.json +113 -74
  131. package/front_end/third_party/lighthouse/locales/zh.json +113 -74
  132. package/front_end/third_party/lighthouse/report/bundle.d.ts +4 -4
  133. package/front_end/third_party/lighthouse/report/bundle.js +10 -3
  134. package/front_end/third_party/lighthouse/report-assets/report-generator.mjs +2 -2
  135. package/front_end/third_party/lit/lit.ts +3 -0
  136. package/front_end/third_party/lit/package.json +3 -0
  137. package/front_end/third_party/lit/rebuild.sh +3 -2
  138. package/front_end/ui/comments/CommentAnchorResolver.ts +8 -2
  139. package/front_end/ui/comments/CommentOverlayManager.ts +2 -1
  140. package/front_end/ui/components/buttons/Button.ts +19 -1
  141. package/front_end/ui/legacy/ContextMenu.ts +16 -1
  142. package/front_end/ui/legacy/InspectorView.ts +4 -3
  143. package/front_end/ui/legacy/PlusButton.ts +5 -6
  144. package/front_end/ui/legacy/TabbedPane.ts +121 -80
  145. package/front_end/ui/legacy/Treeoutline.ts +37 -6
  146. package/front_end/ui/legacy/UIUtils.ts +7 -0
  147. package/front_end/ui/legacy/Widget.ts +33 -0
  148. package/front_end/ui/legacy/components/cookie_table/CookiesTable.ts +3 -0
  149. package/front_end/ui/legacy/components/data_grid/DataGridElement.ts +14 -4
  150. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +157 -171
  151. package/front_end/ui/lit/lit.ts +1 -1
  152. package/front_end/ui/visual_logging/KnownContextValues.ts +7 -0
  153. package/inspector_overlay/highlight_anchor.ts +197 -0
  154. package/inspector_overlay/tool_highlight.ts +8 -0
  155. package/package.json +1 -1
  156. package/.agents/skills/devtools-ci/SKILL.md +0 -65
  157. package/.agents/skills/evaluate-ai-css-completion/SKILL.md +0 -143
  158. package/.agents/skills/evaluate-ai-css-completion/scripts/evaluate.js +0 -104
  159. package/.agents/skills/fixing-skipped-tests/SKILL.md +0 -32
  160. package/.agents/skills/gerrit-cli/SKILL.md +0 -74
  161. package/.agents/skills/gerrit-cli/references/commands.md +0 -169
  162. package/.agents/skills/gerrit-cli/references/workflows.md +0 -63
  163. package/.agents/skills/gerrit-cli/scripts/import_upstream.py +0 -85
  164. package/front_end/models/issues_manager/descriptions/cookieExcludeBlockedWithinRelatedWebsiteSet.md +0 -4
@@ -21,6 +21,9 @@ import {resolveScopeChain} from './NamesResolver.js';
21
21
  * This class tracks all that and sends events with the latest scope chain for a specific call frame.
22
22
  */
23
23
  export class ScopeChainModel extends Common.ObjectWrapper.ObjectWrapper<EventTypes> {
24
+ static readonly #cachedScopeChainByCallFrame =
25
+ new WeakMap<SDK.DebuggerModel.CallFrame, Promise<SDK.DebuggerModel.ScopeChainEntry[]>>();
26
+
24
27
  readonly #callFrame: SDK.DebuggerModel.CallFrame;
25
28
  readonly #debuggerWorkspaceBinding: Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding;
26
29
 
@@ -35,8 +38,10 @@ export class ScopeChainModel extends Common.ObjectWrapper.ObjectWrapper<EventTyp
35
38
  this.#debuggerWorkspaceBinding = debuggerWorkspaceBinding;
36
39
  this.#callFrame.debuggerModel.addEventListener(
37
40
  SDK.DebuggerModel.Events.DebugInfoAttached, this.#debugInfoAttached, this);
38
- this.#callFrame.debuggerModel.sourceMapManager().addEventListener(
39
- SDK.SourceMapManager.Events.SourceMapAttached, this.#sourceMapAttached, this);
41
+ this.#callFrame.debuggerModel.sourceMapManager().addEventListener(SDK.SourceMapManager.Events.SourceMapAttached,
42
+ this.#sourceMapChanged, this);
43
+ this.#callFrame.debuggerModel.sourceMapManager().addEventListener(SDK.SourceMapManager.Events.SourceMapDetached,
44
+ this.#sourceMapChanged, this);
40
45
 
41
46
  void this.#throttler.schedule(this.#boundUpdate);
42
47
  }
@@ -44,25 +49,44 @@ export class ScopeChainModel extends Common.ObjectWrapper.ObjectWrapper<EventTyp
44
49
  dispose(): void {
45
50
  this.#callFrame.debuggerModel.removeEventListener(
46
51
  SDK.DebuggerModel.Events.DebugInfoAttached, this.#debugInfoAttached, this);
47
- this.#callFrame.debuggerModel.sourceMapManager().removeEventListener(
48
- SDK.SourceMapManager.Events.SourceMapAttached, this.#sourceMapAttached, this);
52
+ this.#callFrame.debuggerModel.sourceMapManager().removeEventListener(SDK.SourceMapManager.Events.SourceMapAttached,
53
+ this.#sourceMapChanged, this);
54
+ this.#callFrame.debuggerModel.sourceMapManager().removeEventListener(SDK.SourceMapManager.Events.SourceMapDetached,
55
+ this.#sourceMapChanged, this);
49
56
  this.listeners?.clear();
50
57
  }
51
58
 
59
+ static resolveScopeChain(callFrame: SDK.DebuggerModel.CallFrame,
60
+ debuggerWorkspaceBinding: Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding):
61
+ Promise<SDK.DebuggerModel.ScopeChainEntry[]> {
62
+ let cachedPromise = ScopeChainModel.#cachedScopeChainByCallFrame.get(callFrame);
63
+ if (!cachedPromise) {
64
+ cachedPromise = resolveScopeChain(callFrame, debuggerWorkspaceBinding);
65
+ ScopeChainModel.#cachedScopeChainByCallFrame.set(callFrame, cachedPromise);
66
+ }
67
+ return cachedPromise;
68
+ }
69
+
70
+ resolveScopeChain(): Promise<SDK.DebuggerModel.ScopeChainEntry[]> {
71
+ return ScopeChainModel.resolveScopeChain(this.#callFrame, this.#debuggerWorkspaceBinding);
72
+ }
73
+
52
74
  async #update(): Promise<void> {
53
- const scopeChain = await resolveScopeChain(this.#callFrame, this.#debuggerWorkspaceBinding);
75
+ const scopeChain = await this.resolveScopeChain();
54
76
  this.dispatchEventToListeners(Events.SCOPE_CHAIN_UPDATED, new ScopeChain(scopeChain));
55
77
  }
56
78
 
57
79
  #debugInfoAttached(event: Common.EventTarget.EventTargetEvent<SDK.Script.Script>): void {
58
80
  if (event.data === this.#callFrame.script) {
81
+ ScopeChainModel.#cachedScopeChainByCallFrame.delete(this.#callFrame);
59
82
  void this.#throttler.schedule(this.#boundUpdate);
60
83
  }
61
84
  }
62
85
 
63
- #sourceMapAttached(event: Common.EventTarget
64
- .EventTargetEvent<{client: SDK.Script.Script, sourceMap: SDK.SourceMap.SourceMap}>): void {
86
+ #sourceMapChanged(event: Common.EventTarget
87
+ .EventTargetEvent<{client: SDK.Script.Script, sourceMap: SDK.SourceMap.SourceMap}>): void {
65
88
  if (event.data.client === this.#callFrame.script) {
89
+ ScopeChainModel.#cachedScopeChainByCallFrame.delete(this.#callFrame);
66
90
  void this.#throttler.schedule(this.#boundUpdate);
67
91
  }
68
92
  }
@@ -6,25 +6,192 @@ import type * as Common from '../../core/common/common.js';
6
6
  import * as i18n from '../../core/i18n/i18n.js';
7
7
  import * as SDK from '../../core/sdk/sdk.js';
8
8
  import type * as Protocol from '../../generated/protocol.js';
9
+ import * as Buttons from '../../ui/components/buttons/buttons.js';
10
+ import * as UI from '../../ui/legacy/legacy.js';
11
+ import * as Lit from '../../ui/lit/lit.js';
12
+ import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
9
13
 
14
+ import {AccessibilityAnnouncementRecordingListView} from './AccessibilityAnnouncementRecordingListView.js';
15
+ import accessibilityAnnouncementRecordingViewStyles from './accessibilityAnnouncementRecordingView.css.js';
10
16
  import {AccessibilitySubPane} from './AccessibilitySubPane.js';
11
17
 
18
+ const {html, render} = Lit;
19
+ const {widget} = UI.Widget;
20
+
12
21
  const UIStrings = {
13
22
  /**
14
23
  * @description Title for the ARIA-Live and JS announcements recording tool
15
24
  */
16
25
  ariaLiveRecording: 'Announcements recording',
26
+ /**
27
+ * @description Tooltip for the start recording button in the announcements tool.
28
+ */
29
+ startRecording: 'Start recording',
30
+ /**
31
+ * @description Tooltip for the stop recording button in the announcements tool.
32
+ */
33
+ stopRecording: 'Stop recording',
34
+ /**
35
+ * @description Tooltip for the clear announcements button in the announcements tool.
36
+ */
37
+ clearAnnouncements: 'Clear announcements',
38
+ /**
39
+ * @description Label/title for the dropdown filter to select which announcement types to record.
40
+ */
41
+ filterByType: 'Filter by type',
42
+ /**
43
+ * @description Option label to record and display both ARIA-live and JavaScript announcements.
44
+ */
45
+ recordBoth: 'Record both',
46
+ /**
47
+ * @description Option label to record and display only ARIA-live announcements.
48
+ */
49
+ ariaLiveOnly: 'ARIA-live only',
50
+ /**
51
+ * @description Option label to record and display only JavaScript-triggered announcements.
52
+ */
53
+ announcementsOnly: 'Announcements only',
54
+ /**
55
+ * @description Placeholder text for the filter input in the announcements tool.
56
+ */
57
+ filter: 'Filter',
58
+ /**
59
+ * @description Screen reader announcement when no events match the filter in the announcements tool.
60
+ */
61
+ noEventsMatch: 'No events match',
62
+ /**
63
+ * @description Screen reader announcement when exactly one event matches the filter in the announcements tool.
64
+ */
65
+ oneEventMatches: '1 event matches',
66
+ /**
67
+ * @description Screen reader announcement when multiple events match the filter in the announcements tool.
68
+ * @example {15} PH1
69
+ */
70
+ nEventsMatch: '{PH1} events match',
71
+ /**
72
+ * @description Warning banner title shown when recording could not be enabled in some frames.
73
+ */
74
+ recordingBlockedWarning: 'Recording was blocked for some frames:',
75
+ /**
76
+ * @description Warning item describing a specific frame and the reason recording was blocked.
77
+ * @example {iframe#main} PH1
78
+ * @example {Script evaluation failed} PH2
79
+ */
80
+ frameBlockedReason: '{PH1}: {PH2}',
81
+ /**
82
+ * @description Fallback reason shown when an unknown error occurs while blocking recording.
83
+ */
84
+ unknownError: 'Unknown error',
17
85
  } as const;
18
86
  const str_ = i18n.i18n.registerUIStrings('panels/accessibility/AccessibilityAnnouncementRecordingView.ts', UIStrings);
19
87
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
20
88
 
21
89
  export const BINDING_NAME = '__announcementsRecorderBinding';
22
90
 
23
- export const enum AnnouncementApi {
91
+ export enum AnnouncementApi {
24
92
  ARIA_LIVE = 'aria-live',
25
93
  JS_TRIGGERED = 'js-triggered',
26
94
  }
27
95
 
96
+ export const enum RecordTypeFilter {
97
+ BOTH = 'both',
98
+ ARIA_LIVE = 'aria-live',
99
+ JS_TRIGGERED = 'js-triggered',
100
+ }
101
+
102
+ export interface BlockedTargetInfo {
103
+ targetName: string;
104
+ reason: string;
105
+ }
106
+
107
+ export interface ViewInput {
108
+ isRecording: boolean;
109
+ onToggleRecording: () => void;
110
+ onClear: () => void;
111
+ recordTypeFilter: RecordTypeFilter;
112
+ onRecordTypeFilterChange: (type: RecordTypeFilter) => void;
113
+ textFilter: string;
114
+ onTextFilterChange: (text: string) => void;
115
+ blockedTargets: BlockedTargetInfo[];
116
+ announcements: readonly A11yAnnouncement[];
117
+ }
118
+
119
+ export type View = (input: ViewInput, output: undefined, target: HTMLElement) => void;
120
+
121
+ export const DEFAULT_VIEW: View = (input, _output, target) => {
122
+ // clang-format off
123
+ render(html`
124
+ <style>${accessibilityAnnouncementRecordingViewStyles}</style>
125
+ <div class="accessibility-announcement-recording-view">
126
+ <div class="announcements-toolbar-container">
127
+ <devtools-toolbar class="announcements-toolbar" jslog=${VisualLogging.toolbar()}>
128
+ <devtools-button
129
+ title=${input.isRecording ? i18nString(UIStrings.stopRecording) : i18nString(UIStrings.startRecording)}
130
+ aria-label=${input.isRecording ? i18nString(UIStrings.stopRecording) : i18nString(UIStrings.startRecording)}
131
+ .iconName=${'record-start'}
132
+ .toggledIconName=${'record-stop'}
133
+ .toggleType=${Buttons.Button.ToggleType.PRIMARY}
134
+ .toggled=${input.isRecording}
135
+ @click=${input.onToggleRecording}
136
+ .variant=${Buttons.Button.Variant.TOOLBAR}
137
+ .jslogContext=${'accessibility.toggle-recording'}>
138
+ </devtools-button>
139
+ <devtools-button
140
+ title=${i18nString(UIStrings.clearAnnouncements)}
141
+ aria-label=${i18nString(UIStrings.clearAnnouncements)}
142
+ .iconName=${'clear'}
143
+ @click=${input.onClear}
144
+ .variant=${Buttons.Button.Variant.TOOLBAR}
145
+ .jslogContext=${'accessibility.clear-announcements'}>
146
+ </devtools-button>
147
+ <div class="toolbar-divider" role="separator"></div>
148
+ <select
149
+ title=${i18nString(UIStrings.filterByType)}
150
+ aria-label=${i18nString(UIStrings.filterByType)}
151
+ @change=${(event: Event) => input.onRecordTypeFilterChange((event.target as HTMLSelectElement).value as RecordTypeFilter)}
152
+ .value=${input.recordTypeFilter}
153
+ jslog=${VisualLogging.dropDown('accessibility-announcements.filter-by-type').track({change: true})}>
154
+ <option value=${RecordTypeFilter.BOTH} .selected=${input.recordTypeFilter === RecordTypeFilter.BOTH}>
155
+ ${i18nString(UIStrings.recordBoth)}
156
+ </option>
157
+ <option value=${RecordTypeFilter.ARIA_LIVE} .selected=${input.recordTypeFilter === RecordTypeFilter.ARIA_LIVE}>
158
+ ${i18nString(UIStrings.ariaLiveOnly)}
159
+ </option>
160
+ <option value=${RecordTypeFilter.JS_TRIGGERED} .selected=${input.recordTypeFilter === RecordTypeFilter.JS_TRIGGERED}>
161
+ ${i18nString(UIStrings.announcementsOnly)}
162
+ </option>
163
+ </select>
164
+ <div class="toolbar-divider" role="separator"></div>
165
+ <devtools-toolbar-input
166
+ type="filter"
167
+ placeholder=${i18nString(UIStrings.filter)}
168
+ .value=${input.textFilter}
169
+ @change=${(event: CustomEvent<string>) => input.onTextFilterChange(event.detail)}
170
+ style="flex-grow: 1">
171
+ </devtools-toolbar-input>
172
+ </devtools-toolbar>
173
+ </div>
174
+ ${input.blockedTargets.length > 0 ? html`
175
+ <div class="announcements-blocked-banner" role="alert">
176
+ <div class="blocked-banner-header">
177
+ <devtools-icon name="warning-filled"></devtools-icon>
178
+ <span>${i18nString(UIStrings.recordingBlockedWarning)}</span>
179
+ </div>
180
+ <ul class="blocked-targets-list">
181
+ ${input.blockedTargets.map(targetInfo => html`
182
+ <li>${i18nString(UIStrings.frameBlockedReason, {PH1: targetInfo.targetName, PH2: targetInfo.reason || i18nString(UIStrings.unknownError)})}</li>
183
+ `)}
184
+ </ul>
185
+ </div>
186
+ ` : Lit.nothing}
187
+ <div class="announcements-main-pane">
188
+ ${widget(AccessibilityAnnouncementRecordingListView, {items: input.announcements})}
189
+ </div>
190
+ </div>`,
191
+ target);
192
+ // clang-format on
193
+ };
194
+
28
195
  declare global {
29
196
  interface Window {
30
197
  // eslint-disable-next-line @typescript-eslint/naming-convention
@@ -564,17 +731,23 @@ export function validateAndSanitizeAnnouncement(payload: unknown): A11yAnnouncem
564
731
 
565
732
  export class AccessibilityAnnouncementRecordingView extends AccessibilitySubPane implements SDK.TargetManager.Observer {
566
733
  #announcements: A11yAnnouncement[] = [];
734
+ #filteredAnnouncements: readonly A11yAnnouncement[]|null = null;
567
735
  #isRecording = false;
568
736
  #blockedTargets = new Map<SDK.Target.Target, string>();
569
737
  #scriptIdentifiers = new Map<SDK.Target.Target, Protocol.Page.ScriptIdentifier>();
570
738
  #targets = new Set<SDK.Target.Target>();
571
739
  #enabledTargets = new Set<SDK.Target.Target>();
740
+ #recordTypeFilter: RecordTypeFilter = RecordTypeFilter.BOTH;
741
+ #textFilter = '';
742
+ #regexFilter: RegExp|null = null;
743
+ readonly #view: View;
572
744
 
573
- constructor() {
745
+ constructor(view: View = DEFAULT_VIEW) {
574
746
  super({
575
747
  title: i18nString(UIStrings.ariaLiveRecording),
576
748
  viewId: 'aria-live-recording',
577
749
  });
750
+ this.#view = view;
578
751
  SDK.TargetManager.TargetManager.instance().observeTargets(this, {scoped: true});
579
752
  }
580
753
 
@@ -595,8 +768,11 @@ export class AccessibilityAnnouncementRecordingView extends AccessibilitySubPane
595
768
 
596
769
  async targetRemoved(target: SDK.Target.Target): Promise<void> {
597
770
  this.#targets.delete(target);
598
- this.#blockedTargets.delete(target);
771
+ const wasBlocked = this.#blockedTargets.delete(target);
599
772
  await this.#disableTarget(target);
773
+ if (wasBlocked) {
774
+ this.requestUpdate();
775
+ }
600
776
  }
601
777
 
602
778
  async #enableTarget(target: SDK.Target.Target): Promise<void> {
@@ -708,6 +884,11 @@ export class AccessibilityAnnouncementRecordingView extends AccessibilitySubPane
708
884
  return;
709
885
  }
710
886
  this.#announcements.push(announcement);
887
+ if (this.#filteredAnnouncements !== null) {
888
+ if (this.#matchesFilter(announcement)) {
889
+ this.#filteredAnnouncements = [...this.#filteredAnnouncements, announcement];
890
+ }
891
+ }
711
892
  this.requestUpdate();
712
893
  }
713
894
 
@@ -736,9 +917,106 @@ export class AccessibilityAnnouncementRecordingView extends AccessibilitySubPane
736
917
 
737
918
  clearAnnouncements(): void {
738
919
  this.#announcements = [];
920
+ this.#filteredAnnouncements = [];
921
+ this.requestUpdate();
922
+ }
923
+
924
+ #matchesFilter(announcement: A11yAnnouncement): boolean {
925
+ if (this.#recordTypeFilter === RecordTypeFilter.ARIA_LIVE && announcement.api !== AnnouncementApi.ARIA_LIVE) {
926
+ return false;
927
+ }
928
+ if (this.#recordTypeFilter === RecordTypeFilter.JS_TRIGGERED && announcement.api !== AnnouncementApi.JS_TRIGGERED) {
929
+ return false;
930
+ }
931
+ if (this.#regexFilter && !this.#regexFilter.test(announcement.message)) {
932
+ return false;
933
+ }
934
+ return true;
935
+ }
936
+
937
+ get filteredAnnouncements(): readonly A11yAnnouncement[] {
938
+ if (this.#filteredAnnouncements !== null) {
939
+ return this.#filteredAnnouncements;
940
+ }
941
+ this.#filteredAnnouncements = this.#announcements.filter(announcement => this.#matchesFilter(announcement));
942
+ return this.#filteredAnnouncements;
943
+ }
944
+
945
+ #announceFilterMatches(): void {
946
+ const count = this.filteredAnnouncements.length;
947
+ let message: string;
948
+ if (count === 0) {
949
+ message = i18nString(UIStrings.noEventsMatch);
950
+ } else if (count === 1) {
951
+ message = i18nString(UIStrings.oneEventMatches);
952
+ } else {
953
+ message = i18nString(UIStrings.nEventsMatch, {PH1: count});
954
+ }
955
+ UI.ARIAUtils.LiveAnnouncer.alert(message);
956
+ }
957
+
958
+ setRecordTypeFilter(type: RecordTypeFilter): void {
959
+ if (this.#recordTypeFilter === type) {
960
+ return;
961
+ }
962
+ this.#recordTypeFilter = type;
963
+ this.#filteredAnnouncements = null;
964
+ this.#announceFilterMatches();
739
965
  this.requestUpdate();
740
966
  }
741
967
 
968
+ setTextFilter(text: string): void {
969
+ if (this.#textFilter === text) {
970
+ return;
971
+ }
972
+ this.#textFilter = text;
973
+ if (!text) {
974
+ this.#regexFilter = null;
975
+ } else {
976
+ try {
977
+ this.#regexFilter = new RegExp(text, 'i');
978
+ } catch {
979
+ this.#regexFilter = new RegExp('(?!)', 'i');
980
+ }
981
+ }
982
+ this.#filteredAnnouncements = null;
983
+ this.#announceFilterMatches();
984
+ this.requestUpdate();
985
+ }
986
+
987
+ override performUpdate(): void {
988
+ const blockedTargets: BlockedTargetInfo[] = [];
989
+ for (const [target, reason] of this.#blockedTargets) {
990
+ const targetName = target.name() || target.inspectedURL() || target.id();
991
+ blockedTargets.push({targetName, reason});
992
+ }
993
+
994
+ const input: ViewInput = {
995
+ isRecording: this.#isRecording,
996
+ onToggleRecording: () => {
997
+ if (this.#isRecording) {
998
+ void this.stopRecording();
999
+ } else {
1000
+ void this.startRecording();
1001
+ }
1002
+ },
1003
+ onClear: () => {
1004
+ this.clearAnnouncements();
1005
+ },
1006
+ recordTypeFilter: this.#recordTypeFilter,
1007
+ onRecordTypeFilterChange: (type: RecordTypeFilter) => {
1008
+ this.setRecordTypeFilter(type);
1009
+ },
1010
+ textFilter: this.#textFilter,
1011
+ onTextFilterChange: (text: string) => {
1012
+ this.setTextFilter(text);
1013
+ },
1014
+ blockedTargets,
1015
+ announcements: this.filteredAnnouncements,
1016
+ };
1017
+ this.#view(input, undefined, this.contentElement);
1018
+ }
1019
+
742
1020
  announcementsForTest(): A11yAnnouncement[] {
743
1021
  return [...this.#announcements];
744
1022
  }
@@ -0,0 +1,85 @@
1
+ /*
2
+ * Copyright 2026 The Chromium Authors
3
+ * Use of this source code is governed by a BSD-style license that can be
4
+ * found in the LICENSE file.
5
+ */
6
+
7
+ @scope to (devtools-widget > *) {
8
+ :scope {
9
+ font-family: inherit;
10
+ display: flex;
11
+ flex-direction: column;
12
+ flex: auto;
13
+ height: 100%;
14
+ overflow: hidden;
15
+ }
16
+
17
+ .accessibility-announcement-recording-view {
18
+ display: flex;
19
+ flex-direction: column;
20
+ flex: auto;
21
+ height: 100%;
22
+ overflow: hidden;
23
+ }
24
+
25
+ .announcements-toolbar-container {
26
+ display: flex;
27
+ flex: none;
28
+ background-color: var(--sys-color-cdt-base-container);
29
+ border-bottom: var(--sys-size-1) solid var(--sys-color-divider);
30
+ }
31
+
32
+ .announcements-toolbar {
33
+ flex: auto;
34
+ }
35
+
36
+ .announcements-toolbar select {
37
+ background-color: transparent;
38
+ border: none;
39
+ color: var(--sys-color-on-surface);
40
+ font-size: var(--sys-typescale-body4-size);
41
+ padding: var(--sys-size-2) var(--sys-size-4);
42
+ height: 100%;
43
+ cursor: pointer;
44
+ }
45
+
46
+ .toolbar-divider {
47
+ background-color: var(--sys-color-divider);
48
+ width: var(--sys-size-1);
49
+ margin: 5px var(--sys-size-3);
50
+ height: var(--sys-size-8);
51
+ }
52
+
53
+ .announcements-blocked-banner {
54
+ display: flex;
55
+ flex-direction: column;
56
+ flex: none;
57
+ gap: var(--sys-size-3);
58
+ padding: var(--sys-size-5) var(--sys-size-8);
59
+ background-color: var(--sys-color-surface-yellow);
60
+ color: var(--sys-color-on-surface-yellow);
61
+ border-bottom: var(--sys-size-1) solid var(--sys-color-divider);
62
+ font-size: var(--sys-typescale-body4-size);
63
+ line-height: var(--sys-typescale-body4-line-height);
64
+ }
65
+
66
+ .blocked-banner-header {
67
+ display: flex;
68
+ align-items: center;
69
+ gap: var(--sys-size-4);
70
+ font-weight: var(--ref-typeface-weight-medium);
71
+ }
72
+
73
+ .blocked-targets-list {
74
+ margin: 0;
75
+ padding-left: var(--sys-size-8);
76
+ }
77
+
78
+ .announcements-main-pane {
79
+ display: flex;
80
+ flex-direction: column;
81
+ flex: auto;
82
+ min-height: 0;
83
+ overflow: hidden;
84
+ }
85
+ }
@@ -391,9 +391,8 @@ function toolbarView(input: ToolbarViewInput): Lit.LitTemplate {
391
391
  @click=${input.onNewChatClick}></devtools-button>
392
392
  <div class="toolbar-divider"></div>
393
393
  <devtools-menu-button
394
- title=${i18nString(UIStrings.history)}
395
- aria-label=${i18nString(UIStrings.history)}
396
394
  .iconName=${'history'}
395
+ .accessibleLabel=${i18nString(UIStrings.history)}
397
396
  .jslogContext=${'freestyler.history'}
398
397
  .populateMenuCall=${input.populateHistoryMenu}
399
398
  ></devtools-menu-button>`
@@ -35,22 +35,18 @@ const UIStrings = {
35
35
  * the current context.
36
36
  */
37
37
  debugWithAi: 'Debug with AI',
38
- /**
39
- * @description The title of the Gemini panel.
40
- */
41
- gemini: 'Gemini',
42
38
  /**
43
39
  * @description The title of the command menu action for showing the Gemini panel.
44
40
  */
45
- showGemini: 'Show Gemini',
41
+ showGemini: 'Show `Gemini`',
46
42
  /**
47
43
  * @description The setting title to enable the Gemini via the settings tab.
48
44
  */
49
- enableGemini: 'Enable Gemini',
45
+ enableGemini: 'Enable `Gemini`',
50
46
  /**
51
47
  * @description Text of a context menu item to redirect to the Gemini panel with the current context.
52
48
  */
53
- debugWithGemini: 'Debug with Gemini',
49
+ debugWithGemini: 'Debug with `Gemini`',
54
50
  } as const;
55
51
 
56
52
  const str_ = i18n.i18n.registerUIStrings('panels/ai_assistance/ai_assistance-meta.ts', UIStrings);
@@ -108,7 +104,8 @@ UI.ViewManager.registerViewExtension({
108
104
  location: UI.ViewManager.ViewLocationValues.DRAWER_VIEW,
109
105
  id: 'freestyler',
110
106
  commandPrompt: i18nAiBrandedString(UIStrings.showGemini, UIStrings.showAiAssistance),
111
- title: i18nAiBrandedString(UIStrings.gemini, UIStrings.aiAssistance),
107
+ title: () => Root.Runtime.hostConfig.devToolsGeminiRebranding?.enabled ? i18n.i18n.lockedString('Gemini') :
108
+ i18nString(UIStrings.aiAssistance),
112
109
  order: 10,
113
110
  persistence: UI.ViewManager.ViewPersistence.CLOSEABLE,
114
111
  hasToolbar: false,
@@ -72,7 +72,7 @@ const UIStrings = {
72
72
  /**
73
73
  * @description Empty buffer hint text content in the animation timeline of the Animations panel that explains the panel.
74
74
  */
75
- animationDescription: 'On this page you can inspect and modify animations.',
75
+ animationDescription: 'On this page you can inspect and modify animations',
76
76
  /**
77
77
  * @description Tooltip text that appears when hovering over largeicon replay animation button in the animation timeline of the Animations panel.
78
78
  */