chrome-devtools-frontend 1.0.1604514 → 1.0.1605219

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 (54) hide show
  1. package/AUTHORS +1 -0
  2. package/front_end/Tests.js +23 -7
  3. package/front_end/core/host/AidaClient.ts +114 -63
  4. package/front_end/core/host/AidaGcaTranslation.ts +6 -0
  5. package/front_end/core/host/GcaClient.ts +112 -0
  6. package/front_end/core/host/UserMetrics.ts +0 -1
  7. package/front_end/core/host/host.ts +2 -0
  8. package/front_end/core/root/ExperimentNames.ts +0 -1
  9. package/front_end/core/root/Runtime.ts +5 -0
  10. package/front_end/core/sdk/AutofillModel.ts +3 -2
  11. package/front_end/core/sdk/CSSModel.ts +3 -4
  12. package/front_end/core/sdk/CSSProperty.ts +1 -1
  13. package/front_end/core/sdk/ConsoleModel.ts +15 -13
  14. package/front_end/core/sdk/CookieModel.ts +5 -4
  15. package/front_end/core/sdk/DOMDebuggerModel.ts +20 -14
  16. package/front_end/core/sdk/DebuggerModel.ts +21 -36
  17. package/front_end/core/sdk/EventBreakpointsModel.ts +16 -6
  18. package/front_end/core/sdk/IsolateManager.ts +12 -7
  19. package/front_end/entrypoints/main/MainImpl.ts +0 -4
  20. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +31 -17
  21. package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.snapshot.txt +1 -1
  22. package/front_end/models/emulation/DeviceModeModel.ts +21 -26
  23. package/front_end/models/trace/insights/LCPDiscovery.ts +14 -6
  24. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +2 -17
  25. package/front_end/panels/ai_assistance/components/ChatMessage.ts +36 -20
  26. package/front_end/panels/ai_assistance/components/chatMessage.css +5 -1
  27. package/front_end/panels/application/ApplicationPanelSidebar.ts +6 -0
  28. package/front_end/panels/application/IndexedDBViews.ts +0 -9
  29. package/front_end/panels/application/WebMCPTreeElement.ts +35 -0
  30. package/front_end/panels/application/WebMCPView.ts +118 -0
  31. package/front_end/panels/application/application.ts +4 -0
  32. package/front_end/panels/application/webMCPView.css +65 -0
  33. package/front_end/panels/common/aiCodeCompletionSummaryToolbar.css +1 -1
  34. package/front_end/panels/console/consoleView.css +4 -0
  35. package/front_end/panels/elements/StylesAiCodeCompletionProvider.ts +23 -2
  36. package/front_end/panels/elements/StylesSidebarPane.ts +85 -20
  37. package/front_end/panels/elements/elementsPanel.css +14 -0
  38. package/front_end/panels/network/NetworkLogViewColumns.ts +3 -3
  39. package/front_end/panels/recorder/components/recordingView.css +0 -6
  40. package/front_end/panels/sensors/SensorsView.ts +28 -59
  41. package/front_end/panels/timeline/EventsTimelineTreeView.ts +2 -2
  42. package/front_end/panels/timeline/ThirdPartyTreeView.ts +35 -36
  43. package/front_end/panels/timeline/TimelineDetailsView.ts +47 -41
  44. package/front_end/panels/timeline/TimelineTreeView.ts +13 -13
  45. package/front_end/panels/timeline/components/LiveMetricsView.ts +839 -783
  46. package/front_end/panels/timeline/thirdPartyTreeView.css +2 -2
  47. package/front_end/ui/components/buttons/button.css +2 -2
  48. package/front_end/ui/components/text_editor/AiCodeCompletionProvider.ts +0 -11
  49. package/front_end/ui/legacy/InspectorDrawerView.ts +206 -0
  50. package/front_end/ui/legacy/InspectorView.ts +41 -91
  51. package/front_end/ui/legacy/ViewManager.ts +14 -5
  52. package/front_end/ui/legacy/inspectorDrawerTabbedPane.css +5 -0
  53. package/front_end/ui/visual_logging/KnownContextValues.ts +2 -1
  54. package/package.json +1 -1
@@ -4,7 +4,7 @@
4
4
 
5
5
  import type * as ProtocolProxyApi from '../../generated/protocol-proxy-api.js';
6
6
  import * as Protocol from '../../generated/protocol.js';
7
- import * as Common from '../common/common.js';
7
+ import type * as Common from '../common/common.js';
8
8
  import * as Platform from '../platform/platform.js';
9
9
 
10
10
  import {CategorizedBreakpoint, Category} from './CategorizedBreakpoint.js';
