chrome-devtools-frontend 1.0.1586699 → 1.0.1587905

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 (43) hide show
  1. package/front_end/core/sdk/ScopeTreeCache.ts +4 -0
  2. package/front_end/entrypoint_template.html +5 -1
  3. package/front_end/generated/Deprecation.ts +21 -0
  4. package/front_end/generated/InspectorBackendCommands.ts +1 -1
  5. package/front_end/generated/protocol.ts +2 -0
  6. package/front_end/models/ai_assistance/AiConversation.ts +5 -1
  7. package/front_end/models/ai_assistance/agents/AiAgent.ts +16 -4
  8. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +7 -1
  9. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +0 -2
  10. package/front_end/panels/ai_assistance/components/ChatInput.ts +36 -31
  11. package/front_end/panels/ai_assistance/components/chatInput.css +6 -9
  12. package/front_end/panels/application/ServiceWorkerCacheViews.ts +3 -6
  13. package/front_end/panels/browser_debugger/CategorizedBreakpointsSidebarPane.ts +11 -3
  14. package/front_end/panels/common/DOMLinkifier.ts +6 -1
  15. package/front_end/panels/elements/ComputedStyleWidget.ts +25 -2
  16. package/front_end/panels/elements/ElementsPanel.ts +114 -15
  17. package/front_end/panels/elements/PropertiesWidget.ts +40 -2
  18. package/front_end/panels/elements/StylesSidebarPane.ts +26 -3
  19. package/front_end/panels/network/NetworkDataGridNode.ts +4 -0
  20. package/front_end/panels/network/NetworkItemView.ts +6 -44
  21. package/front_end/panels/network/RequestHeadersView.ts +499 -0
  22. package/front_end/panels/network/components/RequestHeaderSection.ts +3 -0
  23. package/front_end/panels/network/components/components.ts +0 -2
  24. package/front_end/panels/network/network.ts +3 -0
  25. package/front_end/third_party/chromium/README.chromium +1 -1
  26. package/front_end/third_party/puppeteer/README.chromium +2 -2
  27. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.d.ts +2 -2
  28. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js +2 -2
  29. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.d.ts +1 -1
  30. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.js +1 -1
  31. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +3 -3
  32. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.d.ts +2 -2
  33. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js +2 -2
  34. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.d.ts +1 -1
  35. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.js +1 -1
  36. package/front_end/third_party/puppeteer/package/package.json +1 -1
  37. package/front_end/third_party/puppeteer/package/src/revisions.ts +2 -2
  38. package/front_end/third_party/puppeteer/package/src/util/version.ts +1 -1
  39. package/front_end/ui/legacy/Toolbar.ts +37 -5
  40. package/front_end/ui/legacy/UIUtils.ts +35 -5
  41. package/front_end/ui/legacy/components/utils/JSPresentationUtils.ts +113 -133
  42. package/package.json +1 -1
  43. package/front_end/panels/network/components/RequestHeadersView.ts +0 -569
