chrome-devtools-frontend 1.0.1574367 → 1.0.1575635

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 (107) hide show
  1. package/.stylelintrc.json +1 -1
  2. package/AUTHORS +1 -0
  3. package/docs/contributing/settings-experiments-features.md +5 -2
  4. package/front_end/core/common/Settings.ts +1 -1
  5. package/front_end/core/host/InspectorFrontendHostAPI.ts +7 -1
  6. package/front_end/core/host/InspectorFrontendHostStub.ts +10 -0
  7. package/front_end/core/host/UserMetrics.ts +15 -0
  8. package/front_end/core/root/Runtime.ts +119 -39
  9. package/front_end/core/sdk/CSSMatchedStyles.ts +27 -0
  10. package/front_end/entrypoints/main/MainImpl.ts +25 -7
  11. package/front_end/entrypoints/node_app/app/NodeConnectionsPanel.ts +3 -2
  12. package/front_end/generated/InspectorBackendCommands.ts +37 -0
  13. package/front_end/generated/protocol-mapping.d.ts +257 -0
  14. package/front_end/generated/protocol-proxy-api.d.ts +256 -0
  15. package/front_end/generated/protocol.ts +359 -0
  16. package/front_end/models/ai_assistance/AiConversation.ts +31 -10
  17. package/front_end/models/ai_assistance/AiHistoryStorage.ts +1 -0
  18. package/front_end/models/ai_assistance/agents/AiAgent.ts +29 -5
  19. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.snapshot.txt +73 -0
  20. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +141 -0
  21. package/front_end/models/ai_assistance/agents/StylingAgent.snapshot.txt +3 -3
  22. package/front_end/models/ai_assistance/agents/StylingAgent.ts +5 -4
  23. package/front_end/models/ai_assistance/ai_assistance.ts +2 -0
  24. package/front_end/models/issues_manager/CorsIssue.ts +0 -3
  25. package/front_end/models/javascript_metadata/NativeFunctions.js +5 -0
  26. package/front_end/models/live-metrics/LiveMetrics.ts +31 -51
  27. package/front_end/models/stack_trace/StackTrace.ts +41 -0
  28. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +72 -6
  29. package/front_end/panels/ai_assistance/ai_assistance-meta.ts +1 -0
  30. package/front_end/panels/ai_assistance/components/ChatInput.ts +24 -2
  31. package/front_end/panels/ai_assistance/components/ChatMessage.ts +6 -4
  32. package/front_end/panels/ai_assistance/components/ChatView.ts +5 -6
  33. package/front_end/panels/ai_assistance/components/chatView.css +10 -0
  34. package/front_end/panels/application/StorageView.ts +3 -3
  35. package/front_end/panels/application/preloading/components/preloadingDisabledInfobar.css +1 -1
  36. package/front_end/panels/common/AiCodeGenerationUpgradeDialog.ts +32 -16
  37. package/front_end/panels/common/aiCodeCompletionSummaryToolbar.css +1 -1
  38. package/front_end/panels/console/ConsoleContextSelector.ts +4 -3
  39. package/front_end/panels/console/ConsoleFormat.ts +31 -2
  40. package/front_end/panels/console/ConsoleInsightTeaser.ts +6 -1
  41. package/front_end/panels/elements/ElementsTreeElement.ts +46 -2
  42. package/front_end/panels/elements/ElementsTreeOutline.ts +1 -1
  43. package/front_end/panels/elements/StylePropertyTreeElement.ts +63 -0
  44. package/front_end/panels/elements/StylesSidebarPane.ts +17 -2
  45. package/front_end/panels/elements/stylePropertiesTreeOutline.css +4 -0
  46. package/front_end/panels/issues/AffectedMetadataAllowedSitesView.ts +3 -3
  47. package/front_end/panels/layer_viewer/Layers3DView.ts +2 -1
  48. package/front_end/panels/lighthouse/LighthouseStartView.ts +7 -6
  49. package/front_end/panels/linear_memory_inspector/LinearMemoryInspectorPane.ts +3 -2
  50. package/front_end/panels/network/RequestConditionsDrawer.ts +2 -3
  51. package/front_end/panels/network/SignedExchangeInfoView.ts +3 -4
  52. package/front_end/panels/network/components/HeaderSectionRow.css +1 -1
  53. package/front_end/panels/recorder/components/RecordingView.ts +2 -2
  54. package/front_end/panels/recorder/components/recordingView.css +5 -0
  55. package/front_end/panels/security/SecurityPanelSidebar.ts +10 -9
  56. package/front_end/panels/settings/AISettingsTab.ts +2 -1
  57. package/front_end/panels/settings/KeybindsSettingsTab.ts +3 -5
  58. package/front_end/panels/settings/SettingsScreen.ts +14 -9
  59. package/front_end/panels/settings/settings-meta.ts +1 -0
  60. package/front_end/panels/sources/CallStackSidebarPane.ts +10 -0
  61. package/front_end/panels/sources/DebuggerPlugin.ts +18 -12
  62. package/front_end/panels/sources/ScopeChainSidebarPane.ts +4 -3
  63. package/front_end/panels/sources/SourcesNavigator.ts +2 -2
  64. package/front_end/panels/sources/SourcesPanel.ts +6 -3
  65. package/front_end/panels/sources/TabbedEditorContainer.ts +2 -2
  66. package/front_end/panels/sources/sources-meta.ts +2 -1
  67. package/front_end/panels/timeline/TimelinePanel.ts +2 -1
  68. package/front_end/panels/timeline/TimelineUIUtils.ts +2 -2
  69. package/front_end/panels/timeline/components/DetailsView.ts +7 -7
  70. package/front_end/panels/timeline/components/insights/BaseInsightComponent.ts +2 -2
  71. package/front_end/panels/timeline/overlays/components/EntryLabelOverlay.ts +4 -4
  72. package/front_end/panels/whats_new/ReleaseNoteText.ts +11 -11
  73. package/front_end/panels/whats_new/releaseNoteView.css +1 -1
  74. package/front_end/panels/whats_new/resources/WNDT.md +6 -6
  75. package/front_end/third_party/chromium/README.chromium +1 -1
  76. package/front_end/third_party/puppeteer/README.chromium +2 -2
  77. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/TargetManager.d.ts.map +1 -1
  78. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/TargetManager.js +30 -25
  79. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/TargetManager.js.map +1 -1
  80. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/injected/injected.d.ts +1 -1
  81. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/Mutex.d.ts +2 -2
  82. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.d.ts +1 -1
  83. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.js +1 -1
  84. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +30 -21
  85. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/TargetManager.d.ts.map +1 -1
  86. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/TargetManager.js +31 -26
  87. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/TargetManager.js.map +1 -1
  88. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.d.ts +1 -1
  89. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.js +1 -1
  90. package/front_end/third_party/puppeteer/package/package.json +3 -3
  91. package/front_end/third_party/puppeteer/package/src/cdp/TargetManager.ts +45 -44
  92. package/front_end/third_party/puppeteer/package/src/util/version.ts +1 -1
  93. package/front_end/ui/components/buttons/Button.ts +2 -2
  94. package/front_end/ui/components/panel_feedback/PreviewToggle.ts +1 -1
  95. package/front_end/ui/components/panel_feedback/panelFeedback.css +1 -1
  96. package/front_end/ui/kit/link/Link.ts +31 -0
  97. package/front_end/ui/legacy/LinkContextMenuProvider.ts +3 -5
  98. package/front_end/ui/legacy/components/object_ui/ObjectPopoverHelper.ts +2 -2
  99. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +336 -250
  100. package/front_end/ui/legacy/inspectorCommon.css +0 -1
  101. package/front_end/ui/legacy/legacy.ts +0 -4
  102. package/front_end/ui/visual_logging/KnownContextValues.ts +4 -0
  103. package/front_end/ui/visual_logging/LoggingDriver.ts +10 -6
  104. package/mcp/HostBindings.ts +6 -0
  105. package/package.json +1 -1
  106. package/front_end/ui/legacy/XElement.ts +0 -41
  107. package/front_end/ui/legacy/XLink.ts +0 -128
