chrome-devtools-frontend 1.0.1516909 → 1.0.1519267

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 (162) hide show
  1. package/config/owner/COMMON_OWNERS +2 -2
  2. package/docs/checklist/README.md +2 -2
  3. package/docs/checklist/javascript.md +1 -1
  4. package/docs/contributing/README.md +1 -1
  5. package/docs/contributing/settings-experiments-features.md +9 -8
  6. package/docs/cookbook/devtools_on_devtools.md +2 -2
  7. package/docs/cookbook/localization.md +10 -10
  8. package/docs/devtools-protocol.md +9 -8
  9. package/docs/ecosystem/automatic_workspace_folders.md +3 -3
  10. package/docs/get_the_code.md +0 -2
  11. package/docs/styleguide/ux/components.md +166 -85
  12. package/docs/styleguide/ux/numbers.md +3 -4
  13. package/eslint.config.mjs +1 -0
  14. package/front_end/core/common/README.md +13 -12
  15. package/front_end/core/host/GdpClient.ts +16 -1
  16. package/front_end/core/host/UserMetrics.ts +4 -2
  17. package/front_end/core/root/Runtime.ts +13 -0
  18. package/front_end/core/sdk/CSSMatchedStyles.ts +5 -1
  19. package/front_end/core/sdk/EnhancedTracesParser.ts +5 -5
  20. package/front_end/core/sdk/RehydratingConnection.snapshot.txt +211 -0
  21. package/front_end/core/sdk/TargetManager.ts +4 -0
  22. package/front_end/entrypoints/main/MainImpl.ts +6 -3
  23. package/front_end/generated/InspectorBackendCommands.js +10 -7
  24. package/front_end/generated/SupportedCSSProperties.js +40 -11
  25. package/front_end/generated/protocol-mapping.d.ts +16 -1
  26. package/front_end/generated/protocol-proxy-api.d.ts +13 -1
  27. package/front_end/generated/protocol.ts +95 -0
  28. package/front_end/models/ai_assistance/agents/AiAgent.ts +57 -10
  29. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +119 -51
  30. package/front_end/models/ai_assistance/agents/StylingAgent.ts +0 -31
  31. package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.snapshot.txt +14 -181
  32. package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.ts +19 -315
  33. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +224 -50
  34. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.ts +310 -11
  35. package/front_end/models/ai_assistance/performance/AIContext.ts +15 -2
  36. package/front_end/models/ai_code_completion/AiCodeCompletion.ts +22 -11
  37. package/front_end/models/badges/AiExplorerBadge.ts +19 -3
  38. package/front_end/models/badges/Badge.ts +10 -3
  39. package/front_end/models/badges/CodeWhispererBadge.ts +3 -4
  40. package/front_end/models/badges/DOMDetectiveBadge.ts +1 -0
  41. package/front_end/models/badges/SpeedsterBadge.ts +1 -0
  42. package/front_end/models/badges/StarterBadge.ts +3 -2
  43. package/front_end/models/badges/UserBadges.ts +21 -3
  44. package/front_end/models/badges/badges.ts +1 -0
  45. package/front_end/models/javascript_metadata/NativeFunctions.js +2 -2
  46. package/front_end/models/trace/EventsSerializer.ts +4 -3
  47. package/front_end/models/trace/README.md +28 -1
  48. package/front_end/models/trace/handlers/UserInteractionsHandler.ts +101 -73
  49. package/front_end/models/trace/handlers/UserTimingsHandler.ts +1 -1
  50. package/front_end/models/trace/helpers/Timing.ts +1 -1
  51. package/front_end/models/trace/helpers/Trace.ts +99 -43
  52. package/front_end/models/trace/types/TraceEvents.ts +9 -0
  53. package/front_end/panels/accessibility/ARIAAttributesView.ts +113 -191
  54. package/front_end/panels/accessibility/AccessibilityNodeView.ts +9 -9
  55. package/front_end/panels/accessibility/AccessibilitySubPane.ts +6 -4
  56. package/front_end/panels/accessibility/accessibilityProperties.css +2 -0
  57. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +20 -3
  58. package/front_end/panels/ai_assistance/components/ChatView.ts +9 -10
  59. package/front_end/panels/ai_assistance/components/PerformanceAgentMarkdownRenderer.ts +44 -0
  60. package/front_end/panels/application/components/BounceTrackingMitigationsView.ts +2 -2
  61. package/front_end/panels/common/AiCodeCompletionDisclaimer.ts +32 -9
  62. package/front_end/panels/common/AiCodeCompletionSummaryToolbar.ts +7 -1
  63. package/front_end/panels/common/BadgeNotification.ts +21 -5
  64. package/front_end/panels/common/GdpSignUpDialog.ts +20 -12
  65. package/front_end/panels/console/ConsolePrompt.ts +1 -1
  66. package/front_end/panels/console/ConsoleView.ts +6 -2
  67. package/front_end/panels/css_overview/CSSOverviewCompletedView.ts +5 -5
  68. package/front_end/panels/elements/ElementsPanel.ts +4 -0
  69. package/front_end/panels/elements/ElementsTreeElement.ts +18 -0
  70. package/front_end/panels/elements/ElementsTreeOutline.ts +13 -0
  71. package/front_end/panels/elements/StylePropertyTreeElement.ts +21 -6
  72. package/front_end/panels/media/TickingFlameChart.ts +1 -1
  73. package/front_end/panels/profiler/HeapSnapshotView.ts +34 -19
  74. package/front_end/panels/recorder/components/RecordingView.ts +2 -2
  75. package/front_end/panels/search/SearchResultsPane.ts +167 -152
  76. package/front_end/panels/search/SearchView.ts +36 -26
  77. package/front_end/panels/search/searchResultsPane.css +9 -0
  78. package/front_end/panels/security/CookieControlsView.ts +2 -1
  79. package/front_end/panels/settings/AISettingsTab.ts +6 -3
  80. package/front_end/panels/settings/components/SyncSection.ts +39 -17
  81. package/front_end/panels/settings/emulation/components/UserAgentClientHintsForm.ts +1 -1
  82. package/front_end/panels/sources/AiCodeCompletionPlugin.ts +9 -1
  83. package/front_end/panels/sources/SourcesPanel.ts +4 -1
  84. package/front_end/panels/sources/sourcesView.css +6 -1
  85. package/front_end/panels/timeline/AppenderUtils.ts +2 -2
  86. package/front_end/panels/timeline/ExtensionTrackAppender.ts +13 -4
  87. package/front_end/panels/timeline/GPUTrackAppender.ts +2 -1
  88. package/front_end/panels/timeline/InteractionsTrackAppender.ts +5 -1
  89. package/front_end/panels/timeline/LayoutShiftsTrackAppender.ts +2 -1
  90. package/front_end/panels/timeline/ThreadAppender.ts +12 -3
  91. package/front_end/panels/timeline/TimelineFlameChartDataProvider.ts +9 -4
  92. package/front_end/panels/timeline/TimelinePanel.ts +3 -2
  93. package/front_end/panels/timeline/TimelineUIUtils.ts +5 -4
  94. package/front_end/panels/timeline/TimingsTrackAppender.ts +6 -1
  95. package/front_end/panels/timeline/components/CPUThrottlingSelector.ts +95 -82
  96. package/front_end/panels/timeline/components/LayoutShiftDetails.ts +1 -1
  97. package/front_end/panels/timeline/components/LiveMetricsView.ts +2 -2
  98. package/front_end/panels/timeline/components/NetworkRequestDetails.ts +1 -1
  99. package/front_end/panels/timeline/components/RelatedInsightChips.ts +1 -1
  100. package/front_end/panels/timeline/components/SidebarSingleInsightSet.ts +1 -1
  101. package/front_end/panels/timeline/components/cpuThrottlingSelector.css +17 -15
  102. package/front_end/panels/timeline/components/insights/BaseInsightComponent.ts +3 -0
  103. package/front_end/third_party/chromium/README.chromium +1 -1
  104. package/front_end/third_party/codemirror.next/chunk/codemirror.js +1 -1
  105. package/front_end/third_party/codemirror.next/chunk/codemirror.js.map +1 -1
  106. package/front_end/third_party/codemirror.next/codemirror.next.d.ts +6 -9
  107. package/front_end/third_party/codemirror.next/package.json +2 -1
  108. package/front_end/third_party/diff/README.chromium +1 -0
  109. package/front_end/third_party/puppeteer/README.chromium +2 -2
  110. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/bidi/core/Realm.d.ts +2 -2
  111. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/generated/injected.d.ts +1 -1
  112. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/generated/injected.js +1 -1
  113. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/generated/version.d.ts +1 -1
  114. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/generated/version.js +1 -1
  115. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/injected/injected.d.ts +1 -1
  116. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/ChromeLauncher.d.ts.map +1 -1
  117. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/ChromeLauncher.js +1 -0
  118. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/node/ChromeLauncher.js.map +1 -1
  119. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.d.ts +3 -3
  120. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js +3 -3
  121. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js.map +1 -1
  122. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Function.d.ts.map +1 -1
  123. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Function.js +16 -25
  124. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Function.js.map +1 -1
  125. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Mutex.d.ts +2 -2
  126. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +19 -28
  127. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/injected.d.ts +1 -1
  128. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/injected.js +1 -1
  129. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/version.d.ts +1 -1
  130. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/generated/version.js +1 -1
  131. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/ChromeLauncher.d.ts.map +1 -1
  132. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/ChromeLauncher.js +1 -0
  133. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/node/ChromeLauncher.js.map +1 -1
  134. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.d.ts +3 -3
  135. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js +3 -3
  136. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js.map +1 -1
  137. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/Function.d.ts.map +1 -1
  138. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/Function.js +16 -25
  139. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/Function.js.map +1 -1
  140. package/front_end/third_party/puppeteer/package/package.json +10 -3
  141. package/front_end/third_party/puppeteer/package/src/generated/injected.ts +1 -1
  142. package/front_end/third_party/puppeteer/package/src/generated/version.ts +1 -1
  143. package/front_end/third_party/puppeteer/package/src/node/ChromeLauncher.ts +1 -0
  144. package/front_end/third_party/puppeteer/package/src/revisions.ts +3 -3
  145. package/front_end/third_party/puppeteer/package/src/util/Function.ts +22 -30
  146. package/front_end/ui/components/dialogs/Dialog.ts +1 -1
  147. package/front_end/ui/components/markdown_view/MarkdownImage.ts +4 -5
  148. package/front_end/ui/components/switch/SwitchImpl.ts +12 -1
  149. package/front_end/ui/components/text_editor/config.ts +22 -9
  150. package/front_end/ui/components/tooltips/Tooltip.ts +70 -31
  151. package/front_end/ui/legacy/README.md +33 -24
  152. package/front_end/ui/legacy/SearchableView.ts +19 -26
  153. package/front_end/ui/legacy/TextPrompt.ts +166 -1
  154. package/front_end/ui/legacy/Treeoutline.ts +19 -3
  155. package/front_end/ui/legacy/UIUtils.ts +15 -2
  156. package/front_end/ui/legacy/XElement.ts +0 -43
  157. package/front_end/ui/legacy/components/perf_ui/FlameChart.ts +20 -4
  158. package/front_end/ui/legacy/components/source_frame/XMLView.ts +12 -11
  159. package/front_end/ui/lit/i18n-template.ts +5 -2
  160. package/front_end/ui/visual_logging/KnownContextValues.ts +23 -6
  161. package/front_end/ui/visual_logging/README.md +43 -27
  162. package/package.json +1 -1
