chrome-devtools-frontend 1.0.1613625 → 1.0.1614363

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 (61) hide show
  1. package/AUTHORS +1 -0
  2. package/front_end/core/common/MapWithDefault.ts +2 -2
  3. package/front_end/core/common/Object.ts +6 -9
  4. package/front_end/core/common/VersionController.ts +17 -1
  5. package/front_end/core/host/UserMetrics.ts +0 -1
  6. package/front_end/core/root/ExperimentNames.ts +0 -1
  7. package/front_end/core/sdk/OverlayModel.ts +2 -4
  8. package/front_end/core/sdk/sdk-meta.ts +13 -0
  9. package/front_end/entrypoints/device_mode_emulation_frame/device_mode_emulation_frame.ts +4 -0
  10. package/front_end/entrypoints/greendev_floaty/FloatyEntrypoint.ts +0 -1
  11. package/front_end/entrypoints/greendev_floaty/greendev_floaty.ts +0 -1
  12. package/front_end/entrypoints/main/MainImpl.ts +0 -6
  13. package/front_end/entrypoints/shell/shell.ts +4 -0
  14. package/front_end/entrypoints/trace_app/trace_app.ts +4 -0
  15. package/front_end/generated/InspectorBackendCommands.ts +2 -2
  16. package/front_end/generated/protocol.ts +5 -3
  17. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.snapshot.txt +10 -2
  18. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +23 -7
  19. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +161 -36
  20. package/front_end/models/javascript_metadata/NativeFunctions.js +673 -639
  21. package/front_end/models/stack_trace/DetailedErrorStackParser.ts +142 -0
  22. package/front_end/models/stack_trace/StackTrace.ts +18 -0
  23. package/front_end/models/stack_trace/StackTraceImpl.ts +96 -4
  24. package/front_end/models/stack_trace/Trie.ts +21 -0
  25. package/front_end/models/stack_trace/stack_trace_impl.ts +2 -0
  26. package/front_end/panels/ai_assistance/components/ChatView.ts +4 -3
  27. package/front_end/panels/ai_assistance/components/ExportForAgentsDialog.ts +4 -1
  28. package/front_end/panels/ai_assistance/components/optInChangeDialog.css +1 -2
  29. package/front_end/panels/application/WebMCPView.ts +249 -17
  30. package/front_end/panels/application/components/ProtocolHandlersView.ts +2 -2
  31. package/front_end/panels/common/AiCodeCompletionDisclaimer.ts +7 -0
  32. package/front_end/panels/console/ConsoleContextSelector.ts +1 -1
  33. package/front_end/panels/css_overview/CSSOverviewCompletedView.ts +2 -3
  34. package/front_end/panels/css_overview/CSSOverviewModel.ts +1 -2
  35. package/front_end/panels/network/RequestConditionsDrawer.ts +4 -2
  36. package/front_end/panels/network/RequestPayloadView.ts +8 -3
  37. package/front_end/panels/network/RequestTimingView.ts +6 -7
  38. package/front_end/panels/performance_monitor/PerformanceMonitor.ts +7 -5
  39. package/front_end/panels/protocol_monitor/JSONEditor.ts +1 -1
  40. package/front_end/panels/recorder/models/RecordingPlayer.ts +1 -1
  41. package/front_end/services/puppeteer/PuppeteerConnection.ts +1 -1
  42. package/front_end/third_party/chromium/README.chromium +1 -1
  43. package/front_end/third_party/codemirror/codemirror-tsconfig.json +4 -4
  44. package/front_end/third_party/lighthouse/lighthouse-tsconfig.json +1 -1
  45. package/front_end/tsconfig.json +2 -1
  46. package/front_end/ui/kit/icons/Icon.ts +0 -1
  47. package/front_end/ui/legacy/EmptyWidget.ts +2 -2
  48. package/front_end/ui/legacy/Treeoutline.ts +2 -3
  49. package/front_end/ui/legacy/components/color_picker/ContrastDetails.ts +1 -2
  50. package/front_end/ui/legacy/components/color_picker/ContrastOverlay.ts +4 -5
  51. package/front_end/ui/legacy/components/data_grid/DataGridElement.ts +3 -4
  52. package/front_end/ui/legacy/components/source_frame/XMLView.ts +12 -7
  53. package/front_end/ui/lit/lit.ts +4 -1
  54. package/front_end/ui/lit/render.ts +81 -0
  55. package/front_end/ui/visual_logging/KnownContextValues.ts +1 -1
  56. package/package.json +2 -2
  57. /package/front_end/third_party/codemirror/package/addon/runmode/{runmode-standalone.mjs.d.ts → runmode-standalone.d.mts} +0 -0
  58. /package/front_end/third_party/codemirror/package/mode/css/{css.mjs.d.ts → css.d.mts} +0 -0
  59. /package/front_end/third_party/codemirror/package/mode/javascript/{javascript.mjs.d.ts → javascript.d.mts} +0 -0
  60. /package/front_end/third_party/codemirror/package/mode/xml/{xml.mjs.d.ts → xml.d.mts} +0 -0
  61. /package/front_end/third_party/lighthouse/report-assets/{report-generator.mjs.d.ts → report-generator.d.mts} +0 -0
package/AUTHORS CHANGED
@@ -41,6 +41,7 @@ Emir D <emrd434@gmail.com>
41
41
  Ergün Erdoğmuş <erdogmusergun@gmail.com>
42
42
  Eric Rannaud <eric.rannaud@gmail.com>
43
43
  Faisal Salman <fyzlman@gmail.com>
44
+ Fedor Indutny <indutny@signal.org>
44
45
  Feng Lu <lufengd3@gmail.com>
45
46
  Feng Yu <f3n67u@gmail.com>
46
47
  Gabriel Luong <gabriel.luong@gmail.com>