@@ -151,7 +151,7 @@ const UIStringsNotTranslate = {
151
151
  */
152
152
  accountAvatar: 'Account avatar',
153
153
  /**
154
- * @description Title for the x-link which wraps the image input rendered in chat messages.
154
+ * @description Title for the link which wraps the image input rendered in chat messages.
155
155
  */
156
156
  openImageInNewTab: 'Open image in a new tab',
157
157
  /**
@@ -250,7 +250,7 @@ export interface MessageInput {
250
250
  isReadOnly: boolean;
251
251
  isLastMessage: boolean;
252
252
  canShowFeedbackForm: boolean;
253
- userInfo: Pick<Host.InspectorFrontendHostAPI.SyncInformation, 'accountImage'|'accountFullName'>;
253
+ userInfo: Pick<Host.InspectorFrontendHostAPI.SyncInformation, 'accountImage'|'accountFullName'|'accountGivenName'>;
254
254
  markdownRenderer: MarkdownLitRenderer;
255
255
  onSuggestionClick: (suggestion: string) => void;
256
256
  onFeedbackSubmit: (rpcId: Host.AidaClient.RpcGlobalId, rate: Host.AidaClient.Rating, feedback?: string) => void;
@@ -260,7 +260,8 @@ export interface MessageInput {
260
260
  export const DEFAULT_VIEW = (input: ChatMessageViewInput, output: ViewOutput, target: HTMLElement): void => {
261
261
  const message = input.message;
262
262
  if (message.entity === ChatMessageEntity.USER) {
263
- const name = input.userInfo.accountFullName || lockedString(UIStringsNotTranslate.you);
263
+ const givenName = AiAssistanceModel.AiUtils.isGeminiBranding() ? input.userInfo.accountGivenName : '';
264
+ const name = givenName || input.userInfo.accountFullName || lockedString(UIStringsNotTranslate.you);
264
265
  const image = input.userInfo.accountImage ?
265
266
  html`<img src="data:image/png;base64, ${input.userInfo.accountImage}" alt=${
266
267
  UIStringsNotTranslate.accountAvatar} />` :
@@ -748,7 +749,8 @@ export class ChatMessage extends UI.Widget.Widget {
748
749
  isReadOnly = false;
749
750
  canShowFeedbackForm = false;
750
751
  isLastMessage = false;
751
- userInfo: Pick<Host.InspectorFrontendHostAPI.SyncInformation, 'accountImage'|'accountFullName'> = {};
752
+ userInfo:
753
+ Pick<Host.InspectorFrontendHostAPI.SyncInformation, 'accountImage'|'accountFullName'|'accountGivenName'> = {};
752
754
  markdownRenderer!: MarkdownLitRenderer;
753
755
  onSuggestionClick: (suggestion: string) => void = () => {};
754
756
  onFeedbackSubmit:
@@ -62,7 +62,7 @@ export interface Props {
62
62
  selectedContext: AiAssistanceModel.AiAgent.ConversationContext<unknown>|null;
63
63
  isLoading: boolean;
64
64
  canShowFeedbackForm: boolean;
65
- userInfo: Pick<Host.InspectorFrontendHostAPI.SyncInformation, 'accountImage'|'accountFullName'>;
65
+ userInfo: Pick<Host.InspectorFrontendHostAPI.SyncInformation, 'accountImage'|'accountGivenName'>;
66
66
  conversationType: AiAssistanceModel.AiHistoryStorage.ConversationType;
67
67
  isReadOnly: boolean;
68
68
  blockedByCrossOrigin: boolean;
@@ -78,7 +78,7 @@ export interface Props {
78
78
  }
79
79
 
80
80
  interface ChatWidgetInput extends Props {
81
- accountName: string;
81
+ accountGivenName: string;
82
82
  handleScroll: (ev: Event) => void;
83
83
  handleSuggestionClick: (title: string) => void;
84
84
  handleMessageContainerRef: (el: Element|undefined) => void;
@@ -133,8 +133,8 @@ const DEFAULT_VIEW: View = (input, output, target) => {
133
133
  </div>
134
134
  ${AiAssistanceModel.AiUtils.isGeminiBranding() ?
135
135
  html`
136
- <h1 class='greeting'>Hello, ${input.accountName}</h1>
137
- <h1>${lockedString(UIStringsNotTranslate.emptyStateTextGemini)}</h1>
136
+ <h1 class='greeting'>Hello${input.accountGivenName ? `, ${input.accountGivenName}` : ''}</h1>
137
+ <p class='cta'>${lockedString(UIStringsNotTranslate.emptyStateTextGemini)}</p>
138
138
  ` : html`<h1>${lockedString(UIStringsNotTranslate.emptyStateText)}</h1>`
139
139
  }
140
140
  </div>
@@ -323,8 +323,7 @@ export class ChatView extends HTMLElement {
323
323
  this.#view(
324
324
  {
325
325
  ...this.#props,
326
- // TODO(b/468206227): This needs to be a first name.
327
- accountName: this.#props.userInfo.accountFullName ?? '',
326
+ accountGivenName: this.#props.userInfo.accountGivenName ?? '',
328
327
  handleScroll: this.#handleScroll,
329
328
  handleSuggestionClick: this.#handleSuggestionClick,
330
329
  handleMessageContainerRef: this.#handleMessageContainerRef,
@@ -166,12 +166,17 @@ main {
166
166
  }
167
167
 
168
168
  .gemini {
169
+ .empty-state-container {
170
+ padding: var(--sys-size-8);
171
+ }
172
+
169
173
  .empty-state-container .icon {
170
174
  display: none;
171
175
  }
172
176
 
173
177
  .empty-state-container .header {
174
178
  align-items: flex-start;
179
+ line-height: var(--sys-size-4);
175
180
  }
176
181
 
177
182
  .empty-state-content {
@@ -179,9 +184,14 @@ main {
179
184
  }
180
185
 
181
186
  .empty-state-container .greeting {
187
+ font-size: var(--sys-size-10);
182
188
  color: var(--sys-color-primary);
183
189
  }
184
190
 
191
+ .empty-state-container .cta {
192
+ font-size: var(--sys-size-10);
193
+ }
194
+
185
195
  main {
186
196
  align-items: flex-start;
187
197
  }
@@ -10,7 +10,7 @@ import * as SDK from '../../core/sdk/sdk.js';
10
10
  import * as Protocol from '../../generated/protocol.js';
11
11
  import type * as Buttons from '../../ui/components/buttons/buttons.js';
12
12
  import * as uiI18n from '../../ui/i18n/i18n.js';
13
- import {Icon} from '../../ui/kit/kit.js';
13
+ import {Icon, Link} from '../../ui/kit/kit.js';
14
14
  import * as PerfUI from '../../ui/legacy/components/perf_ui/perf_ui.js';
15
15
  import * as SettingsUI from '../../ui/legacy/components/settings_ui/settings_ui.js';
16
16
  import * as UI from '../../ui/legacy/legacy.js';
@@ -218,9 +218,9 @@ export class StorageView extends UI.Widget.VBox {
218
218
  this.quotaRow = quota.appendSelectableRow();
219
219
  this.quotaRow.classList.add('quota-usage-row');
220
220
  const learnMoreRow = quota.appendRow();
221
- const learnMore = UI.XLink.XLink.create(
221
+ const learnMore = Link.create(
222
222
  'https://developer.chrome.com/docs/devtools/progressive-web-apps#opaque-responses',
223
- i18nString(UIStrings.learnMore), undefined, undefined, 'learn-more');
223
+ i18nString(UIStrings.learnMore), undefined, 'learn-more');
224
224
  learnMoreRow.appendChild(learnMore);
225
225
  this.quotaUsage = null;
226
226
  this.pieChart = new PerfUI.PieChart.PieChart();
@@ -30,7 +30,7 @@
30
30
  grid-column-start: span 2;
31
31
  }
32
32
 
33
- x-link {
33
+ devtools-link {
34
34
  color: var(--sys-color-primary);
35
35
  text-decoration-line: underline;
36
36
  }
@@ -19,33 +19,43 @@ const UIStringsNotTranslate = {
19
19
  */
20
20
  codeCompletionJustGotBetter: 'Code completion just got better',
21
21
  /**
22
- * @description First item in the description
23
- */
24
- asYouType: 'As you type, DevTools generates code suggestions to help you code faster.',
25
- /**
26
- * @description Second item in the description
22
+ * @description First item in the description.
27
23
  */
28
24
  describeCodeInComment:
29
- 'In Console and Sources, you can now describe the code you need in a comment, then press Ctrl+I to generate it.',
25
+ 'Pressing Ctrl+I on a comment in the Console and Sources panels now generates entire code blocks based on the instructions in the comment.',
30
26
  /**
31
- * @description Second item in the description
27
+ * @description First item in the description.
32
28
  */
33
29
  describeCodeInCommentForMacOs:
34
- 'In Console and Sources, you can now describe the code you need in a comment, then press Cmd+I to generate it.',
30
+ 'Pressing Cmd+I on a comment in the Console and Sources panels now generates entire code blocks based on the instructions in the comment.',
31
+ /**
32
+ * @description Second item in the description.
33
+ */
34
+ asYouType: 'You will still receive the real-time, as-you-type suggestions to help you code faster.',
35
+ /**
36
+ * @description Third item in the description.
37
+ */
38
+ disclaimerTextPrivacy:
39
+ 'To generate code suggestions, your console input, the history of your current console session, the currently inspected CSS, and the contents of the currently open file are shared with Google. This data may be seen by human reviewers to improve this feature.',
40
+ /**
41
+ * @description Third item in the description.
42
+ */
43
+ disclaimerTextPrivacyNoLogging:
44
+ 'To generate code suggestions, your console input, the history of your current console session, the currently inspected CSS, and the contents of the currently open file are shared with Google. This data will not be used to improve Google’s AI models. Your organization may change these settings at any time.',
35
45
  /**
36
46
  * @description Text for the manage in settings button in the upgrade notice dialog.
37
47
  */
38
48
  manageInSettings: 'Manage in settings',
39
49
  /**
40
- * @description Text for the got it button in the upgrade notice dialog.
50
+ * @description Text for the generate code button in the upgrade notice dialog.
41
51
  */
42
- gotIt: 'Got it',
52
+ generateCode: 'Generate code',
43
53
  } as const;