@@ -33,6 +33,10 @@ const UIStringsNotTranslate = {
33
33
  */
34
34
  tooltipDisclaimerTextForAiCodeCompletionNoLogging:
35
35
  'To generate code suggestions, your console input and the history of your current console session are shared with Google. This data will not be used to improve Google’s AI models.',
36
+ /**
37
+ * Text for tooltip shown on hovering over spinner.
38
+ */
39
+ tooltipTextForSpinner: 'Shows when data is being sent to Google to generate code suggestions',
36
40
  /**
37
41
  * @description Text for tooltip button which redirects to AI settings
38
42
  */
@@ -47,6 +51,7 @@ const lockedString = i18n.i18n.lockedString;
47
51
 
48
52
  export interface ViewInput {
49
53
  disclaimerTooltipId?: string;
54
+ spinnerTooltipId?: string;
50
55
  noLogging: boolean;
51
56
  aidaAvailability?: Host.AidaClient.AidaAccessPreconditions;
52
57
  onManageInSettingsTooltipClick: () => void;
@@ -59,12 +64,14 @@ export interface ViewOutput {
59
64
 
60
65
  export type View = (input: ViewInput, output: ViewOutput, target: HTMLElement) => void;
61
66
 
62
- export const DEFAULT_SUMMARY_TOOLBAR_VIEW: View = (input, output, target) => {
63
- if (input.aidaAvailability !== Host.AidaClient.AidaAccessPreconditions.AVAILABLE || !input.disclaimerTooltipId) {
64
- render(nothing, target);
65
- return;
66
- }
67
- // clang-format off
67
+ export const DEFAULT_SUMMARY_TOOLBAR_VIEW: View =
68
+ (input, output, target) => {
69
+ if (input.aidaAvailability !== Host.AidaClient.AidaAccessPreconditions.AVAILABLE || !input.disclaimerTooltipId ||
70
+ !input.spinnerTooltipId) {
71
+ render(nothing, target);
72
+ return;
73
+ }
74
+ // clang-format off
68
75
  render(
69
76
  html`
70
77
  <style>${styles}</style>
@@ -76,7 +83,16 @@ export const DEFAULT_SUMMARY_TOOLBAR_VIEW: View = (input, output, target) => {
76
83
  el.toggleAttribute('active', isLoading);
77
84
  };
78
85
  }
79
- })}></devtools-spinner>
86
+ })}
87
+ aria-details=${input.spinnerTooltipId}
88
+ aria-describedby=${input.spinnerTooltipId}></devtools-spinner>
89
+ <devtools-tooltip
90
+ id=${input.spinnerTooltipId}
91
+ variant=${'rich'}
92
+ jslogContext=${'ai-code-completion-spinner-tooltip'}>
93
+ <div class="disclaimer-tooltip-container"><div class="tooltip-text">
94
+ ${lockedString(UIStringsNotTranslate.tooltipTextForSpinner)}
95
+ </div></div></devtools-tooltip>
80
96
  <span
81
97
  tabIndex="0"
82
98
  class="link"
@@ -115,8 +131,8 @@ export const DEFAULT_SUMMARY_TOOLBAR_VIEW: View = (input, output, target) => {
115
131
  >${lockedString(UIStringsNotTranslate.manageInSettings)}</span></div></devtools-tooltip>
116
132
  </div>
117
133
  `, target);
118
- // clang-format on
119
- };
134
+ // clang-format on
135
+ };
120
136
 
121
137
  const MINIMUM_LOADING_STATE_TIMEOUT = 1000;
122
138
 
@@ -124,6 +140,7 @@ export class AiCodeCompletionDisclaimer extends UI.Widget.Widget {
124
140
  readonly #view: View;
125
141
  #viewOutput: ViewOutput = {};
126
142
 
143
+ #spinnerTooltipId?: string;
127
144
  #disclaimerTooltipId?: string;
128
145
  #noLogging: boolean; // Whether the enterprise setting is `ALLOW_WITHOUT_LOGGING` or not.
129
146
  #loading = false;
@@ -147,6 +164,11 @@ export class AiCodeCompletionDisclaimer extends UI.Widget.Widget {
147
164
  this.requestUpdate();
148
165
  }
149
166
 
167
+ set spinnerTooltipId(spinnerTooltipId: string) {
168
+ this.#spinnerTooltipId = spinnerTooltipId;
169
+ this.requestUpdate();
170
+ }
171
+
150
172
  set loading(loading: boolean) {
151
173
  if (!loading && !this.#loading) {
152
174
  return;
@@ -191,6 +213,7 @@ export class AiCodeCompletionDisclaimer extends UI.Widget.Widget {
191
213
  this.#view(
192
214
  {
193
215
  disclaimerTooltipId: this.#disclaimerTooltipId,
216
+ spinnerTooltipId: this.#spinnerTooltipId,
194
217
  noLogging: this.#noLogging,
195
218
  aidaAvailability: this.#aidaAvailability,
196
219
  onManageInSettingsTooltipClick: this.#onManageInSettingsTooltipClick.bind(this),
@@ -30,11 +30,13 @@ const lockedString = i18n.i18n.lockedString;
30
30
  export interface AiCodeCompletionSummaryToolbarProps {
31
31
  citationsTooltipId: string;
32
32
  disclaimerTooltipId?: string;
33
+ spinnerTooltipId?: string;
33
34
  hasTopBorder?: boolean;
34
35
  }
35
36
 
36
37
  export interface ViewInput {
37
38
  disclaimerTooltipId?: string;
39
+ spinnerTooltipId?: string;
38
40
  citations?: Set<string>;
39
41
  citationsTooltipId: string;
40
42
  loading: boolean;
@@ -57,10 +59,11 @@ export const DEFAULT_SUMMARY_TOOLBAR_VIEW: View = (input, _output, target) => {
57
59
  });
58
60
 
59
61
  // clang-format off
60
- const disclaimer = input.disclaimerTooltipId ?
62
+ const disclaimer = input.disclaimerTooltipId && input.spinnerTooltipId ?
61
63
  html`<devtools-widget
62
64
  .widgetConfig=${UI.Widget.widgetConfig(AiCodeCompletionDisclaimer, {
63
65
  disclaimerTooltipId: input.disclaimerTooltipId,
66
+ spinnerTooltipId: input.spinnerTooltipId,
64
67
  loading: input.loading,
65
68
  })} class="disclaimer-widget"></devtools-widget>` : nothing;
66
69
 
@@ -102,6 +105,7 @@ export class AiCodeCompletionSummaryToolbar extends UI.Widget.Widget {
102
105
  readonly #view: View;
103
106
 
104
107
  #disclaimerTooltipId?: string;
108
+ #spinnerTooltipId?: string;
105
109
  #citationsTooltipId: string;
106
110
  #citations = new Set<string>();
107
111
  #loading = false;
@@ -113,6 +117,7 @@ export class AiCodeCompletionSummaryToolbar extends UI.Widget.Widget {
113
117
  constructor(props: AiCodeCompletionSummaryToolbarProps, view?: View) {
114
118
  super();
115
119
  this.#disclaimerTooltipId = props.disclaimerTooltipId;
120
+ this.#spinnerTooltipId = props.spinnerTooltipId;
116
121
  this.#citationsTooltipId = props.citationsTooltipId;
117
122
  this.#hasTopBorder = props.hasTopBorder ?? false;
118
123
  this.#boundOnAidaAvailabilityChange = this.#onAidaAvailabilityChange.bind(this);
@@ -147,6 +152,7 @@ export class AiCodeCompletionSummaryToolbar extends UI.Widget.Widget {
147
152
  this.#view(
148
153
  {
149
154
  disclaimerTooltipId: this.#disclaimerTooltipId,
155
+ spinnerTooltipId: this.#spinnerTooltipId,
150
156
  citations: this.#citations,
151
157
  citationsTooltipId: this.#citationsTooltipId,
152
158
  loading: this.#loading,
@@ -73,13 +73,14 @@ const AUTO_CLOSE_TIME_IN_MS = 30000;
73
73
 
74
74
  export interface BadgeNotificationAction {
75
75
  label: string;
76
- jslogContext?: string;
76
+ jslogContext: string;
77
77
  title?: string;
78
78
  onClick: () => void;
79
79
  }
80
80
 
81
81
  export interface BadgeNotificationProperties {
82
82
  message: HTMLElement|string;
83
+ jslogContext: string;
83
84
  imageUri: string;
84
85
  actions: BadgeNotificationAction[];
85
86
  isStarterBadge: boolean;
@@ -115,8 +116,8 @@ const DEFAULT_VIEW = (input: ViewInput, _output: undefined, target: HTMLElement)
115
116
 
116
117
  render(html`
117
118
  <style>${badgeNotificationStyles}</style>
118
- <div class="container">
119
- <div class="badge-container">
119
+ <div class="container" jslog=${VisualLogging.dialog('badge-notification')}>
120
+ <div class="badge-container" jslog=${VisualLogging.item(input.jslogContext)}>
120
121
  <img class="badge-image" role="presentation" src=${input.imageUri}>
121
122
  </div>
122
123
  <div class="action-and-text-container">
@@ -138,6 +139,7 @@ function revealBadgeSettings(): void {
138
139
  }
139
140
 
140
141
  export class BadgeNotification extends UI.Widget.Widget {
142
+ jslogContext = '';
141
143
  message: HTMLElement|string = '';
142
144
  imageUri = '';
143
145
  actions: BadgeNotificationAction[] = [];
@@ -175,6 +177,7 @@ export class BadgeNotification extends UI.Widget.Widget {
175
177
  this.imageUri = properties.imageUri;
176
178
  this.actions = properties.actions;
177
179
  this.isStarterBadge = properties.isStarterBadge;
180
+ this.jslogContext = properties.jslogContext;
178
181
  this.requestUpdate();
179
182
  this.show(document.body);
180
183
 
@@ -193,7 +196,7 @@ export class BadgeNotification extends UI.Widget.Widget {
193
196
  const receiveBadgesSettingEnabled = Badges.UserBadges.instance().isReceiveBadgesSettingEnabled();
194
197
  const googleDeveloperProgramLink = UI.XLink.XLink.create(
195
198
  'https://developers.google.com/program', lockedString('Google Developer Program'), 'badge-link', undefined,
196
- 'gdp.program-link');
199
+ 'program-link');
197
200
 
198
201
  // If the user already has a GDP profile and the receive badges setting enabled,
199
202
  // starter badge behaves as if it's an activity based badge.
@@ -208,9 +211,11 @@ export class BadgeNotification extends UI.Widget.Widget {
208
211
  this.#show({
209
212
  message: i18nFormatString(
210
213
  UIStrings.starterBadgeAwardMessageSettingDisabled, {PH1: badge.title, PH2: googleDeveloperProgramLink}),
214
+ jslogContext: badge.jslogContext,
211
215
  actions: [
212
216
  {
213
217
  label: i18nString(UIStrings.remindMeLater),
218
+ jslogContext: 'remind-me-later',
214
219
  onClick: () => {
215
220
  this.detach();
216
221
  Badges.UserBadges.instance().snoozeStarterBadge();
@@ -218,6 +223,7 @@ export class BadgeNotification extends UI.Widget.Widget {
218
223
  },
219
224
  {
220
225
  label: i18nString(UIStrings.receiveBadges),
226
+ jslogContext: 'receive-badges',
221
227
  onClick: () => {
222
228
  this.detach();
223
229
  revealBadgeSettings();
@@ -234,9 +240,11 @@ export class BadgeNotification extends UI.Widget.Widget {
234
240
  this.#show({
235
241
  message: i18nFormatString(
236
242
  UIStrings.starterBadgeAwardMessageNoGdpProfile, {PH1: badge.title, PH2: googleDeveloperProgramLink}),
243
+ jslogContext: badge.jslogContext,
237
244
  actions: [
238
245
  {
239
246
  label: i18nString(UIStrings.remindMeLater),
247
+ jslogContext: 'remind-me-later',
240
248
  onClick: () => {
241
249
  this.detach();
242
250
  Badges.UserBadges.instance().snoozeStarterBadge();
@@ -244,9 +252,13 @@ export class BadgeNotification extends UI.Widget.Widget {
244
252
  },
245
253
  {
246
254
  label: i18nString(UIStrings.createProfile),
255
+ jslogContext: 'create-profile',
247
256
  onClick: () => {
248
257
  this.detach();
249
- GdpSignUpDialog.GdpSignUpDialog.show();
258
+ GdpSignUpDialog.GdpSignUpDialog.show({
259
+ // We want to consider cancelling from the starter badge as a "snooze" for starter badge.
260
+ onCancel: () => Badges.UserBadges.instance().snoozeStarterBadge(),
261
+ });
250
262
  }
251
263
  }
252
264
  ],
@@ -258,9 +270,11 @@ export class BadgeNotification extends UI.Widget.Widget {
258
270
  #presentActivityBasedBadge(badge: Badges.Badge): void {
259
271
  this.#show({
260
272
  message: i18nString(UIStrings.activityBasedBadgeAwardMessage, {PH1: badge.title}),
273
+ jslogContext: badge.jslogContext,
261
274
  actions: [
262
275
  {
263
276
  label: i18nString(UIStrings.manageSettings),
277
+ jslogContext: 'manage-settings',
264
278
  onClick: () => {
265
279
  this.detach();
266
280
  revealBadgeSettings();
@@ -268,6 +282,7 @@ export class BadgeNotification extends UI.Widget.Widget {
268
282
  },
269
283
  {
270
284
  label: i18nString(UIStrings.viewProfile),
285
+ jslogContext: 'view-profile',
271
286
  onClick: () => {
272
287
  UI.UIUtils.openInNewTab(Host.GdpClient.GOOGLE_DEVELOPER_PROGRAM_PROFILE_LINK);
273
288
  }
@@ -310,6 +325,7 @@ export class BadgeNotification extends UI.Widget.Widget {
310
325
  actions: this.actions,
311
326
  isStarterBadge: this.isStarterBadge,
312
327
  onDismissClick: this.#onDismissClick,
328
+ jslogContext: this.jslogContext,
313
329
  };
314
330
  this.#view(viewInput, undefined, this.contentElement);
315
331
  }
@@ -14,7 +14,6 @@ import * as Snackbars from '../../ui/components/snackbars/snackbars.js';
14
14
  import type * as Switch from '../../ui/components/switch/switch.js';
15
15
  import * as UI from '../../ui/legacy/legacy.js';
16
16
  import {html, render} from '../../ui/lit/lit.js';
17
- import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
18
17
 
19
18
  import styles from './gdpSignUpDialog.css.js';
20
19
 
@@ -114,7 +113,7 @@ export const DEFAULT_VIEW: View = (input, _output, target): void => {
114
113
  render(
115
114
  html`
116
115
  <style>${styles}</style>
117
- <div class="gdp-sign-up-dialog-header" role="img" tabindex="0" aria-label="Google Developer Program"></div>
116
+ <div class="gdp-sign-up-dialog-header" role="img" aria-label="Google Developer Program"></div>
118
117
  <div class="main-content">
119
118
  <div class="section">
120
119
  <div class="icon-container">
@@ -136,9 +135,9 @@ export const DEFAULT_VIEW: View = (input, _output, target): void => {
136
135
  <div class="switch-container">
137
136
  <devtools-switch
138
137
  .checked=${input.keepMeUpdated}
138
+ .jslogContext=${'keep-me-updated'}
139
+ .label=${i18nString(UIStrings.keepUpdated)}
139
140
  @switchchange=${(e: Switch.Switch.SwitchChangeEvent) => input.onKeepMeUpdatedChange(e.checked)}
140
- jslog=${VisualLogging.toggle('gdp.signup.keep-me-updated').track({ click: true })}
141
- aria-label=${i18nString(UIStrings.keepUpdated)}
142
141
  >
143
142
  </devtools-switch>
144
143
  </div>
@@ -152,11 +151,11 @@ export const DEFAULT_VIEW: View = (input, _output, target): void => {
152
151
  <div class="section-text">
153
152
  <div>${i18nString(UIStrings.tailorProfileBody)}</div><br/>
154
153
  <div>${i18n.i18n.getFormatLocalizedString(str_, UIStrings.tailorProfileBodyDisclaimer, {
155
- PH1: UI.XLink.XLink.create(CONTENT_POLICY_URL, i18nString(UIStrings.contentPolicy), 'link', undefined, 'gdp.content-policy'),
154
+ PH1: UI.XLink.XLink.create(CONTENT_POLICY_URL, i18nString(UIStrings.contentPolicy), 'link', undefined, 'content-policy'),
156
155
  PH2: UI.XLink.XLink.create(TERMS_OF_SERVICE_URL, i18nString(UIStrings.termsOfService), 'link',
157
- undefined, 'gdp.terms-of-service'),
156
+ undefined, 'terms-of-service'),
158
157
  PH3: UI.XLink.XLink.create(PRIVACY_POLICY_URL, i18nString(UIStrings.privacyPolicy), 'link',
159
- undefined, 'gdp.privacy-policy'),
158
+ undefined, 'privacy-policy'),
160
159
  })}</div>
161
160
  </div>
162
161
  </div>
@@ -176,7 +175,7 @@ export const DEFAULT_VIEW: View = (input, _output, target): void => {
176
175
  @click=${input.onCancelClick}>${i18nString(UIStrings.cancel)}</devtools-button>
177
176
  <devtools-button
178
177
  .variant=${Buttons.Button.Variant.PRIMARY}
179
- .jslogContext=${'gdp.sign-up'}
178
+ .jslogContext=${'sign-up'}
180
179
  .spinner=${input.isSigningUp}
181
180
  .disabled=${input.isSigningUp}
182
181
  @click=${input.onSignUpClick}>${i18nString(UIStrings.signUp)}</devtools-button>
@@ -194,11 +193,19 @@ export class GdpSignUpDialog extends UI.Widget.VBox {
194
193
  #keepMeUpdated = false;
195
194
  #isSigningUp = false;
196
195
  #onSuccess?: () => void;
196
+ #onCancel?: () => void;
197
197
 
198
- constructor(options: {dialog: UI.Dialog.Dialog, onSuccess?: () => void}, view?: View) {
198
+ constructor(
199
+ options: {
200
+ dialog: UI.Dialog.Dialog,
201
+ onSuccess?: () => void,
202
+ onCancel?: () => void,
203
+ },
204
+ view?: View) {
199
205
  super();
200
206
  this.#dialog = options.dialog;
201
207
  this.#onSuccess = options.onSuccess;
208
+ this.#onCancel = options.onCancel;
202
209
  this.#view = view ?? DEFAULT_VIEW;
203
210
  this.requestUpdate();
204
211
  }
@@ -231,6 +238,7 @@ export class GdpSignUpDialog extends UI.Widget.VBox {
231
238
  onSignUpClick: this.#onSignUpClick.bind(this),
232
239
  onCancelClick: () => {
233
240
  this.#dialog.hide();
241
+ this.#onCancel?.();
234
242
  },
235
243
  keepMeUpdated: this.#keepMeUpdated,
236
244
  onKeepMeUpdatedChange: (value: boolean) => {
@@ -243,14 +251,14 @@ export class GdpSignUpDialog extends UI.Widget.VBox {
243
251
  this.#view(viewInput, undefined, this.contentElement);
244
252
  }
245
253
 
246
- static show({onSuccess}: {onSuccess?: () => void} = {}): void {
247
- const dialog = new UI.Dialog.Dialog();
254
+ static show({onSuccess, onCancel}: {onSuccess?: () => void, onCancel?: () => void} = {}): void {
255
+ const dialog = new UI.Dialog.Dialog('gdp-sign-up-dialog');
248
256
  dialog.setAriaLabel(i18nString(UIStrings.gdpDialogAriaLabel));
249
257
  dialog.setMaxContentSize(new Geometry.Size(384, 500));
250
258
  dialog.setSizeBehavior(UI.GlassPane.SizeBehavior.SET_EXACT_WIDTH_MAX_HEIGHT);
251
259
  dialog.setDimmed(true);
252
260
 
253
- new GdpSignUpDialog({dialog, onSuccess}).show(dialog.contentElement);
261
+ new GdpSignUpDialog({dialog, onSuccess, onCancel}).show(dialog.contentElement);
254
262
  dialog.show(undefined, /* stack */ true);
255
263
  }
256
264
  }
@@ -529,7 +529,7 @@ export class ConsolePrompt extends Common.ObjectWrapper.eventMixin<EventTypes, t
529
529
  this.teaser = undefined;
530
530
  }
531
531
  this.aiCodeCompletion = new AiCodeCompletion.AiCodeCompletion.AiCodeCompletion(
532
- {aidaClient: this.aidaClient}, this.editor, AiCodeCompletion.AiCodeCompletion.Panel.CONSOLE, ['\n\n']);
532
+ {aidaClient: this.aidaClient}, this.editor, AiCodeCompletion.AiCodeCompletion.ContextFlavor.CONSOLE, ['\n\n']);
533
533
  this.aiCodeCompletion.addEventListener(AiCodeCompletion.AiCodeCompletion.Events.RESPONSE_RECEIVED, event => {
534
534
  this.aiCodeCompletionCitations = event.data.citations;
535
535
  this.dispatchEventToListeners(Events.AI_CODE_COMPLETION_RESPONSE_RECEIVED, event.data);
@@ -270,6 +270,7 @@ let consoleViewInstance: ConsoleView;
270
270
 
271
271
  const MIN_HISTORY_LENGTH_FOR_DISABLING_SELF_XSS_WARNING = 5;
272
272
  const DISCLAIMER_TOOLTIP_ID = 'console-ai-code-completion-disclaimer-tooltip';
273
+ const SPINNER_TOOLTIP_ID = 'console-ai-code-completion-spinner-tooltip';
273
274
  const CITATIONS_TOOLTIP_ID = 'console-ai-code-completion-citations-tooltip';
274
275
 
275
276
  export class ConsoleView extends UI.Widget.VBox implements
@@ -624,8 +625,11 @@ export class ConsoleView extends UI.Widget.VBox implements
624
625
  }
625
626
 
626
627
  createAiCodeCompletionSummaryToolbar(): void {
627
- this.aiCodeCompletionSummaryToolbar = new AiCodeCompletionSummaryToolbar(
628
- {citationsTooltipId: CITATIONS_TOOLTIP_ID, disclaimerTooltipId: DISCLAIMER_TOOLTIP_ID});
628
+ this.aiCodeCompletionSummaryToolbar = new AiCodeCompletionSummaryToolbar({
629
+ citationsTooltipId: CITATIONS_TOOLTIP_ID,
630
+ disclaimerTooltipId: DISCLAIMER_TOOLTIP_ID,
631
+ spinnerTooltipId: SPINNER_TOOLTIP_ID
632
+ });
629
633
  this.aiCodeCompletionSummaryToolbarContainer = this.element.createChild('div');
630
634
  this.aiCodeCompletionSummaryToolbar.show(this.aiCodeCompletionSummaryToolbarContainer, undefined, true);
631
635
  }
@@ -15,7 +15,7 @@ import * as Geometry from '../../models/geometry/geometry.js';
15
15
  import * as TextUtils from '../../models/text_utils/text_utils.js';
16
16
  import * as Components from '../../ui/legacy/components/utils/utils.js';
17
17
  import * as UI from '../../ui/legacy/legacy.js';
18
- import {Directives, html, nothing, render, type TemplateResult} from '../../ui/lit/lit.js';
18
+ import {Directives, html, type LitTemplate, nothing, render, type TemplateResult} from '../../ui/lit/lit.js';
19
19
  import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
20
20
 
21
21
  import cssOverviewCompletedViewStyles from './cssOverviewCompletedView.css.js';
@@ -519,10 +519,10 @@ function renderContrastIssue(key: string, issues: ContrastIssue[]): TemplateResu
519
519
  // clang-format on
520
520
  }
521
521
 
522
- function renderColor(section: string, color: string): TemplateResult {
522
+ function renderColor(section: string, color: string): LitTemplate {
523
523
  const borderColor = Common.Color.parse(color)?.asLegacyColor();
524
524
  if (!borderColor) {
525
- return html``;
525
+ return nothing;
526
526
  }
527
527
  // clang-format off
528
528
  return html`<li>
@@ -1006,9 +1006,9 @@ export class ElementDetailsView extends UI.Widget.Widget {
1006
1006
  }
1007
1007
  }
1008
1008
 
1009
- function renderNode(data: PopulateNodesEventNodeTypes, link?: HTMLElement, showNode?: () => void): TemplateResult {
1009
+ function renderNode(data: PopulateNodesEventNodeTypes, link?: HTMLElement, showNode?: () => void): LitTemplate {
1010
1010
  if (!link) {
1011
- return html``;
1011
+ return nothing;
1012
1012
  }
1013
1013
  return html`
1014
1014
  <td>
@@ -774,6 +774,10 @@ export class ElementsPanel extends UI.Panel.Panel implements UI.SearchableView.S
774
774
  this.#domTreeWidget.selectDOMNode(node, focus);
775
775
  }
776
776
 
777
+ highlightNodeAttribute(node: SDK.DOMModel.DOMNode, attribute: string): void {
778
+ this.#domTreeWidget.highlightNodeAttribute(node, attribute);
779
+ }
780
+
777
781
  selectAndShowSidebarTab(tabId: SidebarPaneTabId): void {
778
782
  if (!this.sidebarPaneView) {
779
783
  return;
@@ -488,6 +488,24 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
488
488
  }
489
489
  }
490
490
 
491
+ highlightAttribute(attributeName: string): void {
492
+ // If the attribute is not found, we highlight the tag name instead.
493
+ let animationElement = this.listItemElement.querySelector('.webkit-html-tag-name') ?? this.listItemElement;
494
+
495
+ if (this.nodeInternal.getAttribute(attributeName) !== undefined) {
496
+ const tag = this.listItemElement.getElementsByClassName('webkit-html-tag')[0];
497
+ const attributes = tag.getElementsByClassName('webkit-html-attribute');
498
+ for (const attribute of attributes) {
499
+ const attributeElement = attribute.getElementsByClassName('webkit-html-attribute-name')[0];
500
+ if (attributeElement.textContent === attributeName) {
501
+ animationElement = attributeElement;
502
+ break;
503
+ }
504
+ }
505
+ }
506
+ UI.UIUtils.runCSSAnimationOnce(animationElement, 'dom-update-highlight');
507
+ }
508
+
491
509
  isClosingTag(): boolean {
492
510
  return !isOpeningTag(this.tagTypeContext);
493
511
  }
@@ -260,6 +260,10 @@ export class DOMTreeWidget extends UI.Widget.Widget {
260
260
  this.#viewOutput?.elementsTreeOutline?.selectDOMNode(node, focus);
261
261
  }
262
262
 
263
+ highlightNodeAttribute(node: SDK.DOMModel.DOMNode, attribute: string): void {
264
+ this.#viewOutput?.elementsTreeOutline?.highlightNodeAttribute(node, attribute);
265
+ }
266
+
263
267
  setWordWrap(wrap: boolean): void {
264
268
  this.#wrap = wrap;
265
269
  this.performUpdate();
@@ -1006,6 +1010,15 @@ export class ElementsTreeOutline extends
1006
1010
  treeElement.revealAndSelect(omitFocus);
1007
1011
  }
1008
1012
 
1013
+ highlightNodeAttribute(node: SDK.DOMModel.DOMNode, attribute: string): void {
1014
+ const treeElement = this.findTreeElement(node);
1015
+ if (!treeElement) {
1016
+ return;
1017
+ }
1018
+ treeElement.reveal();
1019
+ treeElement.highlightAttribute(attribute);
1020
+ }
1021
+
1009
1022
  treeElementFromEventInternal(event: MouseEvent): UI.TreeOutline.TreeElement|null {
1010
1023
  const scrollContainer = this.element.parentElement;
1011
1024
  if (!scrollContainer) {
@@ -52,7 +52,7 @@ import {
52
52
  StylesSidebarPane,
53
53
  } from './StylesSidebarPane.js';
54
54
 
55
- const {html, nothing, render, Directives: {classMap, ifDefined}} = Lit;
55
+ const {html, nothing, render, Directives: {classMap}} = Lit;
56
56
  const ASTUtils = SDK.CSSPropertyParser.ASTUtils;
57
57
  const FlexboxEditor = ElementsComponents.StylePropertyEditor.FlexboxEditor;
58
58
  const GridEditor = ElementsComponents.StylePropertyEditor.GridEditor;
@@ -446,19 +446,25 @@ export class AttributeRenderer extends rendererBase(SDK.CSSPropertyParserMatcher
446
446
  const attrCall =
447
447
  this.#treeElement?.getTracingTooltip('attr', match.node, this.#matchedStyles, this.#computedStyles, context);
448
448
  const tooltipId = attributeMissing ? undefined : this.#treeElement?.getTooltipId('custom-attribute');
449
+ const tooltip = tooltipId ? {tooltipId} : undefined;
449
450
  // clang-format off
450
451
  render(html`
451
452
  <span data-title=${computedValue || ''}
452
453
  jslog=${VisualLogging.link('css-variable').track({click: true, hover: true})}
453
- >${attrCall ?? 'attr'}(<span class=${attributeClass} aria-details=${ifDefined(tooltipId)}>${match.name}</span>${
454
- match.type ? html` <span class=${typeClass}>${match.type}</span>` : nothing
455
- }${renderedFallback ? html`, <span class=${fallbackClass}>${renderedFallback.nodes}</span>` : nothing
456
- })</span>${tooltipId ? html`
454
+ >${attrCall ?? 'attr'}(<devtools-link-swatch class=${attributeClass} .data=${{
455
+ tooltip,
456
+ text: match.name,
457
+ isDefined: true,
458
+ onLinkActivate: () => this.#handleAttributeActivate(this.#matchedStyles.originatingNodeForStyle(match.style), match.name),
459
+ }}></devtools-link-swatch>${tooltipId ? html`
457
460
  <devtools-tooltip
458
461
  id=${tooltipId}
459
462
  variant=rich
460
463
  jslogContext=elements.css-var
461
- >${JSON.stringify(rawValue)}</devtools-tooltip>` : ''}`, varSwatch);
464
+ >${JSON.stringify(rawValue)}</devtools-tooltip>` : nothing}${
465
+ match.type ? html` <span class=${typeClass}>${match.type}</span>` : nothing
466
+ }${renderedFallback ? html`, <span class=${fallbackClass}>${renderedFallback.nodes}</span>` : nothing
467
+ })</span>`, varSwatch);
462
468
  // clang-format on
463
469
 
464
470
  const color = computedValue && Common.Color.parse(computedValue);
@@ -478,6 +484,15 @@ export class AttributeRenderer extends rendererBase(SDK.CSSPropertyParserMatcher
478
484
 
479
485
  return [colorSwatch, varSwatch];
480
486
  }
487
+
488
+ #handleAttributeActivate(node: SDK.DOMModel.DOMNode|null, attribute: string): void {
489
+ if (!node) {
490
+ return;
491
+ }
492
+ Host.userMetrics.actionTaken(Host.UserMetrics.Action.AttributeLinkClicked);
493
+ Host.userMetrics.swatchActivated(Host.UserMetrics.SwatchType.ATTR_LINK);
494
+ ElementsPanel.instance().highlightNodeAttribute(node, attribute);
495
+ }
481
496
  }
482
497
 
483
498
  // clang-format off
@@ -19,7 +19,7 @@ function getGroupDefaultTextColor(): string {
19
19
  const DefaultStyle: () => PerfUI.FlameChart.GroupStyle = () => ({
20
20
  height: 20,
21
21
  padding: 2,
22
- collapsible: false,
22
+ collapsible: PerfUI.FlameChart.GroupCollapsibleState.NEVER,
23
23
  font: defaultFont,
24
24
  color: getGroupDefaultTextColor(),
25
25
  backgroundColor: 'rgba(100 0 0 / 10%)',