@@ -25,6 +25,10 @@ const scopeTrees = new WeakMap<Script, Promise<{scopeTree: ScopeTreeNode, text:
25
25
  * and the text allows conversion from/to line/column numbers.
26
26
  */
27
27
  export function scopeTreeForScript(script: Script): Promise<{scopeTree: ScopeTreeNode, text: Text}|null> {
28
+ if (script.isWasm()) {
29
+ return Promise.resolve(null);
30
+ }
31
+
28
32
  let promise = scopeTrees.get(script);
29
33
  if (promise === undefined) {
30
34
  promise = script.requestContentData().then(content => {
@@ -14,7 +14,11 @@
14
14
  }
15
15
  }
16
16
  </style>
17
- <meta http-equiv="Content-Security-Policy" content="object-src 'none'; script-src 'self' https://chrome-devtools-frontend.appspot.com">
17
+ <meta
18
+ http-equiv="Content-Security-Policy"
19
+ content="default-src 'self' devtools: data:; style-src 'self' 'unsafe-inline' devtools:; object-src 'none'; script-src
20
+ 'self' https://chrome-devtools-frontend.appspot.com; img-src 'self' data:; frame-src * data:; connect-src data:
21
+ https://chromeuxreport.googleapis.com 'self' devtools:;">
18
22
  <meta name="referrer" content="no-referrer">
19
23
  <script type="module" src="./entrypoints/%ENTRYPOINT_NAME%/%ENTRYPOINT_NAME%.js"></script>
20
24
  <link href="./application_tokens.css" rel="stylesheet">
@@ -94,6 +94,18 @@ export const UIStrings = {
94
94
  * @description Warning displayed to developers that instead of calling the `Intl.v8BreakIterator` constructor, which is not a standard JavaScript API, use ECMA402 standard API Intl.Segmenter shipped in end of 2020 instead.
95
95
  */
96
96
  IntlV8BreakIterator: "`Intl.v8BreakIterator` is deprecated. Please use `Intl.Segmenter` instead.",
97
+ /**
98
+ * @description Warning for using deprecated 'inputQuota' attribute.
99
+ */
100
+ LanguageModel_InputQuota: "LanguageModel.inputQuota is deprecated. Please use LanguageModel.contextWindow instead. This alias is only available in extensions.",
101
+ /**
102
+ * @description Warning for using deprecated 'inputUsage' attribute.
103
+ */
104
+ LanguageModel_InputUsage: "LanguageModel.inputUsage is deprecated. Please use LanguageModel.contextUsage instead. This alias is only available in extensions.",
105
+ /**
106
+ * @description Warning for using deprecated 'measureInputUsage' method.
107
+ */
108
+ LanguageModel_MeasureInputUsage: "LanguageModel.measureInputUsage() is deprecated. Please use LanguageModel.measureContextUsage() instead. This alias is only available in extensions.",
97
109
  /**
98
110
  * @description Warning message for web developers when they call the deprecated LanguageModel.params() method.
99
111
  */
@@ -313,6 +325,15 @@ export const DEPRECATIONS_METADATA: Partial<Record<string, DeprecationDescriptor
313
325
  "LanguageModelTopK": {
314
326
  "chromeStatusFeature": 5134603979063296
315
327
  },
328
+ "LanguageModel_InputQuota": {
329
+ "chromeStatusFeature": 5134603979063296
330
+ },
331
+ "LanguageModel_InputUsage": {
332
+ "chromeStatusFeature": 5134603979063296
333
+ },
334
+ "LanguageModel_MeasureInputUsage": {
335
+ "chromeStatusFeature": 5134603979063296
336
+ },
316
337
  "LocalCSSFileExtensionRejected": {
317
338
  "milestone": 64
318
339
  },
@@ -857,7 +857,7 @@ inspectorBackend.registerCommand("Network.disable", [], [], "Disables network tr
857
857
  inspectorBackend.registerCommand("Network.emulateNetworkConditions", [{"name": "offline", "type": "boolean", "optional": false, "description": "True to emulate internet disconnection.", "typeRef": null}, {"name": "latency", "type": "number", "optional": false, "description": "Minimum latency from request sent to response headers received (ms).", "typeRef": null}, {"name": "downloadThroughput", "type": "number", "optional": false, "description": "Maximal aggregated download throughput (bytes/sec). -1 disables download throttling.", "typeRef": null}, {"name": "uploadThroughput", "type": "number", "optional": false, "description": "Maximal aggregated upload throughput (bytes/sec). -1 disables upload throttling.", "typeRef": null}, {"name": "connectionType", "type": "string", "optional": true, "description": "Connection type if known.", "typeRef": "Network.ConnectionType"}, {"name": "packetLoss", "type": "number", "optional": true, "description": "WebRTC packet loss (percent, 0-100). 0 disables packet loss emulation, 100 drops all the packets.", "typeRef": null}, {"name": "packetQueueLength", "type": "number", "optional": true, "description": "WebRTC packet queue length (packet). 0 removes any queue length limitations.", "typeRef": null}, {"name": "packetReordering", "type": "boolean", "optional": true, "description": "WebRTC packetReordering feature.", "typeRef": null}], [], "Activates emulation of network conditions. This command is deprecated in favor of the emulateNetworkConditionsByRule and overrideNetworkState commands, which can be used together to the same effect.");
858
858
  inspectorBackend.registerCommand("Network.emulateNetworkConditionsByRule", [{"name": "offline", "type": "boolean", "optional": false, "description": "True to emulate internet disconnection.", "typeRef": null}, {"name": "matchedNetworkConditions", "type": "array", "optional": false, "description": "Configure conditions for matching requests. If multiple entries match a request, the first entry wins. Global conditions can be configured by leaving the urlPattern for the conditions empty. These global conditions are also applied for throttling of p2p connections.", "typeRef": "Network.NetworkConditions"}], ["ruleIds"], "Activates emulation of network conditions for individual requests using URL match patterns. Unlike the deprecated Network.emulateNetworkConditions this method does not affect `navigator` state. Use Network.overrideNetworkState to explicitly modify `navigator` behavior.");
859
859
  inspectorBackend.registerCommand("Network.overrideNetworkState", [{"name": "offline", "type": "boolean", "optional": false, "description": "True to emulate internet disconnection.", "typeRef": null}, {"name": "latency", "type": "number", "optional": false, "description": "Minimum latency from request sent to response headers received (ms).", "typeRef": null}, {"name": "downloadThroughput", "type": "number", "optional": false, "description": "Maximal aggregated download throughput (bytes/sec). -1 disables download throttling.", "typeRef": null}, {"name": "uploadThroughput", "type": "number", "optional": false, "description": "Maximal aggregated upload throughput (bytes/sec). -1 disables upload throttling.", "typeRef": null}, {"name": "connectionType", "type": "string", "optional": true, "description": "Connection type if known.", "typeRef": "Network.ConnectionType"}], [], "Override the state of navigator.onLine and navigator.connection.");
860
- inspectorBackend.registerCommand("Network.enable", [{"name": "maxTotalBufferSize", "type": "number", "optional": true, "description": "Buffer size in bytes to use when preserving network payloads (XHRs, etc).", "typeRef": null}, {"name": "maxResourceBufferSize", "type": "number", "optional": true, "description": "Per-resource buffer size in bytes to use when preserving network payloads (XHRs, etc).", "typeRef": null}, {"name": "maxPostDataSize", "type": "number", "optional": true, "description": "Longest post body size (in bytes) that would be included in requestWillBeSent notification", "typeRef": null}, {"name": "reportDirectSocketTraffic", "type": "boolean", "optional": true, "description": "Whether DirectSocket chunk send/receive events should be reported.", "typeRef": null}, {"name": "enableDurableMessages", "type": "boolean", "optional": true, "description": "Enable storing response bodies outside of renderer, so that these survive a cross-process navigation. Requires maxTotalBufferSize to be set. Currently defaults to false. This field is being deprecated in favor of the dedicated configureDurableMessages command, due to the possibility of deadlocks when awaiting Network.enable before issuing Runtime.runIfWaitingForDebugger.", "typeRef": null}], [], "Enables network tracking, network events will now be delivered to the client.");
860
+ inspectorBackend.registerCommand("Network.enable", [{"name": "maxTotalBufferSize", "type": "number", "optional": true, "description": "Buffer size in bytes to use when preserving network payloads (XHRs, etc). This is the maximum number of bytes that will be collected by this DevTools session.", "typeRef": null}, {"name": "maxResourceBufferSize", "type": "number", "optional": true, "description": "Per-resource buffer size in bytes to use when preserving network payloads (XHRs, etc).", "typeRef": null}, {"name": "maxPostDataSize", "type": "number", "optional": true, "description": "Longest post body size (in bytes) that would be included in requestWillBeSent notification", "typeRef": null}, {"name": "reportDirectSocketTraffic", "type": "boolean", "optional": true, "description": "Whether DirectSocket chunk send/receive events should be reported.", "typeRef": null}, {"name": "enableDurableMessages", "type": "boolean", "optional": true, "description": "Enable storing response bodies outside of renderer, so that these survive a cross-process navigation. Requires maxTotalBufferSize to be set. Currently defaults to false. This field is being deprecated in favor of the dedicated configureDurableMessages command, due to the possibility of deadlocks when awaiting Network.enable before issuing Runtime.runIfWaitingForDebugger.", "typeRef": null}], [], "Enables network tracking, network events will now be delivered to the client.");
861
861
  inspectorBackend.registerCommand("Network.configureDurableMessages", [{"name": "maxTotalBufferSize", "type": "number", "optional": true, "description": "Buffer size in bytes to use when preserving network payloads (XHRs, etc).", "typeRef": null}, {"name": "maxResourceBufferSize", "type": "number", "optional": true, "description": "Per-resource buffer size in bytes to use when preserving network payloads (XHRs, etc).", "typeRef": null}], [], "Configures storing response bodies outside of renderer, so that these survive a cross-process navigation. If maxTotalBufferSize is not set, durable messages are disabled.");
862
862
  inspectorBackend.registerCommand("Network.getAllCookies", [], ["cookies"], "Returns all browser cookies. Depending on the backend support, will return detailed cookie information in the `cookies` field. Deprecated. Use Storage.getCookies instead.");
863
863
  inspectorBackend.registerCommand("Network.getCertificate", [{"name": "origin", "type": "string", "optional": false, "description": "Origin to get certificate for.", "typeRef": null}], ["tableNames"], "Returns the DER-encoded certificate.");
@@ -11896,6 +11896,8 @@ export namespace Network {
11896
11896
  export interface EnableRequest {
11897
11897
  /**
11898
11898
  * Buffer size in bytes to use when preserving network payloads (XHRs, etc).
11899
+ * This is the maximum number of bytes that will be collected by this
11900
+ * DevTools session.
11899
11901
  */
11900
11902
  maxTotalBufferSize?: integer;
11901
11903
  /**
@@ -425,6 +425,10 @@ Time: ${micros(time)}`;
425
425
  yield* this.#runAgent(initialQuery, options);
426
426
  }
427
427
 
428
+ #getQueryAfterSelection(initialQuery: string, selection: string): string {
429
+ return `${selection}\nOriginal user query: ${initialQuery}`;
430
+ }
431
+
428
432
  async *
429
433
  #runAgent(
430
434
  initialQuery: string,
@@ -467,7 +471,7 @@ Time: ${micros(time)}`;
467
471
  // requery with the specialized agent.
468
472
  if (data.type === ResponseType.CONTEXT_CHANGE) {
469
473
  this.setContext(data.context);
470
- yield* this.#runAgent(initialQuery, options);
474
+ yield* this.#runAgent(this.#getQueryAfterSelection(initialQuery, data.description), options);
471
475
  return;
472
476
  }
473
477
  }
@@ -88,6 +88,12 @@ export interface SideEffectResponse {
88
88
  }
89
89
  export interface ContextChangeResponse {
90
90
  type: ResponseType.CONTEXT_CHANGE;
91
+ /**
92
+ * Information to pass down what was selected
93
+ * Use to make the LLM understand the the user
94
+ * already selected something.
95
+ */
96
+ description: string;
91
97
  context: ConversationContext<unknown>;
92
98
  }
93
99
 
@@ -212,7 +218,8 @@ export type FunctionCallHandlerResult<Result> = {
212
218
  }|{
213
219
  result: Result,
214
220
  }|{
215
- context: unknown,
221
+ context: ConversationContext<unknown>,
222
+ description: string,
216
223
  }|{
217
224
  error: string,
218
225
  };
@@ -484,7 +491,9 @@ export abstract class AiAgent<T> {
484
491
  * called with one object with `foo` and `bar` keys.
485
492
  */
486
493
  protected declareFunction<Args extends Record<string, unknown>, ReturnType = unknown>(
487
- name: string, declaration: FunctionDeclaration<Args, ReturnType>): void {
494
+ name: string,
495
+ declaration: FunctionDeclaration<Args, ReturnType>,
496
+ ): void {
488
497
  if (this.#functionDeclarations.has(name)) {
489
498
  throw new Error(`Duplicate function declaration ${name}`);
490
499
  }
@@ -609,6 +618,7 @@ export abstract class AiAgent<T> {
609
618
  if ('context' in result) {
610
619
  yield {
611
620
  type: ResponseType.CONTEXT_CHANGE,
621
+ description: result.description,
612
622
  context: result.context,
613
623
  };
614
624
 
@@ -643,7 +653,9 @@ export abstract class AiAgent<T> {
643
653
  name: string,
644
654
  args: Record<string, unknown>,
645
655
  options?: FunctionHandlerOptions&{explanation?: string},
646
- ): AsyncGenerator<FunctionCallResponseData, {result: unknown}|{context: ConversationContext<unknown>}> {
656
+ ): AsyncGenerator<FunctionCallResponseData, {
657
+ result: unknown,
658
+ }|{context: ConversationContext<unknown>, description: string}> {
647
659
  const call = this.#functionDeclarations.get(name);
648
660
  if (!call) {
649
661
  throw new Error(`Function ${name} is not found.`);
@@ -755,7 +767,7 @@ export abstract class AiAgent<T> {
755
767
  }
756
768
 
757
769
  if ('context' in result) {
758
- return result as {context: ConversationContext<unknown>};
770
+ return result;
759
771
  }
760
772
 
761
773
  return result as {result: unknown};
@@ -169,6 +169,7 @@ export class ContextSelectionAgent extends AiAgent<never> {
169
169
  const calculator = this.#networkTimeCalculator ?? new NetworkTimeCalculator.NetworkTransferTimeCalculator();
170
170
  return {
171
171
  context: new RequestContext(request, calculator),
172
+ description: 'User selected a network request',
172
173
  };
173
174
  }
174
175
 
@@ -231,11 +232,14 @@ export class ContextSelectionAgent extends AiAgent<never> {
231
232
  if (file.fullDisplayName() === params.name) {
232
233
  return {
233
234
  context: new FileContext(file),
235
+ description: 'User selected a source file',
234
236
  };
235
237
  }
236
238
  }
237
239
 
238
- return {error: 'Unable to find file.'};
240
+ return {
241
+ error: 'Unable to find file.',
242
+ };
239
243
  },
240
244
  });
241
245
 
@@ -264,6 +268,7 @@ export class ContextSelectionAgent extends AiAgent<never> {
264
268
 
265
269
  return {
266
270
  context: PerformanceTraceContext.fromParsedTrace(result),
271
+ description: 'User recorded a performance trace',
267
272
  };
268
273
  }
269
274
  });
@@ -292,6 +297,7 @@ export class ContextSelectionAgent extends AiAgent<never> {
292
297
  if (node) {
293
298
  return {
294
299
  context: new NodeContext(node),
300
+ description: 'User selected an element',
295
301
  };
296
302
  }
297
303
  return {
@@ -1426,8 +1426,6 @@ export class AiAssistancePanel extends UI.Panel.Panel {
1426
1426
  // Cancel any previous in-flight conversation.
1427
1427
  this.#cancel();
1428
1428
  const signal = this.#runAbortController.signal;
1429
- const context = this.#getConversationContext(this.#conversation.type);
1430
- this.#conversation.setContext(context);
1431
1429
 
1432
1430
  // If a different context is provided, it must be from the same origin.
1433
1431
  if (this.#conversation.isBlockedByOrigin) {
@@ -202,7 +202,8 @@ export const DEFAULT_VIEW = (input: ViewInput, _output: ViewOutput, target: HTML
202
202
  jslog=${VisualLogging.link('open-ai-settings').track({
203
203
  click: true,
204
204
  })}
205
- @click=${() => {
205
+ @click=${(ev: Event) => {
206
+ ev.preventDefault();
206
207
  void UI.ViewManager.ViewManager.instance().showView('chrome-ai');
207
208
  }}
208
209
  >${lockedString('Relevant data')}</button>&nbsp;${lockedString('is sent to Google')}
@@ -320,42 +321,46 @@ export const DEFAULT_VIEW = (input: ViewInput, _output: ViewOutput, target: HTML
320
321
  ></devtools-button>`
321
322
  : Lit.nothing}
322
323
  <div
323
- role=button
324
324
  class=${Lit.Directives.classMap({
325
325
  'resource-link': true,
326
326
  'has-picker-behavior': input.conversationType === AiAssistanceModel.AiHistoryStorage.ConversationType.STYLING,
327
- disabled: input.isTextInputDisabled,
328
327
  })}
329
- tabindex=${(input.conversationType === AiAssistanceModel.AiHistoryStorage.ConversationType.STYLING || input.isTextInputDisabled) ? '-1' : '0'}
330
- @click=${input.onContextClick}
331
- @keydown=${(ev: KeyboardEvent) => {
332
- if (ev.key === 'Enter' || ev.key === ' ') {
333
- void input.onContextClick();
334
- }
335
- }}
336
- aria-description=${i18nString(UIStrings.revealContextDescription)}
337
328
  >
338
- ${input.selectedContext instanceof AiAssistanceModel.NetworkAgent.RequestContext ?
339
- PanelUtils.PanelUtils.getIconForNetworkRequest(input.selectedContext.getItem()) :
340
- input.selectedContext instanceof AiAssistanceModel.FileAgent.FileContext ?
341
- PanelUtils.PanelUtils.getIconForSourceFile(input.selectedContext.getItem()) :
342
- input.selectedContext instanceof AiAssistanceModel.PerformanceAgent.PerformanceTraceContext ?
343
- html`<devtools-icon name="performance" title="Performance"></devtools-icon>` :
344
- Lit.nothing}
345
- <span class="title">
346
- ${input.selectedContext instanceof AiAssistanceModel.StylingAgent.NodeContext ?
329
+ ${
330
+ input.selectedContext instanceof AiAssistanceModel.StylingAgent.NodeContext ?
347
331
  html`
348
- <devtools-widget .widgetConfig=${UI.Widget.widgetConfig(PanelsCommon.DOMLinkifier.DOMNodeLink, {
349
- node: input.selectedContext.getItem(),
350
- options: {
351
- hiddenClassList: input.selectedContext.getItem().classNames().filter(
352
- className => className.startsWith(AiAssistanceModel.Injected.AI_ASSISTANCE_CSS_CLASS_NAME)),
353
- disabled: input.isTextInputDisabled,
354
- },
355
- })}></devtools-widget>`
356
- :
357
- input.selectedContext.getTitle()}
358
- </span>
332
+ <devtools-widget
333
+ class="title"
334
+ .widgetConfig=${UI.Widget.widgetConfig(PanelsCommon.DOMLinkifier.DOMNodeLink, {
335
+ node: input.selectedContext.getItem(),
336
+ options: {
337
+ hiddenClassList: input.selectedContext.getItem().classNames().filter(
338
+ className => className.startsWith(AiAssistanceModel.Injected.AI_ASSISTANCE_CSS_CLASS_NAME)),
339
+ ariaDescription: i18nString(UIStrings.revealContextDescription),
340
+ },
341
+ })}
342
+ ></devtools-widget>` :
343
+ html`
344
+ ${input.selectedContext instanceof AiAssistanceModel.NetworkAgent.RequestContext ?
345
+ PanelUtils.PanelUtils.getIconForNetworkRequest(input.selectedContext.getItem()) :
346
+ input.selectedContext instanceof AiAssistanceModel.FileAgent.FileContext ?
347
+ PanelUtils.PanelUtils.getIconForSourceFile(input.selectedContext.getItem()) :
348
+ input.selectedContext instanceof AiAssistanceModel.PerformanceAgent.PerformanceTraceContext ?
349
+ html`<devtools-icon name="performance" title="Performance"></devtools-icon>` :
350
+ Lit.nothing}
351
+ <span
352
+ role="button"
353
+ class="title"
354
+ tabindex="0"
355
+ @click=${input.onContextClick}
356
+ @keydown=${(ev: KeyboardEvent) => {
357
+ if (ev.key === 'Enter' || ev.key === ' ') {
358
+ void input.onContextClick();
359
+ }
360
+ }}
361
+ aria-description=${i18nString(UIStrings.revealContextDescription)}
362
+ >${input.selectedContext.getTitle()}</span>`
363
+ }
359
364
  ${input.onContextRemoved ? html`
360
365
  <devtools-button
361
366
  title=${getContextRemoveLabel(input.selectedContext)}
@@ -218,9 +218,9 @@
218
218
  gap: var(--sys-size-3);
219
219
  align-items: center;
220
220
 
221
- .resource-link,
222
- .resource-task {
221
+ .resource-link {
223
222
  display: flex;
223
+ background-color: var(--sys-color-cdt-base-container);
224
224
  align-items: center;
225
225
  cursor: pointer;
226
226
  padding: var(--sys-size-2) var(--sys-size-3);
@@ -250,6 +250,10 @@
250
250
 
251
251
  &.has-picker-behavior {
252
252
  overflow: visible;
253
+
254
+ .title {
255
+ overflow: visible;
256
+ }
253
257
  }
254
258
 
255
259
  &:focus-visible {
@@ -301,13 +305,6 @@
301
305
  }
302
306
  }
303
307
  }
304
-
305
- .resource-link.disabled,
306
- .resource-task.disabled {
307
- color: var(--sys-color-state-disabled);
308
- border-color: var(--sys-color-neutral-outline);
309
- pointer-events: none;
310
- }
311
308
  }
312
309
 
313
310
  .link {
@@ -11,11 +11,9 @@ import * as Platform from '../../core/platform/platform.js';
11
11
  import * as SDK from '../../core/sdk/sdk.js';
12
12
  import type * as Protocol from '../../generated/protocol.js';
13
13
  import * as TextUtils from '../../models/text_utils/text_utils.js';
14
- import * as LegacyWrapper from '../../ui/components/legacy_wrapper/legacy_wrapper.js';
15
14
  import * as DataGrid from '../../ui/legacy/components/data_grid/data_grid.js';
16
15
  import * as UI from '../../ui/legacy/legacy.js';
17
16
  import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
18
- import * as NetworkComponents from '../network/components/components.js';
19
17
  import * as Network from '../network/network.js';
20
18
 
21
19
  import * as ApplicationComponents from './components/components.js';
@@ -542,10 +540,9 @@ export class RequestView extends UI.Widget.VBox {
542
540
  this.resourceViewTabSetting =
543
541
  Common.Settings.Settings.instance().createSetting('cache-storage-view-tab', 'preview');
544
542
 
545
- this.tabbedPane.appendTab(
546
- 'headers', i18nString(UIStrings.headers),
547
- LegacyWrapper.LegacyWrapper.legacyWrapper(
548
- UI.Widget.VBox, new NetworkComponents.RequestHeadersView.RequestHeadersView(request)));
543
+ const requestHeadersView = new Network.RequestHeadersView.RequestHeadersView();
544
+ requestHeadersView.request = request;
545
+ this.tabbedPane.appendTab('headers', i18nString(UIStrings.headers), requestHeadersView);
549
546
  this.tabbedPane.appendTab(
550
547
  'preview', i18nString(UIStrings.preview), new Network.RequestPreviewView.RequestPreviewView(request));
551
548
  this.tabbedPane.show(this.element);
@@ -150,12 +150,20 @@ export const DEFAULT_VIEW = (input: ViewInput, output: ViewOutput, target: HTMLE
150
150
  const shouldExpandCategory = (breakpoints: SDK.CategorizedBreakpoint.CategorizedBreakpoint[]): boolean =>
151
151
  Boolean(input.filterText) || (input.highlightedItem && breakpoints.includes(input.highlightedItem)) ||
152
152
  breakpoints.some(breakpoint => breakpoint.enabled());
153
- const filter = (breakpoint: SDK.CategorizedBreakpoint.CategorizedBreakpoint): boolean => !input.filterText ||
154
- Boolean(Sources.CategorizedBreakpointL10n.getLocalizedBreakpointName(breakpoint.name).match(input.filterText)) ||
153
+ const filterRegex =
154
+ input.filterText ? new RegExp(Platform.StringUtilities.escapeForRegExp(input.filterText), 'i') : null;
155
+ const filter = (breakpoint: SDK.CategorizedBreakpoint.CategorizedBreakpoint): boolean => !filterRegex ||
156
+ Boolean(Sources.CategorizedBreakpointL10n.getLocalizedBreakpointName(breakpoint.name).match(filterRegex)) ||
155
157
  breakpoint === input.highlightedItem;
156
158
  const filteredCategories =
157
159
  input.sortedCategoryNames.values()
158
- .map(category => [category, input.categories.get(category)?.filter(filter)])
160
+ .map(category => {
161
+ const breakpoints = input.categories.get(category);
162
+ if (filterRegex && getLocalizedCategory(category).match(filterRegex)) {
163
+ return [category, breakpoints];
164
+ }
165
+ return [category, breakpoints?.filter(filter)];
166
+ })
159
167
  .filter(
160
168
  (filteredCategory): filteredCategory is
161
169
  [SDK.CategorizedBreakpoint.Category, SDK.CategorizedBreakpoint.CategorizedBreakpoint[]] =>
@@ -12,7 +12,7 @@ import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
12
12
 
13
13
  import domLinkifierStyles from './domLinkifier.css.js';
14
14
 
15
- const {classMap} = Directives;
15
+ const {classMap, ifDefined} = Directives;
16
16
 
17
17
  const UIStrings = {
18
18
  /**
@@ -33,6 +33,7 @@ export interface Options {
33
33
  isDynamicLink?: boolean;
34
34
  hiddenClassList?: string[];
35
35
  disabled?: boolean;
36
+ ariaDescription?: string;
36
37
  }
37
38
 
38
39
  interface ViewInput {
@@ -43,6 +44,7 @@ interface ViewInput {
43
44
  id?: string;
44
45
  classes: string[];
45
46
  pseudo?: string;
47
+ ariaDescription?: string;
46
48
  onClick: () => void;
47
49
  onMouseOver: () => void;
48
50
  onMouseLeave: () => void;
@@ -59,6 +61,7 @@ const DEFAULT_VIEW: View = (input, _output, target: HTMLElement) => {
59
61
  'dynamic-link': Boolean(input.dynamic),
60
62
  disabled: Boolean(input.disabled)
61
63
  })}"
64
+ aria-description=${ifDefined(input.ariaDescription)}
62
65
  jslog=${VisualLogging.link('node').track({click: true, keydown: 'Enter'})}
63
66
  tabindex=${input.preventKeyboardFocus ? -1 : 0}
64
67
  @click=${input.onClick}
@@ -112,6 +115,7 @@ export class DOMNodeLink extends UI.Widget.Widget {
112
115
  textContent: undefined,
113
116
  isDynamicLink: false,
114
117
  disabled: false,
118
+ ariaDescription: undefined,
115
119
  };
116
120
  const viewInput: ViewInput = {
117
121
  dynamic: options.isDynamicLink,
@@ -129,6 +133,7 @@ export class DOMNodeLink extends UI.Widget.Widget {
129
133
  onMouseLeave: () => {
130
134
  SDK.OverlayModel.OverlayModel.hideDOMNodeHighlight();
131
135
  },
136
+ ariaDescription: options.ariaDescription,
132
137
  };
133
138
  if (!this.#node) {
134
139
  this.#view(viewInput, {}, this.contentElement);
@@ -270,6 +270,7 @@ interface ComputedStyleWidgetInput {
270
270
  groupComputedStylesSetting: Common.Settings.Setting<boolean>;
271
271
  onFilterChanged: (event: CustomEvent<string>) => void;
272
272
  filterText: string;
273
+ onRegexToggled: () => void;
273
274
  }
274
275
 
275
276
  type View = (input: ComputedStyleWidgetInput, output: null, target: HTMLElement) => void;
@@ -283,8 +284,10 @@ export const DEFAULT_VIEW: View = (input, _output, target) => {
283
284
  <devtools-toolbar-input
284
285
  type="filter"
285
286
  autofocus
287
+ ?regex=${true}
286
288
  value=${input.filterText}
287
289
  @change=${input.onFilterChanged}
290
+ @regextoggle=${input.onRegexToggled}
288
291
  ></devtools-toolbar-input>
289
292
  <devtools-checkbox
290
293
  title=${i18nString(UIStrings.showAll)}
@@ -317,6 +320,7 @@ export class ComputedStyleWidget extends UI.Widget.VBox {
317
320
  #treeData?: TreeOutline.TreeOutline.TreeOutlineData<ComputedStyleData>;
318
321
  readonly #view: View;
319
322
  #filterText = '';
323
+ #isRegex = false;
320
324
 
321
325
  constructor() {
322
326
  super({useShadowDom: true});
@@ -374,6 +378,7 @@ export class ComputedStyleWidget extends UI.Widget.VBox {
374
378
  groupComputedStylesSetting: this.groupComputedStylesSetting,
375
379
  onFilterChanged: this.onFilterChanged.bind(this),
376
380
  filterText: this.#filterText,
381
+ onRegexToggled: this.onRegexToggled.bind(this),
377
382
  },
378
383
  null, this.contentElement);
379
384
  }
@@ -654,10 +659,28 @@ export class ComputedStyleWidget extends UI.Widget.VBox {
654
659
  return result;
655
660
  }
656
661
 
662
+ #buildFilterRegex(text: string): RegExp|null {
663
+ if (!text) {
664
+ return null;
665
+ }
666
+ if (this.#isRegex) {
667
+ try {
668
+ return new RegExp(text, 'i');
669
+ } catch {
670
+ // Invalid regex: fall through to plain-text matching.
671
+ }
672
+ }
673
+ return new RegExp(Platform.StringUtilities.escapeForRegExp(text), 'i');
674
+ }
675
+
676
+ private async onRegexToggled(): Promise<void> {
677
+ this.#isRegex = !this.#isRegex;
678
+ await this.filterComputedStyles(this.#buildFilterRegex(this.#filterText));
679
+ }
680
+
657
681
  private async onFilterChanged(event: CustomEvent<string>): Promise<void> {
658
682
  this.#filterText = event.detail;
659
- await this.filterComputedStyles(
660
- event.detail ? new RegExp(Platform.StringUtilities.escapeForRegExp(event.detail), 'i') : null);
683
+ await this.filterComputedStyles(this.#buildFilterRegex(event.detail));
661
684
 
662
685
  if (event.detail && this.#computedStylesTree.data && this.#computedStylesTree.data.tree) {
663
686
  UI.ARIAUtils.LiveAnnouncer.alert(i18nString(