44
54
 
45
55
  const lockedString = i18n.i18n.lockedString;
46
56
 
47
57
  export class AiCodeGenerationUpgradeDialog {
48
- static show(): void {
58
+ static show({noLogging}: {noLogging: boolean}): void {
49
59
  const dialog = new UI.Dialog.Dialog();
50
60
  dialog.setAriaLabel(lockedString(UIStringsNotTranslate.codeCompletionJustGotBetter));
51
61
  // clang-format off
@@ -63,10 +73,6 @@ export class AiCodeGenerationUpgradeDialog {
63
73
  </h2>
64
74
  </header>
65
75
  <main class="reminder-container">
66
- <div class="reminder-item">
67
- <devtools-icon class="reminder-icon" name="code"></devtools-icon>
68
- <span>${lockedString(UIStringsNotTranslate.asYouType)}</span>
69
- </div>
70
76
  <div class="reminder-item">
71
77
  <devtools-icon class="reminder-icon" name="text-analysis"></devtools-icon>
72
78
  <span>
@@ -75,6 +81,16 @@ export class AiCodeGenerationUpgradeDialog {
75
81
  lockedString(UIStringsNotTranslate.describeCodeInComment)}
76
82
  </span>
77
83
  </div>
84
+ <div class="reminder-item">
85
+ <devtools-icon class="reminder-icon" name="code"></devtools-icon>
86
+ <span>${lockedString(UIStringsNotTranslate.asYouType)}</span>
87
+ </div>
88
+ <div class="reminder-item">
89
+ <devtools-icon class="reminder-icon" name="google"></devtools-icon>
90
+ <span>${noLogging ? lockedString(UIStringsNotTranslate.disclaimerTextPrivacyNoLogging) :
91
+ lockedString(UIStringsNotTranslate.disclaimerTextPrivacy)}
92
+ </span>
93
+ </div>
78
94
  </main>
79
95
  <footer>
80
96
  <div class="right-buttons">
@@ -93,7 +109,7 @@ export class AiCodeGenerationUpgradeDialog {
93
109
  }}
94
110
  jslogcontext="ai-code-generation-upgrade-dialog.continue"
95
111
  .variant=${Buttons.Button.Variant.PRIMARY}>
96
- ${lockedString(UIStringsNotTranslate.gotIt)}
112
+ ${lockedString(UIStringsNotTranslate.generateCode)}
97
113
  </devtools-button>
98
114
  </div>
99
115
  </footer>
@@ -85,7 +85,7 @@
85
85
  gap: var(--sys-size-2);
86
86
  white-space: normal;
87
87
 
88
- x-link {
88
+ devtools-link {
89
89
  color: var(--sys-color-primary);
90
90
  text-decoration: underline;
91
91
 
@@ -6,6 +6,7 @@ import * as Common from '../../core/common/common.js';
6
6
  import * as i18n from '../../core/i18n/i18n.js';
7
7
  import * as Platform from '../../core/platform/platform.js';
8
8
  import * as SDK from '../../core/sdk/sdk.js';
9
+ import * as StackTrace from '../../models/stack_trace/stack_trace.js';
9
10
  import * as UI from '../../ui/legacy/legacy.js';
10
11
  import * as Lit from '../../ui/lit/lit.js';
11
12
 
@@ -63,7 +64,7 @@ export class ConsoleContextSelector implements SDK.TargetManager.SDKModelObserve
63
64
  UI.Context.Context.instance().addFlavorChangeListener(
64
65
  SDK.RuntimeModel.ExecutionContext, this.executionContextChangedExternally, this);
65
66
  UI.Context.Context.instance().addFlavorChangeListener(
66
- SDK.DebuggerModel.CallFrame, this.callFrameSelectedInUI, this);
67
+ StackTrace.StackTrace.DebuggableFrameFlavor, this.callFrameSelectedInUI, this);
67
68
  SDK.TargetManager.TargetManager.instance().observeModels(SDK.RuntimeModel.RuntimeModel, this, {scoped: true});
68
69
  SDK.TargetManager.TargetManager.instance().addModelListener(
69
70
  SDK.DebuggerModel.DebuggerModel, SDK.DebuggerModel.Events.CallFrameSelected, this.callFrameSelectedInModel,
@@ -263,8 +264,8 @@ export class ConsoleContextSelector implements SDK.TargetManager.SDKModelObserve
263
264
  }
264
265
 
265
266
  private callFrameSelectedInUI(): void {
266
- const callFrame = UI.Context.Context.instance().flavor(SDK.DebuggerModel.CallFrame);
267
- const callFrameContext = callFrame?.script.executionContext();
267
+ const callFrame = UI.Context.Context.instance().flavor(StackTrace.StackTrace.DebuggableFrameFlavor);
268
+ const callFrameContext = callFrame?.sdkFrame.script.executionContext();
268
269
  if (callFrameContext) {
269
270
  UI.Context.Context.instance().setFlavor(SDK.RuntimeModel.ExecutionContext, callFrameContext);
270
271
  }
@@ -199,11 +199,34 @@ export const format = (fmt: string, args: SDK.RemoteObject.RemoteObject[]): {
199
199
  return {tokens, args: args.slice(argIndex)};
200
200
  };
201
201
 
202
+ /**
203
+ * This function converts a string into a partial regex string that
204
+ * case-insensitively matches it in CSS, even if CSS escapes are used.
205
+ *
206
+ * @param cssString the target string.
207
+ * @returns a partial regex matching the string in CSS.
208
+ */
209
+ const cssEscapeRegex = (cssString: string): string => {
210
+ return [...cssString]
211
+ .map(char => {
212
+ const charCodes = new Set([char.toLowerCase(), char.toUpperCase()].map(c => c.charCodeAt(0).toString(16)));
213
+ const charCodeRegex =
214
+ [...charCodes].map(charCode => `\\\\0{0,${6 - charCode.length}}${charCode}[ \\n\\t]?`).join('|');
215
+ return `\\\\?(?:${charCodeRegex}|${char})`;
216
+ })
217
+ .join('');
218
+ };
219
+
202
220
  export const updateStyle = (currentStyle: Map<string, {value: string, priority: string}>, styleToAdd: string): void => {
203
221
  const ALLOWED_PROPERTY_PREFIXES = ['background', 'border', 'color', 'font', 'line', 'margin', 'padding', 'text'];
204
222
  // We only allow data URLs with the `url()` CSS function.
205
223
  // The capture group is not intended to grab the whole URL exactly, just enough so we can check the scheme.
206
- const URL_REGEX = /url\([\'\"]?([^\)]*)/g;
224
+ // The regex also covers CSS hex-escaped variations of `url()`.
225
+ const URL_REGEX = new RegExp(`(?=${cssEscapeRegex('url')}\\(['"]?([^\\)]*))`, 'gi');
226
+ // We greedily capture all `image-set()`s to make sure that all of
227
+ // them properly use `url()`s to enforce the data URL check later.
228
+ const IMAGESET_REGEX = new RegExp(`(?=(${cssEscapeRegex('image-set')}\\(.*))`, 'gi');
229
+ const GOOD_IMAGESET_REGEX = /^image-set\((?:(?:(?:url|type)\("[^\\"]*"\)|[\d.]+(?:x|dpi|dpcm|dppx)),?\s*)+\)/i;
207
230
 
208
231
  currentStyle.clear();
209
232
  /* eslint-disable-next-line @devtools/no-imperative-dom-api --
@@ -218,9 +241,15 @@ export const updateStyle = (currentStyle: Map<string, {value: string, priority:
218
241
  continue;
219
242
  }
220
243
 
244
+ const value = buffer.style.getPropertyValue(property);
245
+ // We make sure every `image-set()` only uses `url()`s for its images.
246
+ // If any of them seem malformed, we skip the whole property.
247
+ const imageSets = [...value.matchAll(IMAGESET_REGEX)];
248
+ if (imageSets.some(match => !GOOD_IMAGESET_REGEX.test(match[1]))) {
249
+ continue;
250
+ }
221
251
  // There could be multiple `url()` functions, so we check them all.
222
252
  // If any of them is not a `data` URL, we skip the whole property.
223
- const value = buffer.style.getPropertyValue(property);
224
253
  const potentialUrls = [...value.matchAll(URL_REGEX)].map(match => match[1]);
225
254
  if (potentialUrls.some(
226
255
  potentialUrl => !Common.ParsedURL.schemeIs(potentialUrl as Platform.DevToolsPath.UrlString, 'data:'))) {
@@ -626,6 +626,7 @@ export class ConsoleInsightTeaser extends UI.Widget.Widget {
626
626
  this.#startTime = performance.now();
627
627
  let teaserText = '';
628
628
  let firstChunkReceived = false;
629
+ let firstChunkTime = 0;
629
630
  try {
630
631
  for await (const chunk of this.#getOnDeviceInsight()) {
631
632
  teaserText += chunk;
@@ -634,7 +635,9 @@ export class ConsoleInsightTeaser extends UI.Widget.Widget {
634
635
  this.requestUpdate();
635
636
  if (!firstChunkReceived) {
636
637
  firstChunkReceived = true;
637
- Host.userMetrics.consoleInsightTeaserFirstChunkGenerated(performance.now() - this.#startTime);
638
+ firstChunkTime = performance.now();
639
+ Host.userMetrics.consoleInsightTeaserFirstChunkGenerated(firstChunkTime - this.#startTime);
640
+ Host.userMetrics.consoleInsightTeaserFirstChunkGeneratedMedium(firstChunkTime - this.#startTime);
638
641
  }
639
642
  }
640
643
  } catch (err) {
@@ -654,6 +657,8 @@ export class ConsoleInsightTeaser extends UI.Widget.Widget {
654
657
  clearTimeout(this.#timeoutId);
655
658
  const duration = performance.now() - this.#startTime;
656
659
  Host.userMetrics.consoleInsightTeaserGenerated(duration);
660
+ Host.userMetrics.consoleInsightTeaserGeneratedMedium(duration);
661
+ Host.userMetrics.consoleInsightTeaserChunkToEndMedium(performance.now() - firstChunkTime);
657
662
  if (teaserText.length > 300) {
658
663
  Host.userMetrics.consoleInsightLongTeaserGenerated(duration);
659
664
  } else {
@@ -54,7 +54,7 @@ import * as Buttons from '../../ui/components/buttons/buttons.js';
54
54
  import * as CodeHighlighter from '../../ui/components/code_highlighter/code_highlighter.js';
55
55
  import * as Highlighting from '../../ui/components/highlighting/highlighting.js';
56
56
  import * as TextEditor from '../../ui/components/text_editor/text_editor.js';
57
- import {Icon} from '../../ui/kit/kit.js';
57
+ import {Icon, Link} from '../../ui/kit/kit.js';
58
58
  import * as Components from '../../ui/legacy/components/utils/utils.js';
59
59
  import * as UI from '../../ui/legacy/legacy.js';
60
60
  import * as Lit from '../../ui/lit/lit.js';
@@ -1092,6 +1092,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
1092
1092
  }
1093
1093
 
1094
1094
  override onbind(): void {
1095
+ this.performUpdate();
1095
1096
  if (this.treeOutline && !this.isClosingTag()) {
1096
1097
  this.treeOutline.treeElementByNode.set(this.nodeInternal, this);
1097
1098
  this.nodeInternal.addEventListener(SDK.DOMModel.DOMNodeEvents.TOP_LAYER_INDEX_CHANGED, this.performUpdate, this);
@@ -1115,6 +1116,49 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
1115
1116
  if (this.editing) {
1116
1117
  this.editing.cancel();
1117
1118
  }
1119
+ // Update the element to clean up adorner registrations with the
1120
+ // ElementsPanel.
1121
+ // We do not change the ElementsTreeElement state in case the
1122
+ // element is bound again.
1123
+ DEFAULT_VIEW(
1124
+ {
1125
+ containerAdornerActive: false,
1126
+ showAdAdorner: false,
1127
+ showContainerAdorner: false,
1128
+ containerType: this.#layout?.containerType,
1129
+ showFlexAdorner: false,
1130
+ flexAdornerActive: false,
1131
+ showGridAdorner: false,
1132
+ showGridLanesAdorner: false,
1133
+ showMediaAdorner: false,
1134
+ showPopoverAdorner: false,
1135
+ showTopLayerAdorner: false,
1136
+ gridAdornerActive: false,
1137
+ popoverAdornerActive: false,
1138
+ isSubgrid: false,
1139
+ showViewSourceAdorner: false,
1140
+ showScrollAdorner: false,
1141
+ showScrollSnapAdorner: false,
1142
+ scrollSnapAdornerActive: false,
1143
+ showSlotAdorner: false,
1144
+ showStartingStyleAdorner: false,
1145
+ startingStyleAdornerActive: false,
1146
+ nodeInfo: this.#nodeInfo,
1147
+ onStartingStyleAdornerClick: () => {},
1148
+ onSlotAdornerClick: () => {},
1149
+ topLayerIndex: -1,
1150
+ onViewSourceAdornerClick: () => {},
1151
+ onGutterClick: () => {},
1152
+ onContainerAdornerClick: () => {},
1153
+ onFlexAdornerClick: () => {},
1154
+ onGridAdornerClick: () => {},
1155
+ onMediaAdornerClick: () => {},
1156
+ onPopoverAdornerClick: () => {},
1157
+ onScrollSnapAdornerClick: () => {},
1158
+ onTopLayerAdornerClick: () => {},
1159
+ },
1160
+ this, this.listItemElement);
1161
+
1118
1162
  if (this.treeOutline && this.treeOutline.treeElementByNode.get(this.nodeInternal) === this) {
1119
1163
  this.treeOutline.treeElementByNode.delete(this.nodeInternal);
1120
1164
  }
@@ -2414,7 +2458,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
2414
2458
  value = Platform.StringUtilities.trimMiddle(value, 60);
2415
2459
  }
2416
2460
  const link = node && node.nodeName().toLowerCase() === 'a' ?
2417
- UI.XLink.XLink.create(rewrittenHref, value, '', true /* preventClick */, 'image-url') :
2461
+ Link.create(rewrittenHref, value, undefined, 'image-url') :
2418
2462
  Components.Linkifier.Linkifier.linkifyURL(rewrittenHref, {
2419
2463
  text: value,
2420
2464
  preventClick: true,
@@ -80,7 +80,7 @@ const str_ = i18n.i18n.registerUIStrings('panels/elements/ElementsTreeOutline.ts
80
80
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
81
81
  const elementsTreeOutlineByDOMModel = new WeakMap<SDK.DOMModel.DOMModel, ElementsTreeOutline>();
82
82
 
83
- const populatedTreeElements = new Set<ElementsTreeElement>();
83
+ const populatedTreeElements = new WeakSet<ElementsTreeElement>();
84
84
 
85
85
  export type View = typeof DEFAULT_VIEW;
86
86
 
@@ -101,6 +101,14 @@ const UIStrings = {
101
101
  * @description A context menu item in Styles panel to view the computed CSS property value.
102
102
  */
103
103
  viewComputedValue: 'View computed value',
104
+ /**
105
+ * @description Tooltip text for a style property overridden by an animation.
106
+ */
107
+ overriddenByAnimation: 'Overridden by animation styles.',
108
+ /**
109
+ * @description Link text in the tooltip to open the Animations panel.
110
+ */
111
+ openAnimationsPanel: 'Open Animations panel',
104
112
  /**
105
113
  * @description Title of the button that opens the flexbox editor in the Styles panel.
106
114
  */
@@ -2465,6 +2473,7 @@ export class StylePropertyTreeElement extends UI.TreeOutline.TreeElement {
2465
2473
 
2466
2474
  if (this.property.parsedOk) {
2467
2475
  this.updateAuthoringHint();
2476
+ this.updateAnimationOverrideHint();
2468
2477
  } else {
2469
2478
  // Avoid having longhands under an invalid shorthand.
2470
2479
  this.listItemElement.classList.add('not-parsed-ok');
@@ -2695,6 +2704,60 @@ export class StylePropertyTreeElement extends UI.TreeOutline.TreeElement {
2695
2704
  }
2696
2705
  }
2697
2706
 
2707
+ updateAnimationOverrideHint(): void {
2708
+ const existingElement = this.listItemElement.querySelector('.animation-override-hint-wrapper');
2709
+ if (existingElement) {
2710
+ existingElement?.remove();
2711
+ }
2712
+
2713
+ if (!this.overriddenByAnimation() || UI.ViewManager.ViewManager.instance().isViewVisible('animations')) {
2714
+ return;
2715
+ }
2716
+
2717
+ const wrapper = document.createElement('span');
2718
+ wrapper.classList.add('animation-override-hint-wrapper', 'hint-wrapper');
2719
+ const hintIcon = new Icon();
2720
+ hintIcon.name = 'info';
2721
+ hintIcon.classList.add('hint', 'small');
2722
+ hintIcon.tabIndex = -1;
2723
+
2724
+ wrapper.append(hintIcon);
2725
+ this.listItemElement.append(wrapper);
2726
+
2727
+ const tooltipId = this.getTooltipId('animation-override-hint');
2728
+ hintIcon.setAttribute('aria-details', tooltipId);
2729
+
2730
+ const tooltip = new Tooltips.Tooltip.Tooltip({
2731
+ anchor: hintIcon,
2732
+ variant: 'rich',
2733
+ padding: 'large',
2734
+ id: tooltipId,
2735
+ jslogContext: 'elements.css-animation-hint'
2736
+ });
2737
+ const message = i18nString(UIStrings.overriddenByAnimation);
2738
+ const content = document.createElement('div');
2739
+ content.classList.add('animation-override-hint');
2740
+ content.textContent = message;
2741
+
2742
+ const link = document.createElement('devtools-link');
2743
+ link.textContent = i18nString(UIStrings.openAnimationsPanel);
2744
+ link.jslogContext = 'open-in-animations-panel';
2745
+ link.addEventListener('click', event => {
2746
+ event.preventDefault();
2747
+ void UI.ViewManager.ViewManager.instance().showView('animations');
2748
+ });
2749
+
2750
+ content.appendChild(document.createTextNode(' '));
2751
+ content.appendChild(link);
2752
+ tooltip.appendChild(content);
2753
+
2754
+ this.listItemElement.appendChild(tooltip);
2755
+ }
2756
+
2757
+ private overriddenByAnimation(): boolean {
2758
+ return this.#matchedStyles.isPropertyOverriddenByAnimation(this.property);
2759
+ }
2760
+
2698
2761
  private mouseUp(event: MouseEvent): void {
2699
2762
  const activeTreeElement = parentMap.get(this.#parentPane);
2700
2763
  parentMap.delete(this.#parentPane);
@@ -237,6 +237,12 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
237
237
  }
238
238
  return null;
239
239
  }, () => this.node());
240
+
241
+ UI.ViewManager.ViewManager.instance().addEventListener(UI.ViewManager.Events.VIEW_VISIBILITY_CHANGED, event => {
242
+ if (event.data.revealedViewId === 'animations' || event.data.hiddenViewId === 'animations') {
243
+ this.#scheduleResetUpdateIfNotEditing();
244
+ }
245
+ });
240
246
  }
241
247
 
242
248
  get webCustomData(): WebCustomData|undefined {
@@ -741,6 +747,10 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
741
747
  return;
742
748
  }
743
749
 
750
+ if (!UI.ViewManager.ViewManager.instance().isViewVisible('animations')) {
751
+ return;
752
+ }
753
+
744
754
  void this.computedStyleUpdateThrottler.schedule(async () => {
745
755
  await this.#updateAnimatedStyles();
746
756
  this.handledComputedStyleChangedForTest();
@@ -1078,7 +1088,14 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
1078
1088
  // We disable the layer widget initially. If we see a layer in
1079
1089
  // the matched styles we reenable the button.
1080
1090
  LayersWidget.ButtonProvider.instance().item().setVisible(false);
1091
+ const animationsPanelVisible = UI.ViewManager.ViewManager.instance().isViewVisible('animations');
1081
1092
  for (const style of matchedStyles.nodeStyles()) {
1093
+ const isTransitionOrAnimationStyle = style.type === SDK.CSSStyleDeclaration.Type.Transition ||
1094
+ style.type === SDK.CSSStyleDeclaration.Type.Animation;
1095
+ if (isTransitionOrAnimationStyle && !animationsPanelVisible) {
1096
+ continue;
1097
+ }
1098
+
1082
1099
  const parentNode = matchedStyles.isInherited(style) ? matchedStyles.nodeForStyle(style) : null;
1083
1100
  if (parentNode && parentNode !== lastParentNode) {
1084
1101
  lastParentNode = parentNode;
@@ -1090,8 +1107,6 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
1090
1107
  addLayerSeparator(style);
1091
1108
 
1092
1109
  const lastBlock = blocks[blocks.length - 1];
1093
- const isTransitionOrAnimationStyle = style.type === SDK.CSSStyleDeclaration.Type.Transition ||
1094
- style.type === SDK.CSSStyleDeclaration.Type.Animation;
1095
1110
  if (lastBlock && (!isTransitionOrAnimationStyle || style.allProperties().length > 0)) {
1096
1111
  this.idleCallbackManager.schedule(() => {
1097
1112
  const section = new StylePropertiesSection(
@@ -165,6 +165,10 @@ devtools-icon.icon-link {
165
165
  opacity: 50%;
166
166
  }
167
167
 
168
+ .animation-override-hint {
169
+ max-width: 232px;
170
+ }
171
+
168
172
  .hint-wrapper {
169
173
  align-items: center;
170
174
  display: inline-block;
@@ -5,7 +5,7 @@
5
5
 
6
6
  import * as i18n from '../../core/i18n/i18n.js';
7
7
  import type * as Platform from '../../core/platform/platform.js';
8
- import * as UI from '../../ui/legacy/legacy.js';
8
+ import {Link} from '../../ui/kit/kit.js';
9
9
 
10
10
  import {AffectedResourcesView} from './AffectedResourcesView.js';
11
11
 
@@ -46,8 +46,8 @@ export class AffectedMetadataAllowedSitesView extends AffectedResourcesView {
46
46
  optOutTextElement.textContent = ' (opt-out: ' + issueData.details().optOutPercentage + '% - ';
47
47
  textContentElement.appendChild(optOutTextElement);
48
48
 
49
- const linkElement = UI.XLink.XLink.create(
50
- 'https://developers.google.com/privacy-sandbox/blog/grace-period-opt-out', 'learn more');
49
+ const linkElement =
50
+ Link.create('https://developers.google.com/privacy-sandbox/blog/grace-period-opt-out', 'learn more');
51
51
  textContentElement.appendChild(linkElement);
52
52
 
53
53
  const endTextElement = document.createElement('span');
@@ -9,6 +9,7 @@ import type * as SDK from '../../core/sdk/sdk.js';
9
9
  import type * as Protocol from '../../generated/protocol.js';
10
10
  import * as Geometry from '../../models/geometry/geometry.js';
11
11
  import * as uiI18n from '../../ui/i18n/i18n.js';
12
+ import {Link} from '../../ui/kit/kit.js';
12
13
  import * as UI from '../../ui/legacy/legacy.js';
13
14
  import * as Lit from '../../ui/lit/lit.js';
14
15
  import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
@@ -118,7 +119,7 @@ export const DEFAULT_VIEW = (input: ViewInput, output: ViewOutput, target: HTMLE
118
119
  uiI18n.getFormatLocalizedString(
119
120
  str_, UIStrings.checkSForPossibleReasons,
120
121
  {
121
- PH1: UI.XLink.XLink.create('about:gpu', undefined, undefined, undefined, 'about-gpu')
122
+ PH1: Link.create('about:gpu', undefined, undefined, 'about-gpu')
122
123
  }
123
124
  )
124
125
  ],