@@ -8,7 +8,7 @@
8
8
  * TODO: Once the proposal is merged, just replace `MapWithDefault` with `Map` and remove it.
9
9
  **/
10
10
  export class MapWithDefault<K, V> extends Map<K, V> {
11
- override getOrInsert(key: K, defaultValue: V): V {
11
+ getOrInsert(key: K, defaultValue: V): V {
12
12
  if (!this.has(key)) {
13
13
  this.set(key, defaultValue);
14
14
  }
@@ -16,7 +16,7 @@ export class MapWithDefault<K, V> extends Map<K, V> {
16
16
  return this.get(key) as V;
17
17
  }
18
18
 
19
- override getOrInsertComputed(key: K, callbackFunction: (key: K) => V): V {
19
+ getOrInsertComputed(key: K, callbackFunction: (key: K) => V): V {
20
20
  if (!this.has(key)) {
21
21
  this.set(key, callbackFunction(key));
22
22
  }
@@ -119,34 +119,31 @@ export class ObjectWrapper<Events> implements EventTarget<Events> {
119
119
  export function eventMixin<Events, Base extends Platform.Constructor.Constructor<object>>(base: Base) {
120
120
  console.assert(base !== HTMLElement);
121
121
  return class EventHandling extends base implements EventTarget<Events> {
122
- // Note that the weird name is due to TSC disallowing private/protected fields in
123
- // anonmous exported classes. We use a `__` prefix to prevent clashes with `base`.
124
- // eslint-disable-next-line @devtools/no-underscored-properties, @typescript-eslint/naming-convention
125
- __events = new ObjectWrapper<Events>();
122
+ #events = new ObjectWrapper<Events>();
126
123
 
127
124
  addEventListener<T extends keyof Events>(
128
125
  eventType: T, listener: (arg0: EventTargetEvent<Events[T]>) => void,
129
126
  thisObject?: Object): EventDescriptor<Events, T> {
130
- return this.__events.addEventListener(eventType, listener, thisObject);
127
+ return this.#events.addEventListener(eventType, listener, thisObject);
131
128
  }
132
129
 
133
130
  once<T extends keyof Events>(eventType: T): Promise<Events[T]> {
134
- return this.__events.once(eventType);
131
+ return this.#events.once(eventType);
135
132
  }
136
133
 
137
134
  removeEventListener<T extends keyof Events>(
138
135
  eventType: T, listener: (arg0: EventTargetEvent<Events[T]>) => void, thisObject?: Object): void {
139
- this.__events.removeEventListener(eventType, listener, thisObject);
136
+ this.#events.removeEventListener(eventType, listener, thisObject);
140
137
  }
141
138
 
142
139
  hasEventListeners(eventType: keyof Events): boolean {
143
- return this.__events.hasEventListeners(eventType);
140
+ return this.#events.hasEventListeners(eventType);
144
141
  }
145
142
 
146
143
  dispatchEventToListeners<T extends keyof Events>(
147
144
  eventType: Platform.TypeScriptUtilities.NoUnion<T>,
148
145
  ...eventData: EventPayloadToRestParameters<Events, T>): void {
149
- this.__events.dispatchEventToListeners(eventType, ...eventData);
146
+ this.#events.dispatchEventToListeners(eventType, ...eventData);
150
147
  }
151
148
  };
152
149
  }
@@ -17,7 +17,7 @@ export class VersionController {
17
17
  static readonly SYNCED_VERSION_SETTING_NAME = 'syncedInspectorVersion';
18
18
  static readonly LOCAL_VERSION_SETTING_NAME = 'localInspectorVersion';
19
19
 
20
- static readonly CURRENT_VERSION = 43;
20
+ static readonly CURRENT_VERSION = 44;
21
21
 
22
22
  readonly #settings: Settings;
23
23
  readonly #globalVersionSetting: Setting<number>;
@@ -848,6 +848,22 @@ export class VersionController {
848
848
  }
849
849
  }
850
850
 
851
+ updateVersionFrom43To44(): void {
852
+ const apcaExperimentEnabled =
853
+ Root.Runtime.experiments.getValueFromStorage('apca' as Root.ExperimentNames.ExperimentName);
854
+ if (apcaExperimentEnabled !== undefined) {
855
+ if (this.#settings.syncedStorage.has('apca')) {
856
+ return; // Already migrated
857
+ }
858
+ try {
859
+ const apcaSetting = this.#settings.moduleSetting('apca');
860
+ apcaSetting.set(apcaExperimentEnabled);
861
+ } catch {
862
+ // If the setting is not registered yet (e.g. in tests), skip.
863
+ }
864
+ }
865
+ }
866
+
851
867
  /*
852
868
  * Any new migration should be added before this comment.
853
869
  *
@@ -816,7 +816,6 @@ export enum DevtoolsExperiments {
816
816
  'protocol-monitor' = 13,
817
817
  'sampling-heap-profiler-timeline' = 17,
818
818
  'timeline-invalidation-tracking' = 26,
819
- apca = 39,
820
819
  'font-editor' = 41,
821
820
  'instrumentation-breakpoints' = 61,
822
821
  'use-source-map-scopes' = 76,
@@ -9,7 +9,6 @@ export enum ExperimentName {
9
9
  PROTOCOL_MONITOR = 'protocol-monitor',
10
10
  SAMPLING_HEAP_PROFILER_TIMELINE = 'sampling-heap-profiler-timeline',
11
11
  TIMELINE_INVALIDATION_TRACKING = 'timeline-invalidation-tracking',
12
- APCA = 'apca',
13
12
  FONT_EDITOR = 'font-editor',
14
13
  INSTRUMENTATION_BREAKPOINTS = 'instrumentation-breakpoints',
15
14
  USE_SOURCE_MAP_SCOPES = 'use-source-map-scopes',
@@ -7,7 +7,6 @@ import type * as ProtocolProxyApi from '../../generated/protocol-proxy-api.js';
7
7
  import * as Protocol from '../../generated/protocol.js';
8
8
  import * as Common from '../common/common.js';
9
9
  import * as i18n from '../i18n/i18n.js';
10
- import * as Root from '../root/root.js';
11
10
 
12
11
  import type {CSSModel} from './CSSModel.js';
13
12
  import {DebuggerModel, Events as DebuggerModelEvents} from './DebuggerModel.js';
@@ -534,9 +533,8 @@ export class OverlayModel extends SDKModel<EventTypes> implements ProtocolProxyA
534
533
  gridHighlightConfig: {},
535
534
  flexContainerHighlightConfig: {},
536
535
  flexItemHighlightConfig: {},
537
- contrastAlgorithm: Root.Runtime.experiments.isEnabled(Root.ExperimentNames.ExperimentName.APCA) ?
538
- Protocol.Overlay.ContrastAlgorithm.Apca :
539
- Protocol.Overlay.ContrastAlgorithm.Aa,
536
+ contrastAlgorithm: settings.moduleSetting('apca').get() ? Protocol.Overlay.ContrastAlgorithm.Apca :
537
+ Protocol.Overlay.ContrastAlgorithm.Aa,
540
538
  };
541
539
 
542
540
  if (mode === 'all' || mode === 'content') {
@@ -403,6 +403,10 @@ const UIStrings = {
403
403
  * @description Title of a setting under the Console category in Settings
404
404
  */
405
405
  logXmlhttprequests: 'Log XMLHttpRequests',
406
+ /**
407
+ * @description Title of a setting under the Elements category in Settings.
408
+ */
409
+ apca: 'Advanced Perceptual Contrast Algorithm (APCA) replacing previous contrast ratio and AA/AAA guidelines',
406
410
  } as const;
407
411
  const str_ = i18n.i18n.registerUIStrings('core/sdk/sdk-meta.ts', UIStrings);
408
412
  const i18nLazyString = i18n.i18n.getLazilyComputedLocalizedString.bind(undefined, str_);
@@ -521,6 +525,15 @@ Common.Settings.registerSettingExtension({
521
525
  defaultValue: false,
522
526
  });
523
527
 
528
+ Common.Settings.registerSettingExtension({
529
+ category: Common.Settings.SettingCategory.ELEMENTS,
530
+ storageType: Common.Settings.SettingStorageType.SYNCED,
531
+ title: i18nLazyString(UIStrings.apca),
532
+ settingName: 'apca',
533
+ settingType: Common.Settings.SettingType.BOOLEAN,
534
+ defaultValue: false,
535
+ });
536
+
524
537
  Common.Settings.registerSettingExtension({
525
538
  category: Common.Settings.SettingCategory.GRID,
526
539
  storageType: Common.Settings.SettingStorageType.SYNCED,
@@ -3,6 +3,10 @@
3
3
  // found in the LICENSE file.
4
4
 
5
5
  import '../../core/dom_extension/dom_extension.js';
6
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
7
+ // @ts-ignore: tsc 6.0 does not support side-effect imports without a type definition.
8
+ // We cannot use `@ts-expect-error` here because the import is correctly resolved
9
+ // when bundling the application (which doesn't error) and only errors in unbundled builds.
6
10
  import '../../Images/Images.js';
7
11
 
8
12
  if (window.opener) {
@@ -368,7 +368,6 @@ async function init(): Promise<void> {
368
368
  safeRegisterExperiment(Root.ExperimentNames.ExperimentName.PROTOCOL_MONITOR, 'Protocol Monitor');
369
369
  safeRegisterExperiment(
370
370
  Root.ExperimentNames.ExperimentName.SAMPLING_HEAP_PROFILER_TIMELINE, 'Sampling heap profiler timeline');
371
- safeRegisterExperiment(Root.ExperimentNames.ExperimentName.APCA, 'APCA');
372
371
 
373
372
  const hostUnsyncedStorage: Common.Settings.SettingsBackingStore = {
374
373
  register: (name: string) =>
@@ -254,7 +254,6 @@ async function init(): Promise<void> {
254
254
  Root.Runtime.experiments.register(Root.ExperimentNames.ExperimentName.PROTOCOL_MONITOR, 'Protocol Monitor');
255
255
  Root.Runtime.experiments.register(
256
256
  Root.ExperimentNames.ExperimentName.SAMPLING_HEAP_PROFILER_TIMELINE, 'Sampling heap profiler timeline');
257
- Root.Runtime.experiments.register(Root.ExperimentNames.ExperimentName.APCA, 'APCA');
258
257
 
259
258
  const WINDOW_LOCAL_STORAGE: Common.Settings.SettingsBackingStore = {
260
259
  register(_setting: string): void{},
@@ -372,12 +372,6 @@ export class MainImpl {
372
372
  Root.Runtime.experiments.register(
373
373
  Root.ExperimentNames.ExperimentName.USE_SOURCE_MAP_SCOPES, 'Use scope information from source maps');
374
374
 
375
- // Advanced Perceptual Contrast Algorithm.
376
- Root.Runtime.experiments.register(
377
- Root.ExperimentNames.ExperimentName.APCA,
378
- 'Advanced Perceptual Contrast Algorithm (APCA) replacing previous contrast ratio and AA/AAA guidelines',
379
- 'https://developer.chrome.com/blog/new-in-devtools-89/#apca');
380
-
381
375
  // Font Editor
382
376
  Root.Runtime.experiments.register(
383
377
  Root.ExperimentNames.ExperimentName.FONT_EDITOR, 'New font editor in the Styles tab',
@@ -2,6 +2,10 @@
2
2
  // Use of this source code is governed by a BSD-style license that can be
3
3
  // found in the LICENSE file.
4
4
 
5
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
6
+ // @ts-ignore: tsc 6.0 does not support side-effect imports without a type definition.
7
+ // We cannot use `@ts-expect-error` here because the import is correctly resolved
8
+ // when bundling the application (which doesn't error) and only errors in unbundled builds.
5
9
  import '../../Images/Images.js';
6
10
  import '../../core/dom_extension/dom_extension.js';
7
11
  import '../../panels/sources/sources-meta.js';
@@ -6,6 +6,10 @@ import '../main/main-meta.js';
6
6
  import '../inspector_main/inspector_main-meta.js';
7
7
  import '../../core/sdk/sdk-meta.js';
8
8
  import '../../models/workspace/workspace-meta.js';
9
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
10
+ // @ts-ignore: tsc 6.0 does not support side-effect imports without a type definition.
11
+ // We cannot use `@ts-expect-error` here because the import is correctly resolved
12
+ // when bundling the application (which doesn't error) and only errors in unbundled builds.
9
13
  import '../../Images/Images.js';
10
14
  import '../../models/logs/logs-meta.js';
11
15
  import '../../models/persistence/persistence-meta.js';
@@ -1408,7 +1408,7 @@ inspectorBackend.registerCommand("Target.setDiscoverTargets", [{"name": "discove
1408
1408
  inspectorBackend.registerCommand("Target.setRemoteLocations", [{"name": "locations", "type": "array", "optional": false, "description": "List of remote locations.", "typeRef": "Target.RemoteLocation"}], [], "Enables target discovery for the specified locations, when `setDiscoverTargets` was set to `true`.");
1409
1409
  inspectorBackend.registerCommand("Target.getDevToolsTarget", [{"name": "targetId", "type": "string", "optional": false, "description": "Page or tab target ID.", "typeRef": "Target.TargetID"}], ["targetId"], "Gets the targetId of the DevTools page target opened for the given target (if any).");
1410
1410
  inspectorBackend.registerCommand("Target.openDevTools", [{"name": "targetId", "type": "string", "optional": false, "description": "This can be the page or tab target ID.", "typeRef": "Target.TargetID"}, {"name": "panelId", "type": "string", "optional": true, "description": "The id of the panel we want DevTools to open initially. Currently supported panels are elements, console, network, sources, resources and performance.", "typeRef": null}], ["targetId"], "Opens a DevTools window for the target.");
1411
- inspectorBackend.registerType("Target.TargetInfo", [{"name": "targetId", "type": "string", "optional": false, "description": "", "typeRef": "Target.TargetID"}, {"name": "type", "type": "string", "optional": false, "description": "List of types: https://source.chromium.org/chromium/chromium/src/+/main:content/browser/devtools/devtools_agent_host_impl.cc?ss=chromium&q=f:devtools%20-f:out%20%22::kTypeTab%5B%5D%22", "typeRef": null}, {"name": "title", "type": "string", "optional": false, "description": "", "typeRef": null}, {"name": "url", "type": "string", "optional": false, "description": "", "typeRef": null}, {"name": "attached", "type": "boolean", "optional": false, "description": "Whether the target has an attached client.", "typeRef": null}, {"name": "openerId", "type": "string", "optional": true, "description": "Opener target Id", "typeRef": "Target.TargetID"}, {"name": "canAccessOpener", "type": "boolean", "optional": false, "description": "Whether the target has access to the originating window.", "typeRef": null}, {"name": "openerFrameId", "type": "string", "optional": true, "description": "Frame id of originating window (is only set if target has an opener).", "typeRef": "Page.FrameId"}, {"name": "parentFrameId", "type": "string", "optional": true, "description": "Id of the parent frame, only present for the \\\"iframe\\\" targets.", "typeRef": "Page.FrameId"}, {"name": "browserContextId", "type": "string", "optional": true, "description": "", "typeRef": "Browser.BrowserContextID"}, {"name": "subtype", "type": "string", "optional": true, "description": "Provides additional details for specific target types. For example, for the type of \\\"page\\\", this may be set to \\\"prerender\\\".", "typeRef": null}]);
1411
+ inspectorBackend.registerType("Target.TargetInfo", [{"name": "targetId", "type": "string", "optional": false, "description": "", "typeRef": "Target.TargetID"}, {"name": "type", "type": "string", "optional": false, "description": "List of types: https://source.chromium.org/chromium/chromium/src/+/main:content/browser/devtools/devtools_agent_host_impl.cc?ss=chromium&q=f:devtools%20-f:out%20%22::kTypeTab%5B%5D%22", "typeRef": null}, {"name": "title", "type": "string", "optional": false, "description": "", "typeRef": null}, {"name": "url", "type": "string", "optional": false, "description": "", "typeRef": null}, {"name": "attached", "type": "boolean", "optional": false, "description": "Whether the target has an attached client.", "typeRef": null}, {"name": "openerId", "type": "string", "optional": true, "description": "Opener target Id", "typeRef": "Target.TargetID"}, {"name": "canAccessOpener", "type": "boolean", "optional": false, "description": "Whether the target has access to the originating window.", "typeRef": null}, {"name": "openerFrameId", "type": "string", "optional": true, "description": "Frame id of originating window (is only set if target has an opener).", "typeRef": "Page.FrameId"}, {"name": "parentFrameId", "type": "string", "optional": true, "description": "Id of the parent frame, present for \\\"iframe\\\" and \\\"worker\\\" targets. For nested workers, this is the \\\"ancestor\\\" frame that created the first worker in the nested chain.", "typeRef": "Page.FrameId"}, {"name": "browserContextId", "type": "string", "optional": true, "description": "", "typeRef": "Browser.BrowserContextID"}, {"name": "subtype", "type": "string", "optional": true, "description": "Provides additional details for specific target types. For example, for the type of \\\"page\\\", this may be set to \\\"prerender\\\".", "typeRef": null}]);
1412
1412
  inspectorBackend.registerType("Target.FilterEntry", [{"name": "exclude", "type": "boolean", "optional": true, "description": "If set, causes exclusion of matching targets from the list.", "typeRef": null}, {"name": "type", "type": "string", "optional": true, "description": "If not present, matches any type.", "typeRef": null}]);
1413
1413
  inspectorBackend.registerType("Target.TargetFilter", [{"name": "TargetFilter", "type": "array", "optional": false, "description": "The entries in TargetFilter are matched sequentially against targets and the first entry that matches determines if the target is included or not, depending on the value of `exclude` field in the entry. If filter is not specified, the one assumed is [{type: \\\"browser\\\", exclude: true}, {type: \\\"tab\\\", exclude: true}, {}] (i.e. include everything but `browser` and `tab`).", "typeRef": "Target.FilterEntry"}]);
1414
1414
  inspectorBackend.registerType("Target.RemoteLocation", [{"name": "host", "type": "string", "optional": false, "description": "", "typeRef": null}, {"name": "port", "type": "number", "optional": false, "description": "", "typeRef": null}]);
@@ -1489,7 +1489,7 @@ inspectorBackend.registerType("WebAuthn.VirtualAuthenticatorOptions", [{"name":
1489
1489
  inspectorBackend.registerType("WebAuthn.Credential", [{"name": "credentialId", "type": "string", "optional": false, "description": "", "typeRef": null}, {"name": "isResidentCredential", "type": "boolean", "optional": false, "description": "", "typeRef": null}, {"name": "rpId", "type": "string", "optional": true, "description": "Relying Party ID the credential is scoped to. Must be set when adding a credential.", "typeRef": null}, {"name": "privateKey", "type": "string", "optional": false, "description": "The ECDSA P-256 private key in PKCS#8 format.", "typeRef": null}, {"name": "userHandle", "type": "string", "optional": true, "description": "An opaque byte sequence with a maximum size of 64 bytes mapping the credential to a specific user.", "typeRef": null}, {"name": "signCount", "type": "number", "optional": false, "description": "Signature counter. This is incremented by one for each successful assertion. See https://w3c.github.io/webauthn/#signature-counter", "typeRef": null}, {"name": "largeBlob", "type": "string", "optional": true, "description": "The large blob associated with the credential. See https://w3c.github.io/webauthn/#sctn-large-blob-extension", "typeRef": null}, {"name": "backupEligibility", "type": "boolean", "optional": true, "description": "Assertions returned by this credential will have the backup eligibility (BE) flag set to this value. Defaults to the authenticator's defaultBackupEligibility value.", "typeRef": null}, {"name": "backupState", "type": "boolean", "optional": true, "description": "Assertions returned by this credential will have the backup state (BS) flag set to this value. Defaults to the authenticator's defaultBackupState value.", "typeRef": null}, {"name": "userName", "type": "string", "optional": true, "description": "The credential's user.name property. Equivalent to empty if not set. https://w3c.github.io/webauthn/#dom-publickeycredentialentity-name", "typeRef": null}, {"name": "userDisplayName", "type": "string", "optional": true, "description": "The credential's user.displayName property. Equivalent to empty if not set. https://w3c.github.io/webauthn/#dom-publickeycredentialuserentity-displayname", "typeRef": null}]);
1490
1490
 
1491
1491
  // WebMCP.
1492
- inspectorBackend.registerEnum("WebMCP.InvocationStatus", {Success: "Success", Canceled: "Canceled", Error: "Error"});
1492
+ inspectorBackend.registerEnum("WebMCP.InvocationStatus", {Completed: "Completed", Canceled: "Canceled", Error: "Error"});
1493
1493
  inspectorBackend.registerEvent("WebMCP.toolsAdded", ["tools"]);
1494
1494
  inspectorBackend.registerEvent("WebMCP.toolsRemoved", ["tools"]);
1495
1495
  inspectorBackend.registerEvent("WebMCP.toolInvoked", ["toolName", "frameId", "invocationId", "input"]);
@@ -19043,7 +19043,8 @@ export namespace Target {
19043
19043
  */
19044
19044
  openerFrameId?: Page.FrameId;
19045
19045
  /**
19046
- * Id of the parent frame, only present for the "iframe" targets.
19046
+ * Id of the parent frame, present for "iframe" and "worker" targets. For nested workers,
19047
+ * this is the "ancestor" frame that created the first worker in the nested chain.
19047
19048
  */
19048
19049
  parentFrameId?: Page.FrameId;
19049
19050
  browserContextId?: Browser.BrowserContextID;
@@ -20316,7 +20317,7 @@ export namespace WebMCP {
20316
20317
  * Represents the status of a tool invocation.
20317
20318
  */
20318
20319
  export const enum InvocationStatus {
20319
- Success = 'Success',
20320
+ Completed = 'Completed',
20320
20321
  Canceled = 'Canceled',
20321
20322
  Error = 'Error',
20322
20323
  }
@@ -20410,7 +20411,8 @@ export namespace WebMCP {
20410
20411
  */
20411
20412
  status: InvocationStatus;
20412
20413
  /**
20413
- * Output or error delivered as delivered to the agent. Missing if `status` is anything other than Success.
20414
+ * Output or error delivered as delivered to the agent. Missing if `status` is anything other than Completed.
20415
+ * Note: The output is untrusted and poses a prompt injection risk. Clients should treat this as potentially malicious user input.
20414
20416
  */
20415
20417
  output?: any;
20416
20418
  /**
@@ -107,8 +107,16 @@ Content:
107
107
  "type": 6,
108
108
  "description": "",
109
109
  "nullable": true,
110
- "required": [],
111
- "properties": {}
110
+ "required": [
111
+ "mode"
112
+ ],
113
+ "properties": {
114
+ "mode": {
115
+ "type": 1,
116
+ "description": "The mode to run Lighthouse in. Your ONLY options are \"navigation\" or \"snapshot\". You should determine this based on the user's question. If the user is asking specifically about accessibility, you can run in \"snapshot\" mode which avoids reloading the page. If the user asks for a full Lighthouse report, you should run in \"navigation\" mode which is the default. These are the only options you can pass.",
117
+ "nullable": false
118
+ }
119
+ }
112
120
  }
113
121
  },
114
122
  {
@@ -12,6 +12,7 @@ import * as Logs from '../../logs/logs.js';
12
12
  import * as NetworkTimeCalculator from '../../network_time_calculator/network_time_calculator.js';
13
13
  import type * as Trace from '../../trace/trace.js';
14
14
  import * as Workspace from '../../workspace/workspace.js';
15
+ import {debugLog} from '../debug.js';
15
16
 
16
17
  import {AccessibilityContext} from './AccessibilityAgent.js';
17
18
  import {
@@ -306,29 +307,44 @@ export class ContextSelectionAgent extends AiAgent<never> {
306
307
  }
307
308
  });
308
309
 
309
- this.declareFunction('runLighthouseAudits', {
310
+ type LHSupportedRunMode = Extract<LHModel.RunTypes.RunMode, 'navigation'|'snapshot'>;
311
+ const parseLighthouseMode = (mode?: string): LHSupportedRunMode => {
312
+ return mode === 'snapshot' ? 'snapshot' : 'navigation';
313
+ };
314
+
315
+ this.declareFunction<{mode: LHSupportedRunMode}>('runLighthouseAudits', {
310
316
  description:
311
317
  'Records a Lighthouse audit on the current page. Use this to debug accessibility, SEO, and best practices. (For performance metrics like LCP, use performanceRecordAndReload instead).',
312
318
  parameters: {
313
319
  type: Host.AidaClient.ParametersTypes.OBJECT,
314
320
  description: '',
315
321
  nullable: true,
316
- required: [],
317
- properties: {},
322
+ required: ['mode'],
323
+ properties: {
324
+ mode: {
325
+ type: Host.AidaClient.ParametersTypes.STRING,
326
+ description:
327
+ 'The mode to run Lighthouse in. Your ONLY options are "navigation" or "snapshot". You should determine this based on the user\'s question. If the user is asking specifically about accessibility, you can run in "snapshot" mode which avoids reloading the page. If the user asks for a full Lighthouse report, you should run in "navigation" mode which is the default. These are the only options you can pass.',
328
+ nullable: false,
329
+ }
330
+ },
318
331
  },
319
- displayInfoFromArgs: () => {
332
+ displayInfoFromArgs: args => {
333
+ const mode = parseLighthouseMode(args.mode);
320
334
  return {
321
335
  title: 'Auditing your page with Lighthouse',
322
- action: 'runLighthouseAudits()',
336
+ action: `runLighthouseAudits(${mode})`,
323
337
  };
324
338
  },
325
- handler: async () => {
339
+ handler: async params => {
326
340
  if (!this.#lighthouseRecording) {
327
341
  return {
328
342
  error: 'Lighthouse report is not available.',
329
343
  };
330
344
  }
331
- const result = await this.#lighthouseRecording();
345
+ const mode = parseLighthouseMode(params.mode);
346
+ debugLog(`Recording with Lighthouse; runMode=${mode}`);
347
+ const result = await this.#lighthouseRecording({mode});
332
348
  if (!result) {
333
349
  return {error: 'Failed to generate Lighthouse report.'};
334
350
  }
@@ -49,6 +49,13 @@ const UIStringsNotTranslated = {
49
49
  } as const;
50
50
  const lockedString = i18n.i18n.lockedString;
51
51
 
52
+ /**
53
+ * Labels used to identify specific periods or categories in the trace for getting main thread summary.
54
+ * Supports hardcoded phases, dynamic navigation IDs (`NAVIGATION_X`), and insight models.
55
+ */
56
+ type MainThreadSectionLabel = 'nav-to-lcp'|'lcp-ttfb'|'lcp-render-delay'|'trace-bounds'|'NO_NAVIGATION'|
57
+ `NAVIGATION_${string}`|keyof Trace.Insights.Types.InsightModels;
58
+
52
59
  /**
53
60
  * WARNING: preamble defined in code is only used when userTier is
54
61
  * TESTERS. Otherwise, a server-side preamble is used (see
@@ -102,7 +109,8 @@ You can also use this key with \`selectEventByKey\` to show the user a specific
102
109
 
103
110
  ## Step-by-step instructions for debugging performance issues
104
111
 
105
- Note: if the user asks a specific question about the trace (such as "What is my LCP?", or "How many requests were render-blocking?", directly answer their question and skip starting a performance investigation. Otherwise, your task is to collaborate with the user to discover and resolve real performance issues.
112
+ Note: if the user asks a specific question about the trace (such as "What is my LCP?", or "How many requests were render-blocking?"), directly answer their question using available data. However, if the user asks a general question like "What performance issues exist?" or requests an investigation, you MUST NOT give a generic answer. You must treat it as a full performance investigation (Step 1) and call main thread functions to find specific issues. Generic advice like "reduce long tasks" without specific details is UNACCEPTABLE.
113
+
106
114
 
107
115
  ### Step 1: Determine a performance problem to investigate
108
116
 
@@ -138,11 +146,22 @@ Note: if the user asks a specific question about the trace (such as "What is my
138
146
 
139
147
  ## Guidelines
140
148
 
149
+ - You must call \`getMainThreadTrackSummary\` (by specifying a time range) or \`getMainThreadTrackSummaryByLabel\` (with the relevant label) to investigate the main thread activity before giving the user a reply or suggesting solutions for any performance problem or insight. Call \`getMainThreadTrackSummary\` with specific bounds when you identify a specific time range of interest from the initial data. This applies even if you already have some information about that period from \`getInsightDetails\` or the initial trace summary.
150
+ - Dig Deeper: Before replying, you should really dig into the main thread activity to uncover what the performance issues actually are. Use the initially provided main thread data as a reference to identify interesting tasks or time ranges, and then use those time bounds to call functions like \`getMainThreadTrackSummary\` to get more detailed data. Do not solely rely on the information from the initial data; ensure you identify the root cause before suggesting solutions.
151
+ - No Shortcutting: Even if the initial facts contain specific line numbers or function names, you are not allowed to reply using only that information. You MUST call \`getMainThreadTrackSummary\` with the time bounds of the task to inspect its full context and children before describing it to the user.
152
+ - Look for Aggregated Cost: Performance issues are not always caused by a single "Long Task". Many small, frequent events (like unthrottled \`mousemove\` or \`scroll\` handlers) can add up to significant main thread blockage. Use the Bottom-Up summary in \`getMainThreadTrackSummary\` to identify functions with high total time, even if they are not associated with a Long Task.
141
153
  - Use the provided functions to get detailed performance data. Prioritize functions that provide context relevant to the performance issue being investigated.
142
154
  - Before finalizing your advice, look over it and validate using any relevant functions. If something seems off, refine the advice before giving it to the user.
143
155
  - Base your analysis and advice solely on the data retrieved through the provided functions. Always use the provided functions to gather sufficient data when needed.
144
156
  - Use absolute microsecond timestamps for any function that requires a \`min\` and \`max\` bounds. These timestamps can be found in the trace summary or within the details of an insight.
145
157
  - Example: If the trace bounds are {min: 1000, max: 5000} and you want to investigate a specific interaction that happened between 2000 and 3000, you should call \`getMainThreadTrackSummary({min: 2000, max: 3000})\`.
158
+ - Available labels for \`getMainThreadTrackSummaryByLabel\` include:
159
+ - \`trace-bounds\` (entire trace)
160
+ - \`nav-to-lcp\` (navigation to LCP)
161
+ - \`lcp-ttfb\` (LCP TTFB phase)
162
+ - \`lcp-render-delay\` (LCP render delay phase)
163
+ - Insight names: \`LCPBreakdown\`, \`CLSCulprits\`, \`RenderBlocking\`, \`NetworkDependencyTree\`, \`ImageDelivery\`, \`FontDisplay\`, \`ThirdParties\`, \`ForcedReflow\`, \`Cache\`, \`DOMSize\`
164
+ - Navigation IDs: \`NAVIGATION_0\`, \`NAVIGATION_1\`, etc.
146
165
  - Use \`getEventByKey\` to get data on a specific trace event. This is great for root-cause analysis or validating any assumptions.
147
166
  - Provide clear, actionable recommendations. Avoid technical jargon unless necessary, and explain any technical terms used.
148
167
  - If you see a generic task like "Task", "Evaluate script" or "(anonymous)" in the main thread activity, try to look at its children to see what actual functions are executed and refer to those. When referencing the main thread activity, be as specific as you can. Ensure you identify to the user relevant functions and which script they were defined in. Avoid referencing "Task", "Evaluate script" and "(anonymous)" nodes if possible and instead focus on their children.
@@ -777,6 +796,52 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
777
796
  this.#functionCallCacheForFocus.set(focus, cache);
778
797
  }
779
798
 
799
+ async #handleMainThreadTrackSummary(
800
+ bounds: Trace.Types.Timing.TraceWindowMicro,
801
+ focus: AgentFocus,
802
+ functionName: string,
803
+ cacheKey: string,
804
+ ): Promise<FunctionCallHandlerResult<{summary: string}>> {
805
+ const formatter = this.#formatter;
806
+ if (!formatter) {
807
+ throw new Error('missing formatter');
808
+ }
809
+ const summary = await formatter.formatMainThreadTrackSummary(bounds);
810
+ if (this.#isFunctionResponseTooLarge(summary)) {
811
+ return {
812
+ error:
813
+ `${functionName} response is too large. Try investigating using other functions, or a more narrow bounds`,
814
+ };
815
+ }
816
+
817
+ const byteCount = Platform.StringUtilities.countWtf8Bytes(summary);
818
+ Host.userMetrics.performanceAIMainThreadActivityResponseSize(byteCount);
819
+
820
+ this.#cacheFunctionResult(focus, cacheKey, summary);
821
+ const widgets: AiWidget[] = [];
822
+ widgets.push({
823
+ name: 'TIMELINE_RANGE_SUMMARY',
824
+ data: {
825
+ parsedTrace: focus.parsedTrace,
826
+ bounds,
827
+ track: 'main',
828
+ },
829
+ });
830
+
831
+ widgets.push({
832
+ name: 'BOTTOM_UP_TREE',
833
+ data: {
834
+ bounds,
835
+ parsedTrace: focus.parsedTrace,
836
+ },
837
+ });
838
+
839
+ return {
840
+ result: {summary},
841
+ widgets,
842
+ };
843
+ }
844
+
780
845
  #declareFunctions(context: PerformanceTraceContext): void {
781
846
  const focus = context.getItem();
782
847
  const {parsedTrace} = focus;
@@ -972,53 +1037,51 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
972
1037
  handler: async args => {
973
1038
  debugLog('Function call: getMainThreadTrackSummary');
974
1039
 
975
- if (!this.#formatter) {
976
- throw new Error('missing formatter');
977
- }
978
-
979
1040
  const bounds = createBounds(args.min, args.max);
980
1041
  if (!bounds) {
981
1042
  return {error: 'invalid bounds'};
982
1043
  }
983
1044
 
984
- const formatter = this.#formatter;
985
- const summary = await formatter.formatMainThreadTrackSummary(bounds);
986
- if (this.#isFunctionResponseTooLarge(summary)) {
987
- return {
988
- error:
989
- 'getMainThreadTrackSummary response is too large. Try investigating using other functions, or a more narrow bounds',
990
- };
991
- }
992
-
993
- const byteCount = Platform.StringUtilities.countWtf8Bytes(summary);
994
- Host.userMetrics.performanceAIMainThreadActivityResponseSize(byteCount);
995
-
996
1045
  const key = `getMainThreadTrackSummary({min: ${bounds.min}, max: ${bounds.max}})`;
997
- this.#cacheFunctionResult(focus, key, summary);
998
- const widgets: AiWidget[] = [];
999
- widgets.push({
1000
- name: 'TIMELINE_RANGE_SUMMARY',
1001
- data: {
1002
- parsedTrace,
1003
- bounds,
1004
- track: 'main',
1005
- },
1006
- });
1046
+ return await this.#handleMainThreadTrackSummary(bounds, focus, 'getMainThreadTrackSummary', key);
1047
+ },
1007
1048
 
1008
- widgets.push({
1009
- name: 'BOTTOM_UP_TREE',
1010
- data: {
1011
- bounds,
1012
- parsedTrace,
1013
- },
1014
- });
1049
+ });
1015
1050
 
1051
+ this.declareFunction<{label: MainThreadSectionLabel}, {summary: string}>('getMainThreadTrackSummaryByLabel', {
1052
+ description:
1053
+ 'Returns a focused, detailed summary of the main thread for a predefined labeled period. Use this to get more relevant detail than the initial trace summary before diagnosing issues.',
1054
+ parameters: {
1055
+ type: Host.AidaClient.ParametersTypes.OBJECT,
1056
+ description: '',
1057
+ nullable: false,
1058
+ properties: {
1059
+ label: {
1060
+ type: Host.AidaClient.ParametersTypes.STRING,
1061
+ description:
1062
+ 'The label of the period to investigate (e.g., \'LCPBreakdown\', \'CLSCulprits\', \'nav-to-lcp\').',
1063
+ nullable: false,
1064
+ },
1065
+ },
1066
+ required: ['label']
1067
+ },
1068
+ displayInfoFromArgs: args => {
1016
1069
  return {
1017
- result: {summary},
1018
- widgets,
1070
+ title: lockedString(UIStringsNotTranslated.mainThreadActivity),
1071
+ action: `getMainThreadTrackSummaryByLabel('${args.label}')`
1019
1072
  };
1020
1073
  },
1074
+ handler: async args => {
1075
+ debugLog('Function call: getMainThreadTrackSummaryByLabel');
1076
+
1077
+ const bounds = this.#getBoundsForLabel(args.label, focus);
1078
+ if (!bounds) {
1079
+ return {error: `Invalid label: ${args.label}`};
1080
+ }
1021
1081
 
1082
+ const key = `getMainThreadTrackSummaryByLabel('${args.label}')`;
1083
+ return await this.#handleMainThreadTrackSummary(bounds, focus, 'getMainThreadTrackSummaryByLabel', key);
1084
+ },
1022
1085
  });
1023
1086
 
1024
1087
  this.declareFunction<
@@ -1362,6 +1425,68 @@ export class PerformanceAgent extends AiAgent<AgentFocus> {
1362
1425
  }
1363
1426
  }
1364
1427
 
1428
+ #getBoundsForLabel(label: MainThreadSectionLabel, focus: AgentFocus): Trace.Types.Timing.TraceWindowMicro|null {
1429
+ const {parsedTrace} = focus;
1430
+ const insightSet = focus.primaryInsightSet;
1431
+
1432
+ if (label === 'nav-to-lcp') {
1433
+ if (insightSet) {
1434
+ const lcp = Trace.Insights.Common.getLCP(insightSet);
1435
+ if (lcp) {
1436
+ return Trace.Helpers.Timing.traceWindowFromMicroSeconds(
1437
+ insightSet.bounds.min, lcp.event.ts as Trace.Types.Timing.Micro);
1438
+ }
1439
+ }
1440
+ return null;
1441
+ }
1442
+
1443
+ if (label === 'lcp-ttfb') {
1444
+ if (insightSet) {
1445
+ const subparts = insightSet.model.LCPBreakdown?.subparts;
1446
+ if (subparts?.ttfb) {
1447
+ return subparts.ttfb;
1448
+ }
1449
+ }
1450
+ return null;
1451
+ }
1452
+
1453
+ if (label === 'lcp-render-delay') {
1454
+ if (insightSet) {
1455
+ const subparts = insightSet.model.LCPBreakdown?.subparts;
1456
+ if (subparts?.renderDelay) {
1457
+ return subparts.renderDelay;
1458
+ }
1459
+ }
1460
+ return null;
1461
+ }
1462
+
1463
+ if (label === 'trace-bounds') {
1464
+ return parsedTrace.data.Meta.traceBounds;
1465
+ }
1466
+
1467
+ for (const is of parsedTrace.insights?.values() ?? []) {
1468
+ if (is.id === label) {
1469
+ return is.bounds;
1470
+ }
1471
+ }
1472
+
1473
+ if (insightSet) {
1474
+ const model = insightSet.model[label as keyof Trace.Insights.Types.InsightModels];
1475
+ if (model) {
1476
+ return Trace.Insights.Common.insightBounds(model, insightSet.bounds);
1477
+ }
1478
+ }
1479
+
1480
+ for (const is of parsedTrace.insights?.values() ?? []) {
1481
+ const model = is.model[label as keyof Trace.Insights.Types.InsightModels];
1482
+ if (model) {
1483
+ return Trace.Insights.Common.insightBounds(model, is.bounds);
1484
+ }
1485
+ }
1486
+
1487
+ return null;
1488
+ }
1489
+
1365
1490
  async addElementAnnotation(elementId: string, annotationMessage: string):
1366
1491
  Promise<FunctionCallHandlerResult<unknown>> {
1367
1492
  if (!Annotations.AnnotationRepository.annotationsEnabled()) {