@@ -38,7 +38,7 @@ export class DOMDebuggerModel extends SDKModel<EventTypes> {
38
38
  this.#domModel.addEventListener(DOMModelEvents.NodeRemoved, this.nodeRemoved, this);
39
39
 
40
40
  this.#domBreakpoints = [];
41
- this.#domBreakpointsSetting = Common.Settings.Settings.instance().createLocalSetting('dom-breakpoints', []);
41
+ this.#domBreakpointsSetting = this.target().targetManager().settings.createLocalSetting('dom-breakpoints', []);
42
42
  if (this.#domModel.existingDocument()) {
43
43
  void this.documentUpdated();
44
44
  }
@@ -498,9 +498,11 @@ export class CSPViolationBreakpoint extends CategorizedBreakpoint {
498
498
 
499
499
  export class DOMEventListenerBreakpoint extends CategorizedBreakpoint {
500
500
  readonly eventTargetNames: string[];
501
- constructor(eventName: string, eventTargetNames: string[], category: Category) {
501
+ readonly #targetManager: TargetManager;
502
+ constructor(eventName: string, eventTargetNames: string[], category: Category, targetManager: TargetManager) {
502
503
  super(category, eventName);
503
504
  this.eventTargetNames = eventTargetNames;
505
+ this.#targetManager = targetManager;
504
506
  }
505
507
 
506
508
  override setEnabled(enabled: boolean): void {
@@ -508,7 +510,7 @@ export class DOMEventListenerBreakpoint extends CategorizedBreakpoint {
508
510
  return;
509
511
  }
510
512
  super.setEnabled(enabled);
511
- for (const model of TargetManager.instance().models(DOMDebuggerModel)) {
513
+ for (const model of this.#targetManager.models(DOMDebuggerModel)) {
512
514
  this.updateOnModel(model);
513
515
  }
514
516
  }
@@ -534,9 +536,11 @@ export class DOMDebuggerManager implements SDKModelObserver<DOMDebuggerModel> {
534
536
 
535
537
  readonly #cspViolationsToBreakOn: CSPViolationBreakpoint[] = [];
536
538
  readonly #eventListenerBreakpoints: DOMEventListenerBreakpoint[] = [];
539
+ readonly #targetManager: TargetManager;
537
540
 
538
- constructor() {
539
- this.#xhrBreakpointsSetting = Common.Settings.Settings.instance().createLocalSetting('xhr-breakpoints', []);
541
+ constructor(targetManager: TargetManager = TargetManager.instance()) {
542
+ this.#targetManager = targetManager;
543
+ this.#xhrBreakpointsSetting = this.#targetManager.settings.createLocalSetting('xhr-breakpoints', []);
540
544
  for (const breakpoint of this.#xhrBreakpointsSetting.get()) {
541
545
  this.#xhrBreakpoints.set(breakpoint.url, breakpoint.enabled);
542
546
  }
@@ -659,15 +663,16 @@ export class DOMDebuggerManager implements SDKModelObserver<DOMDebuggerModel> {
659
663
  Category.XHR, ['readystatechange', 'load', 'loadstart', 'loadend', 'abort', 'error', 'progress', 'timeout'],
660
664
  ['xmlhttprequest', 'xmlhttprequestupload']);
661
665
 
662
- TargetManager.instance().observeModels(DOMDebuggerModel, this);
666
+ this.#targetManager.observeModels(DOMDebuggerModel, this);
663
667
  }
664
668
 
665
669
  static instance(opts: {
666
670
  forceNew: boolean|null,
671
+ targetManager?: TargetManager,
667
672
  } = {forceNew: null}): DOMDebuggerManager {
668
- const {forceNew} = opts;
673
+ const {forceNew, targetManager} = opts;
669
674
  if (!domDebuggerManagerInstance || forceNew) {
670
- domDebuggerManagerInstance = new DOMDebuggerManager();
675
+ domDebuggerManagerInstance = new DOMDebuggerManager(targetManager);
671
676
  }
672
677
 
673
678
  return domDebuggerManagerInstance;
@@ -679,7 +684,8 @@ export class DOMDebuggerManager implements SDKModelObserver<DOMDebuggerModel> {
679
684
 
680
685
  private createEventListenerBreakpoints(category: Category, eventNames: string[], eventTargetNames: string[]): void {
681
686
  for (const eventName of eventNames) {
682
- this.#eventListenerBreakpoints.push(new DOMEventListenerBreakpoint(eventName, eventTargetNames, category));
687
+ this.#eventListenerBreakpoints.push(
688
+ new DOMEventListenerBreakpoint(eventName, eventTargetNames, category, this.#targetManager));
683
689
  }
684
690
  }
685
691
 
@@ -710,7 +716,7 @@ export class DOMDebuggerManager implements SDKModelObserver<DOMDebuggerModel> {
710
716
 
711
717
  updateCSPViolationBreakpoints(): void {
712
718
  const violationTypes = this.#cspViolationsToBreakOn.filter(v => v.enabled()).map(v => v.type());
713
- for (const model of TargetManager.instance().models(DOMDebuggerModel)) {
719
+ for (const model of this.#targetManager.models(DOMDebuggerModel)) {
714
720
  this.updateCSPViolationBreakpointsForModel(model, violationTypes);
715
721
  }
716
722
  }
@@ -735,7 +741,7 @@ export class DOMDebuggerManager implements SDKModelObserver<DOMDebuggerModel> {
735
741
  addXHRBreakpoint(url: string, enabled: boolean): void {
736
742
  this.#xhrBreakpoints.set(url, enabled);
737
743
  if (enabled) {
738
- for (const model of TargetManager.instance().models(DOMDebuggerModel)) {
744
+ for (const model of this.#targetManager.models(DOMDebuggerModel)) {
739
745
  void model.agent.invoke_setXHRBreakpoint({url});
740
746
  }
741
747
  }
@@ -746,7 +752,7 @@ export class DOMDebuggerManager implements SDKModelObserver<DOMDebuggerModel> {
746
752
  const enabled = this.#xhrBreakpoints.get(url);
747
753
  this.#xhrBreakpoints.delete(url);
748
754
  if (enabled) {
749
- for (const model of TargetManager.instance().models(DOMDebuggerModel)) {
755
+ for (const model of this.#targetManager.models(DOMDebuggerModel)) {
750
756
  void model.agent.invoke_removeXHRBreakpoint({url});
751
757
  }
752
758
  }
@@ -755,7 +761,7 @@ export class DOMDebuggerManager implements SDKModelObserver<DOMDebuggerModel> {
755
761
 
756
762
  toggleXHRBreakpoint(url: string, enabled: boolean): void {
757
763
  this.#xhrBreakpoints.set(url, enabled);
758
- for (const model of TargetManager.instance().models(DOMDebuggerModel)) {
764
+ for (const model of this.#targetManager.models(DOMDebuggerModel)) {
759
765
  if (enabled) {
760
766
  void model.agent.invoke_setXHRBreakpoint({url});
761
767
  } else {
@@ -173,30 +173,19 @@ export class DebuggerModel extends SDKModel<EventTypes> {
173
173
  (compiledURL, sourceMappingURL, payload, script) =>
174
174
  new SourceMap(compiledURL, sourceMappingURL, payload, script));
175
175
 
176
- Common.Settings.Settings.instance()
177
- .moduleSetting('pause-on-exception-enabled')
178
- .addChangeListener(this.pauseOnExceptionStateChanged, this);
179
- Common.Settings.Settings.instance()
180
- .moduleSetting('pause-on-caught-exception')
181
- .addChangeListener(this.pauseOnExceptionStateChanged, this);
182
- Common.Settings.Settings.instance()
183
- .moduleSetting('pause-on-uncaught-exception')
184
- .addChangeListener(this.pauseOnExceptionStateChanged, this);
185
- Common.Settings.Settings.instance()
186
- .moduleSetting('disable-async-stack-traces')
187
- .addChangeListener(this.asyncStackTracesStateChanged, this);
188
- Common.Settings.Settings.instance()
189
- .moduleSetting('breakpoints-active')
190
- .addChangeListener(this.breakpointsActiveChanged, this);
176
+ const settings = this.target().targetManager().settings;
177
+ settings.moduleSetting('pause-on-exception-enabled').addChangeListener(this.pauseOnExceptionStateChanged, this);
178
+ settings.moduleSetting('pause-on-caught-exception').addChangeListener(this.pauseOnExceptionStateChanged, this);
179
+ settings.moduleSetting('pause-on-uncaught-exception').addChangeListener(this.pauseOnExceptionStateChanged, this);
180
+ settings.moduleSetting('disable-async-stack-traces').addChangeListener(this.asyncStackTracesStateChanged, this);
181
+ settings.moduleSetting('breakpoints-active').addChangeListener(this.breakpointsActiveChanged, this);
191
182
 
192
183
  if (!target.suspended()) {
193
184
  void this.enableDebugger();
194
185
  }
195
186
 
196
- this.#sourceMapManager.setEnabled(
197
- Common.Settings.Settings.instance().moduleSetting('js-source-maps-enabled').get());
198
- Common.Settings.Settings.instance()
199
- .moduleSetting('js-source-maps-enabled')
187
+ this.#sourceMapManager.setEnabled(settings.moduleSetting('js-source-maps-enabled').get());
188
+ settings.moduleSetting('js-source-maps-enabled')
200
189
  .addChangeListener(event => this.#sourceMapManager.setEnabled((event.data as boolean)));
201
190
 
202
191
  const resourceTreeModel = (target.model(ResourceTreeModel) as ResourceTreeModel);
@@ -273,7 +262,8 @@ export class DebuggerModel extends SDKModel<EventTypes> {
273
262
  }
274
263
  this.pauseOnExceptionStateChanged();
275
264
  void this.asyncStackTracesStateChanged();
276
- if (!Common.Settings.Settings.instance().moduleSetting('breakpoints-active').get()) {
265
+ const settings = this.target().targetManager().settings;
266
+ if (!settings.moduleSetting('breakpoints-active').get()) {
277
267
  this.breakpointsActiveChanged();
278
268
  }
279
269
  this.dispatchEventToListeners(Events.DebuggerWasEnabled, this);
@@ -364,11 +354,11 @@ export class DebuggerModel extends SDKModel<EventTypes> {
364
354
  }
365
355
 
366
356
  private pauseOnExceptionStateChanged(): void {
367
- const pauseOnCaughtEnabled = Common.Settings.Settings.instance().moduleSetting('pause-on-caught-exception').get();
357
+ const settings = this.target().targetManager().settings;
358
+ const pauseOnCaughtEnabled = settings.moduleSetting('pause-on-caught-exception').get();
368
359
  let state: Protocol.Debugger.SetPauseOnExceptionsRequestState;
369
360
 
370
- const pauseOnUncaughtEnabled =
371
- Common.Settings.Settings.instance().moduleSetting('pause-on-uncaught-exception').get();
361
+ const pauseOnUncaughtEnabled = settings.moduleSetting('pause-on-uncaught-exception').get();
372
362
  if (pauseOnCaughtEnabled && pauseOnUncaughtEnabled) {
373
363
  state = Protocol.Debugger.SetPauseOnExceptionsRequestState.All;
374
364
  } else if (pauseOnCaughtEnabled) {
@@ -383,15 +373,15 @@ export class DebuggerModel extends SDKModel<EventTypes> {
383
373
 
384
374
  private asyncStackTracesStateChanged(): Promise<Protocol.ProtocolResponseWithError> {
385
375
  const maxAsyncStackChainDepth = 32;
386
- const enabled =
387
- !Common.Settings.Settings.instance().moduleSetting('disable-async-stack-traces').get() && this.#debuggerEnabled;
376
+ const settings = this.target().targetManager().settings;
377
+ const enabled = !settings.moduleSetting('disable-async-stack-traces').get() && this.#debuggerEnabled;
388
378
  const maxDepth = enabled ? maxAsyncStackChainDepth : 0;
389
379
  return this.agent.invoke_setAsyncCallStackDepth({maxDepth});
390
380
  }
391
381
 
392
382
  private breakpointsActiveChanged(): void {
393
- void this.agent.invoke_setBreakpointsActive(
394
- {active: Common.Settings.Settings.instance().moduleSetting('breakpoints-active').get()});
383
+ const settings = this.target().targetManager().settings;
384
+ void this.agent.invoke_setBreakpointsActive({active: settings.moduleSetting('breakpoints-active').get()});
395
385
  }
396
386
 
397
387
  setComputeAutoStepRangesCallback(callback: ((arg0: StepMode, arg1: CallFrame) => Promise<LocationRange[]>)|null):
@@ -872,15 +862,10 @@ export class DebuggerModel extends SDKModel<EventTypes> {
872
862
  if (this.#debuggerId) {
873
863
  debuggerIdToModel.delete(this.#debuggerId);
874
864
  }
875
- Common.Settings.Settings.instance()
876
- .moduleSetting('pause-on-exception-enabled')
877
- .removeChangeListener(this.pauseOnExceptionStateChanged, this);
878
- Common.Settings.Settings.instance()
879
- .moduleSetting('pause-on-caught-exception')
880
- .removeChangeListener(this.pauseOnExceptionStateChanged, this);
881
- Common.Settings.Settings.instance()
882
- .moduleSetting('disable-async-stack-traces')
883
- .removeChangeListener(this.asyncStackTracesStateChanged, this);
865
+ const settings = this.target().targetManager().settings;
866
+ settings.moduleSetting('pause-on-exception-enabled').removeChangeListener(this.pauseOnExceptionStateChanged, this);
867
+ settings.moduleSetting('pause-on-caught-exception').removeChangeListener(this.pauseOnExceptionStateChanged, this);
868
+ settings.moduleSetting('disable-async-stack-traces').removeChangeListener(this.asyncStackTracesStateChanged, this);
884
869
  }
885
870
 
886
871
  override async suspendModel(): Promise<void> {
@@ -56,12 +56,18 @@ export class EventBreakpointsModel extends SDKModel<void> {
56
56
  * instrumentation breakpoints in targets that run JS but do not have a DOM.
57
57
  **/
58
58
  class EventListenerBreakpoint extends CategorizedBreakpoint {
59
+ readonly #targetManager: TargetManager;
60
+ constructor(category: Category, name: string, targetManager: TargetManager) {
61
+ super(category, name);
62
+ this.#targetManager = targetManager;
63
+ }
64
+
59
65
  override setEnabled(enabled: boolean): void {
60
66
  if (this.enabled() === enabled) {
61
67
  return;
62
68
  }
63
69
  super.setEnabled(enabled);
64
- for (const model of TargetManager.instance().models(EventBreakpointsModel)) {
70
+ for (const model of this.#targetManager.models(EventBreakpointsModel)) {
65
71
  this.updateOnModel(model);
66
72
  }
67
73
  }
@@ -81,8 +87,10 @@ let eventBreakpointManagerInstance: EventBreakpointsManager;
81
87
 
82
88
  export class EventBreakpointsManager implements SDKModelObserver<EventBreakpointsModel> {
83
89
  readonly #eventListenerBreakpoints: EventListenerBreakpoint[] = [];
90
+ readonly #targetManager: TargetManager;
84
91
 
85
- constructor() {
92
+ constructor(targetManager: TargetManager = TargetManager.instance()) {
93
+ this.#targetManager = targetManager;
86
94
  this.createInstrumentationBreakpoints(Category.AUCTION_WORKLET, [
87
95
  InstrumentationNames.BEFORE_BIDDER_WORKLET_BIDDING_START,
88
96
  InstrumentationNames.BEFORE_BIDDER_WORKLET_REPORTING_START,
@@ -135,15 +143,16 @@ export class EventBreakpointsManager implements SDKModelObserver<EventBreakpoint
135
143
  InstrumentationNames.AUDIO_CONTEXT_SUSPENDED,
136
144
  ]);
137
145
 
138
- TargetManager.instance().observeModels(EventBreakpointsModel, this);
146
+ this.#targetManager.observeModels(EventBreakpointsModel, this);
139
147
  }
140
148
 
141
149
  static instance(opts: {
142
150
  forceNew: boolean|null,
151
+ targetManager?: TargetManager,
143
152
  } = {forceNew: null}): EventBreakpointsManager {
144
- const {forceNew} = opts;
153
+ const {forceNew, targetManager} = opts;
145
154
  if (!eventBreakpointManagerInstance || forceNew) {
146
- eventBreakpointManagerInstance = new EventBreakpointsManager();
155
+ eventBreakpointManagerInstance = new EventBreakpointsManager(targetManager);
147
156
  }
148
157
 
149
158
  return eventBreakpointManagerInstance;
@@ -151,7 +160,8 @@ export class EventBreakpointsManager implements SDKModelObserver<EventBreakpoint
151
160
 
152
161
  private createInstrumentationBreakpoints(category: Category, instrumentationNames: InstrumentationNames[]): void {
153
162
  for (const instrumentationName of instrumentationNames) {
154
- this.#eventListenerBreakpoints.push(new EventListenerBreakpoint(category, instrumentationName));
163
+ this.#eventListenerBreakpoints.push(
164
+ new EventListenerBreakpoint(category, instrumentationName, this.#targetManager));
155
165
  }
156
166
  }
157
167
 
@@ -19,18 +19,21 @@ export class IsolateManager extends Common.ObjectWrapper.ObjectWrapper<EventType
19
19
  #isolateIdByModel = new Map<RuntimeModel, string|null>();
20
20
  #observers = new Set<Observer>();
21
21
  #pollId = 0;
22
+ readonly #targetManager: TargetManager;
22
23
 
23
- constructor() {
24
+ constructor(targetManager: TargetManager = TargetManager.instance()) {
24
25
  super();
26
+ this.#targetManager = targetManager;
25
27
 
26
- TargetManager.instance().observeModels(RuntimeModel, this);
28
+ this.#targetManager.observeModels(RuntimeModel, this);
27
29
  }
28
30
 
29
- static instance({forceNew}: {
31
+ static instance({forceNew, targetManager}: {
30
32
  forceNew: boolean,
33
+ targetManager?: TargetManager,
31
34
  } = {forceNew: false}): IsolateManager {
32
35
  if (!isolateManagerInstance || forceNew) {
33
- isolateManagerInstance = new IsolateManager();
36
+ isolateManagerInstance = new IsolateManager(targetManager);
34
37
  }
35
38
 
36
39
  return isolateManagerInstance;
@@ -67,7 +70,7 @@ export class IsolateManager extends Common.ObjectWrapper.ObjectWrapper<EventType
67
70
  this.#isolateIdByModel.set(model, isolateId);
68
71
  let isolate = this.#isolates.get(isolateId);
69
72
  if (!isolate) {
70
- isolate = new Isolate(isolateId);
73
+ isolate = new Isolate(isolateId, this);
71
74
  this.#isolates.set(isolateId, isolate);
72
75
  }
73
76
  isolate.models().add(model);
@@ -145,9 +148,11 @@ export class Isolate {
145
148
  readonly #models: Set<RuntimeModel>;
146
149
  #usedHeapSize: number;
147
150
  readonly #memoryTrend: MemoryTrend;
151
+ readonly #manager: IsolateManager;
148
152
 
149
- constructor(id: string) {
153
+ constructor(id: string, manager: IsolateManager) {
150
154
  this.#id = id;
155
+ this.#manager = manager;
151
156
  this.#models = new Set();
152
157
  this.#usedHeapSize = 0;
153
158
  const count = MemoryTrendWindowMs / PollIntervalMs;
@@ -179,7 +184,7 @@ export class Isolate {
179
184
  }
180
185
  this.#usedHeapSize = usage.usedSize + (usage.embedderHeapUsedSize ?? 0) + (usage.backingStorageSize ?? 0);
181
186
  this.#memoryTrend.add(this.#usedHeapSize);
182
- IsolateManager.instance().dispatchEventToListeners(Events.MEMORY_CHANGED, this);
187
+ this.#manager.dispatchEventToListeners(Events.MEMORY_CHANGED, this);
183
188
  }
184
189
 
185
190
  samplesCount(): number {
@@ -397,10 +397,6 @@ export class MainImpl {
397
397
  Root.ExperimentNames.ExperimentName.FONT_EDITOR, 'New font editor in the Styles tab',
398
398
  'https://developer.chrome.com/blog/new-in-devtools-89/#font');
399
399
 
400
- // New cookie features.
401
- Root.Runtime.experiments.register(
402
- Root.ExperimentNames.ExperimentName.EXPERIMENTAL_COOKIE_FEATURES, 'Experimental cookie features');
403
-
404
400
  Root.Runtime.experiments.registerHostExperiment({
405
401
  name: Root.ExperimentNames.ExperimentName.DURABLE_MESSAGES,
406
402
  title: 'Durable Messages',
@@ -82,7 +82,7 @@ Your primary goal is to provide actionable advice to web developers about their
82
82
 
83
83
  You will be provided a summary of a trace: some performance metrics; the most critical network requests; a bottom-up call graph summary; and a brief overview of available insights. Each insight has information about potential performance issues with the page.
84
84
 
85
- Don't mention anything about an insight or the actual LCP element without first getting more data about it by calling \`getInsightDetails\`.
85
+ Always call getInsightDetails to gather more data on an insight or the actual LCP element BEFORE mentioning any specific details about them.
86
86
 
87
87
  You have functions available to learn more about the trace. Use these to confirm hypotheses, or to further explore the trace when diagnosing performance issues.
88
88
 
@@ -105,23 +105,41 @@ Note: if the user asks a specific question about the trace (such as "What is my
105
105
 
106
106
  ### Step 1: Determine a performance problem to investigate
107
107
 
108
+ - If the trace summary indicates that the main performance metrics (LCP, INP, CLS) are all within good thresholds, acknowledge this to the user. In this case, let the user know that they can try recording a trace with mobile emulation and throttling options and show them how.
108
109
  - With help from the user, determine what performance problem to focus on.
109
- - If the user is not specific about what problem to investigate, help them by doing a investigation yourself. Present to the user options with 1-sentence summaries. Mention what performance metrics each option impacts. Call as many functions and confirm the data thoroughly: never present an option without being certain it is a real performance issue. Don't suggest solutions yet.
110
- - Rank the options from most impactful to least impactful, and present them to the user in that order.
111
- - Don't present more than 2 options.
110
+ - If the user is not specific about what problem to investigate, help them by doing a investigation yourself focus on performance improvements for better LCP, INP and CLS. Present to the user options with 1-sentence summaries. Mention what performance metrics each option impacts. Call as many functions and confirm the data thoroughly: never present an option without being certain it is a real performance issue.
111
+ - Focus on identifying the problem in Step 1 and save solution suggestions for Step 2.
112
112
  - Once a performance problem has been identified for investigation, move on to step 2.
113
113
 
114
+ #### Response Structure
115
+
116
+ - Rank the options from most impactful to least impactful, and present them to the user in that order.
117
+ - Limit the number of performance problem options presented to the user to a maximum of 2.
118
+
114
119
  ### Step 2: Suggest solutions
115
120
 
116
121
  - Suggest solutions to remedy the identified performance problem. Be as specific as possible, using data from the trace via the provided functions to back up everything you say. You should prefer specific solutions, but absent any specific solution you may suggest general solutions (such as from an insight's documentation links).
117
122
  - If you are unsure, be honest and present information that can be helpful for further investigation.
118
123
  - A good first step to discover solutions is to consider the insights, but you should also validate all potential advice by analyzing the trace until you are confident about the root cause of a performance issue.
119
124
 
125
+ #### Response Structure
126
+
127
+ - If available, point out the root cause(s) of the problem.
128
+ - Example: "**Root Cause**: The page is slow because of [reason]."
129
+ - Example: "**Root Causes**:"
130
+ - [Reason 1]
131
+ - [Reason 2]
132
+ - if applicable, list actionable solution suggestion(s) in order of impact:
133
+ - Example: "**Suggestion**: [Suggestion 1]
134
+ - Example: "**Suggestions**:"
135
+ - [Suggestion 1]
136
+ - [Suggestion 2]
137
+
120
138
  ## Guidelines
121
139
 
122
140
  - Use the provided functions to get detailed performance data. Prioritize functions that provide context relevant to the performance issue being investigated.
123
141
  - 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.
124
- - Do not rely on assumptions or incomplete information. Use the provided functions to get more data when needed.
142
+ - 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.
125
143
  - Use the track summary functions to get high-level detail about portions of the trace. For the \`bounds\` parameter, default to using the bounds of the trace. Never specifically ask the user for a bounds. You can use more narrow bounds (such as the bounds relevant to a specific insight) when appropriate. Narrow the bounds given functions when possible.
126
144
  - Use \`getEventByKey\` to get data on a specific trace event. This is great for root-cause analysis or validating any assumptions.
127
145
  - Provide clear, actionable recommendations. Avoid technical jargon unless necessary, and explain any technical terms used.
@@ -147,15 +165,6 @@ Adhere to the following critical requirements:
147
165
  - If asked about sensitive topics (religion, race, politics, sexuality, gender, etc.), respond with: "My expertise is limited to website performance analysis. I cannot provide information on that topic.".
148
166
  - Do not provide answers on non-web-development topics, such as legal, financial, medical, or personal advice.
149
167
  - Use the precision of Strunk & White, the brevity of Hemingway, and the simple clarity of Vonnegut. Don't add repeated information, and keep the whole answer short.
150
-
151
- ## Response Structure
152
-
153
- - If available, point out the root cause of the problem. It may be a bullet point list.
154
- - Example: "**Root Cause**: The page is slow because of [reason]."
155
- - if applicable, list actionable solution suggestion(s) in order of impact:
156
- - Example: "**Suggestions**:
157
- - [Suggestion 1]
158
- - [Suggestion 2]
159
168
  `;
160
169
  };
161
170
 
@@ -166,7 +175,7 @@ When referring to a trace event that has a corresponding \`eventKey\`, annotate
166
175
  - When referring to a URL for which you know the eventKey of: [https://www.example.com](#s-1827)
167
176
  - Never show the eventKey (like "eventKey: s-1852"); instead, use a markdown link as described above.
168
177
 
169
- When asking the user to make a choice between multiple options, output a list of choices at the end of your text response. The format is \`SUGGESTIONS: ["suggestion1", "suggestion2", "suggestion3"]\`. This MUST start on a newline, and be a single line.
178
+ When asking the user to make a choice between options, output a list of choices at the end of your text response. The format is \`SUGGESTIONS: ["suggestion1", "suggestion2", "suggestion3"]\`. This MUST start on a newline, and be a single line.
170
179
  `;
171
180
 
172
181
  const buildExtraPreambleWhenFreshTrace = (): string => {
@@ -231,8 +240,13 @@ export class PerformanceTraceContext extends ConversationContext<AgentFocus> {
231
240
  }
232
241
 
233
242
  override getOrigin(): string {
234
- const {min, max} = this.#focus.parsedTrace.data.Meta.traceBounds;
235
- return `trace-${min}-${max}`;
243
+ try {
244
+ const url = new URL(this.#focus.parsedTrace.data.Meta.mainFrameURL);
245
+ return url.origin;
246
+ } catch {
247
+ const {min, max} = this.#focus.parsedTrace.data.Meta.traceBounds;
248
+ return `trace-${min}-${max}`;
249
+ }
236
250
  }
237
251
 
238
252
  override getItem(): AgentFocus {
@@ -226,7 +226,7 @@ Response headers
226
226
 
227
227
  The result of the checks for this insight are:
228
228
  - fetchpriority=high should be applied: FAILED
229
- - lazy load not applied: PASSED
229
+ - LCP resources should not use loading=lazy: PASSED
230
230
  - Request is discoverable in initial document: PASSED
231
231
 
232
232
  ## Estimated savings: none
@@ -795,35 +795,30 @@ export class DeviceModeModel extends Common.ObjectWrapper.ObjectWrapper<EventTyp
795
795
  overlayModel.setShowViewportSizeOnResize(false);
796
796
  }
797
797
 
798
- const screenshot = await screenCaptureModel.captureScreenshot(
799
- Protocol.Page.CaptureScreenshotRequestFormat.Png, 100, screenshotMode, clip);
800
-
801
- const deviceMetrics: Protocol.Page.SetDeviceMetricsOverrideRequest = {
802
- width: 0,
803
- height: 0,
804
- deviceScaleFactor: 0,
805
- mobile: false,
806
- };
807
- if (fullSize && this.#emulationModel) {
808
- if (this.#device && this.#mode) {
809
- const orientation = this.#device.orientationByName(this.#mode.orientation);
810
- deviceMetrics.width = orientation.width;
811
- deviceMetrics.height = orientation.height;
812
- const dispFeature = this.getDisplayFeature();
813
- if (dispFeature) {
814
- // @ts-expect-error: displayFeature isn't in protocol.ts but is an
815
- // experimental flag:
816
- // https://chromedevtools.github.io/devtools-protocol/tot/Emulation/#method-setDeviceMetricsOverride
817
- deviceMetrics.displayFeature = dispFeature;
818
- }
819
- } else {
820
- deviceMetrics.width = 0;
821
- deviceMetrics.height = 0;
798
+ if (this.#emulationModel && this.#device && this.#mode) {
799
+ const orientation = this.#device.orientationByName(this.#mode.orientation);
800
+ const deviceMetrics: Protocol.Emulation.SetDeviceMetricsOverrideRequest = {
801
+ width: orientation.width,
802
+ height: orientation.height,
803
+ deviceScaleFactor: this.#device.deviceScaleFactor,
804
+ mobile: this.isMobile(),
805
+ };
806
+ const dispFeature = this.getDisplayFeature();
807
+ if (dispFeature) {
808
+ deviceMetrics.displayFeature = dispFeature;
822
809
  }
823
810
  await this.#emulationModel.emulateDevice(deviceMetrics);
824
811
  }
825
- this.calculateAndEmulate(false);
826
- return screenshot;
812
+
813
+ try {
814
+ const screenshot = await screenCaptureModel.captureScreenshot(
815
+ Protocol.Page.CaptureScreenshotRequestFormat.Png, 100, screenshotMode, clip);
816
+ return screenshot;
817
+ } finally {
818
+ await this.#emulationModel?.emulateDevice(null);
819
+ overlayModel?.setShowViewportSizeOnResize(this.#type === Type.None);
820
+ this.calculateAndEmulate(false);
821
+ }
827
822
  }
828
823
 
829
824
  private applyTouch(touchEnabled: boolean, mobile: boolean): void {
@@ -41,14 +41,18 @@ export const UIStrings = {
41
41
  * @description Text to tell the user that a fetchpriority property value of "high" should be applied to the LCP request.
42
42
  */
43
43
  fetchPriorityShouldBeApplied: 'fetchpriority=high should be applied',
44
+ /**
45
+ * @description Text to tell the user that a fetchpriority property value of "high" should be applied to the preload request that loads the LCP image.
46
+ */
47
+ fetchPriorityShouldBeAppliedToImagePreload: 'fetchpriority=high should be applied to the image preload request',
44
48
  /**
45
49
  * @description Text to tell the user that the LCP request is discoverable in the initial document.
46
50
  */
47
51
  requestDiscoverable: 'Request is discoverable in initial document',
48
52
  /**
49
- * @description Text to tell the user that the LCP request does not have the lazy load property applied.
53
+ * @description Text to tell the user that LCP resources should avoid using loading=lazy.
50
54
  */
51
- lazyLoadNotApplied: 'lazy load not applied',
55
+ lazyLoadNotApplied: 'LCP resources should not use loading=lazy',
52
56
  /**
53
57
  * @description Text status indicating that the the Largest Contentful Paint (LCP) metric timing was not found. "LCP" is an acronym and should not be translated.
54
58
  */
@@ -133,12 +137,17 @@ export function generateInsight(
133
137
  lcpRequest?.args.data.initiator?.type === 'parser' && docRequest.args.data.url === initiatorUrl;
134
138
  const imgPreloadedOrFoundInHTML = lcpRequest?.args.data.isLinkPreload || initiatedByMainDoc;
135
139
 
136
- const imageLoadingAttr = lcpEvent.args.data?.loadingAttr;
137
140
  const imageFetchPriorityHint = lcpRequest?.args.data.fetchPriorityHint;
138
141
  // This is the earliest discovery time an LCP request could have - it's TTFB (as an absolute timestamp).
139
142
  const earliestDiscoveryTime = calculateDocFirstByteTs(docRequest);
140
143
 
141
144
  const priorityHintFound = imageFetchPriorityHint === 'high';
145
+ const missingPriorityHintLabel = lcpRequest.args.data.isLinkPreload ?
146
+ i18nString(UIStrings.fetchPriorityShouldBeAppliedToImagePreload) :
147
+ i18nString(UIStrings.fetchPriorityShouldBeApplied);
148
+ // A lazy-loaded LCP image can still be eagerly loaded when its request is
149
+ // initiated by a preload.
150
+ const lcpNotLazyLoaded = lcpEvent.args.data?.loadingAttr !== 'lazy' || lcpRequest.args.data.isLinkPreload;
142
151
 
143
152
  return finalize({
144
153
  lcpEvent,
@@ -146,12 +155,11 @@ export function generateInsight(
146
155
  earliestDiscoveryTimeTs: earliestDiscoveryTime ? Types.Timing.Micro(earliestDiscoveryTime) : undefined,
147
156
  checklist: {
148
157
  priorityHinted: {
149
- label: priorityHintFound ? i18nString(UIStrings.fetchPriorityApplied) :
150
- i18nString(UIStrings.fetchPriorityShouldBeApplied),
158
+ label: priorityHintFound ? i18nString(UIStrings.fetchPriorityApplied) : missingPriorityHintLabel,
151
159
  value: priorityHintFound
152
160
  },
153
161
  requestDiscoverable: {label: i18nString(UIStrings.requestDiscoverable), value: imgPreloadedOrFoundInHTML},
154
- eagerlyLoaded: {label: i18nString(UIStrings.lazyLoadNotApplied), value: imageLoadingAttr !== 'lazy'},
162
+ eagerlyLoaded: {label: i18nString(UIStrings.lazyLoadNotApplied), value: lcpNotLazyLoaded},
155
163
  },
156
164
  });
157
165
  }
@@ -379,14 +379,6 @@ interface ToolbarViewInput {
379
379
  showChatActions: boolean;
380
380
  showActiveConversationActions: boolean;
381
381
  isLoading: boolean;
382
-
383
- walkthrough: {
384
- isExpanded: boolean,
385
- isInlined: boolean,
386
- onToggle: (isOpen: boolean, message: ModelChatMessage) => void,
387
- activeSidebarMessage: ModelChatMessage|null,
388
- inlineExpandedMessages: ModelChatMessage[],
389
- };
390
382
  }
391
383
 
392
384
  export const enum ViewState {
@@ -514,7 +506,7 @@ function defaultView(input: ViewInput, output: PanelViewOutput, target: HTMLElem
514
506
  if (Root.Runtime.hostConfig.devToolsAiAssistanceV2?.enabled ||
515
507
  Greendev.Prototypes.instance().isEnabled('breakpointDebuggerAgent')) {
516
508
 
517
- const shouldShowWalkthrough = input.state === ViewState.CHAT_VIEW && input.walkthrough.isExpanded;
509
+ const shouldShowWalkthrough = input.state === ViewState.CHAT_VIEW && input.props.walkthrough.isExpanded;
518
510
  /**
519
511
  * We want to mark the walkthrough as loading only if it's showing the last
520
512
  * message. Otherwise, a previous walkthrough will show as loading if we
@@ -535,7 +527,7 @@ function defaultView(input: ViewInput, output: PanelViewOutput, target: HTMLElem
535
527
  name="ai-assistance-split-view-state"
536
528
  direction="column"
537
529
  sidebar-position="second"
538
- sidebar-visibility=${shouldShowWalkthrough && !input.walkthrough.isInlined ? 'visible' : 'hidden'}
530
+ sidebar-visibility=${shouldShowWalkthrough && !input.props.walkthrough.isInlined ? 'visible' : 'hidden'}
539
531
  sidebar-initial-size=${WALKTHROUGH_SIDEBAR_INITIAL_WIDTH}
540
532
  >
541
533
  <div slot="main" class="main-view">
@@ -716,13 +708,6 @@ export class AiAssistancePanel extends UI.Panel.Panel {
716
708
  onSettingsClick: () => {
717
709
  void UI.ViewManager.ViewManager.instance().showView('chrome-ai');
718
710
  },
719
- walkthrough: {
720
- isExpanded: this.#walkthrough.isExpanded,
721
- isInlined: this.#walkthrough.isInlined,
722
- onToggle: this.#toggleWalkthrough.bind(this),
723
- activeSidebarMessage: this.#walkthrough.activeSidebarMessage,
724
- inlineExpandedMessages: this.#walkthrough.inlineExpandedMessages,
725
- }
726
711
  };
727
712
  }
728
713