chrome-devtools-frontend 1.0.1666631 → 1.0.1667564

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 (78) hide show
  1. package/front_end/core/host/AidaClient.ts +6 -4
  2. package/front_end/core/host/InspectorFrontendHostAPI.ts +1 -0
  3. package/front_end/core/host/InspectorFrontendHostStub.ts +3 -3
  4. package/front_end/core/host/UserMetrics.ts +49 -1
  5. package/front_end/core/sdk/CSSMatchedStyles.ts +2 -5
  6. package/front_end/core/sdk/CSSPropertyParserMatchers.ts +12 -0
  7. package/front_end/core/sdk/Connections.ts +1 -1
  8. package/front_end/core/sdk/DOMModel.test.api.ts +104 -0
  9. package/front_end/core/sdk/DOMModel.ts +23 -11
  10. package/front_end/core/sdk/DebuggerModel.ts +9 -9
  11. package/front_end/core/sdk/IsolateManager.ts +1 -1
  12. package/front_end/core/sdk/NetworkManager.ts +105 -4
  13. package/front_end/core/sdk/OverlayModel.ts +15 -14
  14. package/front_end/core/sdk/RemoteObject.ts +1 -1
  15. package/front_end/devtools_compatibility.js +1 -0
  16. package/front_end/entrypoints/main/MainImpl.ts +5 -2
  17. package/front_end/entrypoints/main/SimpleApp.ts +9 -3
  18. package/front_end/foundation/Universe.ts +6 -0
  19. package/front_end/models/ai_assistance/contexts/PerformanceTraceContext.ts +23 -10
  20. package/front_end/models/computed_style/ComputedStyleModel.ts +4 -7
  21. package/front_end/models/crux-manager/CrUXManager.ts +4 -0
  22. package/front_end/models/extensions/ExtensionAPI.ts +3 -3
  23. package/front_end/models/heap_snapshot/HeapSnapshotProxy.ts +2 -2
  24. package/front_end/models/live-metrics/LiveMetrics.ts +59 -19
  25. package/front_end/models/live-metrics/web-vitals-injected/README.md +1 -1
  26. package/front_end/models/live-metrics/web-vitals-injected/spec/spec.ts +6 -1
  27. package/front_end/models/live-metrics/web-vitals-injected/web-vitals-injected.ts +11 -3
  28. package/front_end/models/logs/LogManager.ts +1 -1
  29. package/front_end/models/persistence/FileSystemWorkspaceBinding.ts +1 -1
  30. package/front_end/models/source_map_scopes/FunctionCodeResolver.ts +6 -7
  31. package/front_end/models/workspace_diff/WorkspaceDiff.ts +13 -14
  32. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +15 -7
  33. package/front_end/panels/application/BackgroundServiceView.ts +18 -20
  34. package/front_end/panels/autofill/AutofillView.ts +17 -17
  35. package/front_end/panels/autofill/autofillView.css +7 -8
  36. package/front_end/panels/browser_debugger/CategorizedBreakpointsSidebarPane.ts +31 -31
  37. package/front_end/panels/browser_debugger/DOMBreakpointsSidebarPane.ts +15 -15
  38. package/front_end/panels/browser_debugger/XHRBreakpointsSidebarPane.ts +12 -12
  39. package/front_end/panels/browser_debugger/browser_debugger-meta.ts +27 -27
  40. package/front_end/panels/common/AiCodeCompletionDisclaimer.ts +14 -6
  41. package/front_end/panels/common/AiCodeCompletionSummaryToolbar.ts +14 -6
  42. package/front_end/panels/common/AiCodeCompletionTeaser.ts +13 -6
  43. package/front_end/panels/common/GeminiRebrandPromoDialog.ts +1 -1
  44. package/front_end/panels/console/ConsoleInsightTeaser.ts +13 -6
  45. package/front_end/panels/console/ConsoleView.ts +6 -6
  46. package/front_end/panels/console_counters/WarningErrorCounter.ts +4 -4
  47. package/front_end/panels/coverage/CoverageModel.ts +11 -1
  48. package/front_end/panels/elements/StylePropertiesSection.ts +50 -17
  49. package/front_end/panels/elements/StylesAiCodeCompletionProvider.ts +18 -7
  50. package/front_end/panels/emulation/AdvancedApp.ts +18 -8
  51. package/front_end/panels/event_listeners/EventListenersView.ts +8 -8
  52. package/front_end/panels/explain/components/ConsoleInsight.ts +15 -7
  53. package/front_end/panels/network/NetworkLogView.ts +13 -14
  54. package/front_end/panels/screencast/ScreencastApp.ts +12 -6
  55. package/front_end/panels/screencast/ScreencastView.ts +37 -11
  56. package/front_end/panels/sensors/locationsSettingsTab.css +4 -0
  57. package/front_end/panels/settings/AISettingsTab.ts +13 -6
  58. package/front_end/panels/sources/DebuggerPlugin.ts +13 -2
  59. package/front_end/panels/timeline/components/LiveMetricsView.ts +14 -1
  60. package/front_end/panels/timeline/components/cpuThrottlingSelector.css +4 -0
  61. package/front_end/panels/timeline/components/liveMetricsView.css +28 -0
  62. package/front_end/panels/timeline/timeline-meta.ts +13 -0
  63. package/front_end/third_party/chromium/README.chromium +1 -1
  64. package/front_end/ui/components/menus/menu.css +4 -0
  65. package/front_end/ui/components/menus/menuItem.css +4 -0
  66. package/front_end/ui/components/menus/selectMenu.css +4 -0
  67. package/front_end/ui/components/menus/selectMenuButton.css +4 -0
  68. package/front_end/ui/components/text_editor/AiCodeCompletionProvider.ts +21 -10
  69. package/front_end/ui/components/text_editor/AiCodeGenerationProvider.ts +22 -10
  70. package/front_end/ui/components/text_editor/config.ts +7 -0
  71. package/front_end/ui/legacy/AppProvider.ts +2 -1
  72. package/front_end/ui/legacy/RootView.ts +8 -1
  73. package/front_end/ui/legacy/UniverseRequestEvent.ts +19 -0
  74. package/front_end/ui/legacy/Widget.ts +49 -6
  75. package/front_end/ui/legacy/inspectorCommon.css +1 -1
  76. package/front_end/ui/legacy/legacy.ts +2 -0
  77. package/front_end/ui/visual_logging/KnownContextValues.ts +2 -0
  78. package/package.json +1 -1
@@ -522,7 +522,7 @@ export class MainImpl {
522
522
 
523
523
  async #showAppUI(appProvider: Object): Promise<void> {
524
524
  MainImpl.time('Main._showAppUI');
525
- const app = (appProvider as UI.AppProvider.AppProvider).createApp();
525
+ const app = (appProvider as UI.AppProvider.AppProvider).createApp(this.#universe);
526
526
  // It is important to kick controller lifetime after apps are instantiated.
527
527
  UI.DockController.DockController.instance().initialize();
528
528
  ThemeSupport.ThemeSupport.instance().fetchColorsAndApplyHostTheme();
@@ -856,7 +856,10 @@ export class MainMenuItem implements UI.Toolbar.Provider {
856
856
  const button = this.#item.element;
857
857
 
858
858
  function setDockSide(side: UI.DockController.DockState): void {
859
- void dockController.once(UI.DockController.Events.AFTER_DOCK_SIDE_CHANGED).then(() => button.focus());
859
+ if (dockController.dockSide() !== UI.DockController.DockState.UNDOCKED &&
860
+ side !== UI.DockController.DockState.UNDOCKED) {
861
+ void dockController.once(UI.DockController.Events.AFTER_DOCK_SIDE_CHANGED).then(() => button.focus());
862
+ }
860
863
  dockController.setDockSide(side);
861
864
  contextMenu.discard();
862
865
  }
@@ -2,11 +2,17 @@
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
+ import type * as Foundation from '../../foundation/foundation.js';
5
6
  import * as UI from '../../ui/legacy/legacy.js';
6
7
 
7
8
  export class SimpleApp implements UI.App.App {
9
+ readonly #universe: Foundation.Universe.Universe;
10
+ constructor(universe: Foundation.Universe.Universe) {
11
+ this.#universe = universe;
12
+ }
13
+
8
14
  presentUI(document: Document): void {
9
- const rootView = new UI.RootView.RootView();
15
+ const rootView = new UI.RootView.RootView(this.#universe);
10
16
  UI.InspectorView.InspectorView.instance().show(rootView.element);
11
17
  rootView.attachToDocument(document);
12
18
  rootView.focus();
@@ -14,7 +20,7 @@ export class SimpleApp implements UI.App.App {
14
20
  }
15
21
 
16
22
  export class SimpleAppProvider implements UI.AppProvider.AppProvider {
17
- createApp(): UI.App.App {
18
- return new SimpleApp();
23
+ createApp(universe: Foundation.Universe.Universe): UI.App.App {
24
+ return new SimpleApp(universe);
19
25
  }
20
26
  }
@@ -137,6 +137,7 @@ export class Universe {
137
137
 
138
138
  this.fileSystemWorkspaceBinding =
139
139
  new Persistence.FileSystemWorkspaceBinding.FileSystemWorkspaceBinding(isolatedFileSystemManager, workspace);
140
+ context.set(Persistence.FileSystemWorkspaceBinding.FileSystemWorkspaceBinding, this.fileSystemWorkspaceBinding);
140
141
 
141
142
  const ignoreListManager = new Workspace.IgnoreListManager.IgnoreListManager(settings, targetManager);
142
143
  context.set(Workspace.IgnoreListManager.IgnoreListManager, ignoreListManager);
@@ -216,6 +217,7 @@ export class Universe {
216
217
  context.set(AiAssistance.AiHistoryStorage.AiHistoryStorage, aiHistoryStorage);
217
218
 
218
219
  this.autofillManager = new AutofillManager.AutofillManager.AutofillManager(targetManager, frameManager);
220
+ context.set(AutofillManager.AutofillManager.AutofillManager, this.autofillManager);
219
221
  }
220
222
 
221
223
  get automaticFileSystemManager(): Persistence.AutomaticFileSystemManager.AutomaticFileSystemManager {
@@ -347,4 +349,8 @@ export class Universe {
347
349
  get workspaceDiff(): WorkspaceDiff.WorkspaceDiff.WorkspaceDiffImpl {
348
350
  return this.context.get(WorkspaceDiff.WorkspaceDiff.WorkspaceDiffImpl);
349
351
  }
352
+
353
+ get<T>(ctor: Root.DevToolsContext.ConstructorT<T>): T {
354
+ return this.context.get(ctor);
355
+ }
350
356
  }
@@ -7,6 +7,7 @@ import type {UrlString} from '../../../core/platform/DevToolsPath.js';
7
7
  import type * as Platform from '../../../core/platform/platform.js';
8
8
  import * as SDK from '../../../core/sdk/sdk.js';
9
9
  import * as Tracing from '../../../services/tracing/tracing.js';
10
+ import * as Bindings from '../../bindings/bindings.js';
10
11
  import * as SourceMapScopes from '../../source_map_scopes/source_map_scopes.js';
11
12
  import * as Trace from '../../trace/trace.js';
12
13
  import {
@@ -28,52 +29,63 @@ import {AgentFocus} from '../performance/AIContext.js';
28
29
  * the context data for the LLM prompt and user-facing accordion disclosures.
29
30
  */
30
31
  export class PerformanceTraceContext extends ConversationContext<AgentFocus> {
31
- static fromParsedTrace(parsedTrace: Trace.TraceModel.ParsedTrace,
32
- targetManager: SDK.TargetManager.TargetManager = SDK.TargetManager.TargetManager.instance(),
33
- freshRecordingTracker:
34
- Tracing.FreshRecording.Tracker = Tracing.FreshRecording.Tracker.instance()):
35
- PerformanceTraceContext {
32
+ static fromParsedTrace(
33
+ parsedTrace: Trace.TraceModel.ParsedTrace,
34
+ targetManager: SDK.TargetManager.TargetManager = SDK.TargetManager.TargetManager.instance(),
35
+ freshRecordingTracker: Tracing.FreshRecording.Tracker = Tracing.FreshRecording.Tracker.instance(),
36
+ debuggerWorkspaceBinding: Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding =
37
+ Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance()): PerformanceTraceContext {
36
38
  return new PerformanceTraceContext(
37
39
  AgentFocus.fromParsedTrace(parsedTrace),
38
40
  targetManager,
39
41
  freshRecordingTracker,
42
+ debuggerWorkspaceBinding,
40
43
  );
41
44
  }
42
45
 
43
46
  static fromInsight(parsedTrace: Trace.TraceModel.ParsedTrace, insight: Trace.Insights.Types.InsightModel,
44
47
  targetManager: SDK.TargetManager.TargetManager = SDK.TargetManager.TargetManager.instance(),
45
- freshRecordingTracker: Tracing.FreshRecording.Tracker = Tracing.FreshRecording.Tracker.instance()):
48
+ freshRecordingTracker: Tracing.FreshRecording.Tracker = Tracing.FreshRecording.Tracker.instance(),
49
+ debuggerWorkspaceBinding: Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding =
50
+ Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance()):
46
51
  PerformanceTraceContext {
47
52
  return new PerformanceTraceContext(
48
53
  AgentFocus.fromInsight(parsedTrace, insight),
49
54
  targetManager,
50
55
  freshRecordingTracker,
56
+ debuggerWorkspaceBinding,
51
57
  );
52
58
  }
53
59
 
54
60
  static fromCallTree(callTree: AICallTree,
55
61
  targetManager: SDK.TargetManager.TargetManager = SDK.TargetManager.TargetManager.instance(),
56
- freshRecordingTracker:
57
- Tracing.FreshRecording.Tracker = Tracing.FreshRecording.Tracker.instance()):
62
+ freshRecordingTracker: Tracing.FreshRecording.Tracker = Tracing.FreshRecording.Tracker.instance(),
63
+ debuggerWorkspaceBinding: Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding =
64
+ Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance()):
58
65
  PerformanceTraceContext {
59
66
  return new PerformanceTraceContext(
60
67
  AgentFocus.fromCallTree(callTree),
61
68
  targetManager,
62
69
  freshRecordingTracker,
70
+ debuggerWorkspaceBinding,
63
71
  );
64
72
  }
65
73
 
66
74
  readonly #focus: AgentFocus;
67
75
  readonly #targetManager: SDK.TargetManager.TargetManager;
68
76
  readonly #freshRecordingTracker: Tracing.FreshRecording.Tracker;
77
+ readonly #debuggerWorkspaceBinding: Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding;
69
78
 
70
79
  constructor(focus: AgentFocus,
71
80
  targetManager: SDK.TargetManager.TargetManager = SDK.TargetManager.TargetManager.instance(),
72
- freshRecordingTracker: Tracing.FreshRecording.Tracker = Tracing.FreshRecording.Tracker.instance()) {
81
+ freshRecordingTracker: Tracing.FreshRecording.Tracker = Tracing.FreshRecording.Tracker.instance(),
82
+ debuggerWorkspaceBinding: Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding =
83
+ Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance()) {
73
84
  super();
74
85
  this.#focus = focus;
75
86
  this.#targetManager = targetManager;
76
87
  this.#freshRecordingTracker = freshRecordingTracker;
88
+ this.#debuggerWorkspaceBinding = debuggerWorkspaceBinding;
77
89
  }
78
90
 
79
91
  /**
@@ -95,7 +107,8 @@ export class PerformanceTraceContext extends ConversationContext<AgentFocus> {
95
107
  return null;
96
108
  }
97
109
  return await SourceMapScopes.FunctionCodeResolver.getFunctionCodeFromLocation(
98
- target, url, line, column, {contextLength: 200, contextLineLength: 5, appendProfileData: true});
110
+ target, url, line, column, this.#debuggerWorkspaceBinding,
111
+ {contextLength: 200, contextLineLength: 5, appendProfileData: true});
99
112
  };
100
113
  return formatter;
101
114
  }
@@ -15,7 +15,7 @@ export class ComputedStyleModel extends Common.ObjectWrapper.ObjectWrapper<Event
15
15
  #node: SDK.DOMModel.DOMNode|null = null;
16
16
  #cssModel: SDK.CSSModel.CSSModel|null = null;
17
17
  private eventListeners: Common.EventTarget.EventDescriptor[] = [];
18
- private frameResizedTimer?: number;
18
+ private frameResizedTimer?: ReturnType<typeof setTimeout>;
19
19
  private computedStylePromise?: Promise<ComputedStyle|null>;
20
20
 
21
21
  constructor(node?: SDK.DOMModel.DOMNode|null) {
@@ -49,10 +49,9 @@ export class ComputedStyleModel extends Common.ObjectWrapper.ObjectWrapper<Event
49
49
  this.node = null;
50
50
  this.#cssModel = null;
51
51
  this.computedStylePromise = undefined;
52
- if (this.frameResizedTimer) {
52
+
53
53
  clearTimeout(this.frameResizedTimer);
54
54
  this.frameResizedTimer = undefined;
55
- }
56
55
  }
57
56
 
58
57
  private updateModel(cssModel: SDK.CSSModel.CSSModel|null): void {
@@ -111,14 +110,12 @@ export class ComputedStyleModel extends Common.ObjectWrapper.ObjectWrapper<Event
111
110
  private onFrameResized(): void {
112
111
  function refreshContents(this: ComputedStyleModel): void {
113
112
  this.onCSSModelChanged(null);
114
- delete this.frameResizedTimer;
113
+ this.frameResizedTimer = undefined;
115
114
  }
116
115
 
117
- if (this.frameResizedTimer) {
118
116
  clearTimeout(this.frameResizedTimer);
119
- }
120
117
 
121
- this.frameResizedTimer = window.setTimeout(refreshContents.bind(this), 100);
118
+ this.frameResizedTimer = globalThis.setTimeout(refreshContents.bind(this), 100);
122
119
  }
123
120
 
124
121
  private elementNode(): SDK.DOMModel.DOMNode|null {
@@ -311,6 +311,10 @@ export class CrUXManager extends Common.ObjectWrapper.ObjectWrapper<EventTypes>
311
311
  this.dispatchEventToListeners(Events.FIELD_DATA_CHANGED, this.#pageResult);
312
312
  }
313
313
 
314
+ setMainDocumentURL(url: string): void {
315
+ this.#mainDocumentUrl = url;
316
+ }
317
+
314
318
  #normalizeUrl(inputUrl: string): URL {
315
319
  const normalizedUrl = new URL(inputUrl);
316
320
  normalizedUrl.hash = '';
@@ -1554,7 +1554,7 @@ self.injectedExtensionAPI = function(
1554
1554
  }
1555
1555
 
1556
1556
  let keyboardEventRequestQueue: KeyboardEventInit&Array<{eventType: string}> = [];
1557
- let forwardTimer: number|null = null;
1557
+ let forwardTimer: ReturnType<typeof setTimeout>|undefined;
1558
1558
  function forwardKeyboardEvent(event: KeyboardEvent): void {
1559
1559
  // Check if the event should be forwarded.
1560
1560
  // This is a workaround for crbug.com/923338.
@@ -1601,12 +1601,12 @@ self.injectedExtensionAPI = function(
1601
1601
  };
1602
1602
  keyboardEventRequestQueue.push(requestPayload);
1603
1603
  if (!forwardTimer) {
1604
- forwardTimer = window.setTimeout(forwardEventQueue, 0);
1604
+ forwardTimer = globalThis.setTimeout(forwardEventQueue, 0);
1605
1605
  }
1606
1606
  }
1607
1607
 
1608
1608
  function forwardEventQueue(): void {
1609
- forwardTimer = null;
1609
+ forwardTimer = undefined;
1610
1610
  extensionServer.sendRequest(
1611
1611
  {command: PrivateAPI.Commands.ForwardKeyboardEvent, entries: keyboardEventRequestQueue});
1612
1612
  keyboardEventRequestQueue = [];
@@ -17,7 +17,7 @@ export class HeapSnapshotWorkerProxy extends Common.ObjectWrapper.ObjectWrapper<
17
17
  callbacks = new Map<number, (...args: any[]) => void>();
18
18
  readonly previousCallbacks = new Set<number>();
19
19
  readonly worker: PlatformApi.HostRuntime.Worker;
20
- interval?: number;
20
+ interval?: ReturnType<typeof setInterval>;
21
21
  readonly workerUrl?: string;
22
22
 
23
23
  constructor(
@@ -129,7 +129,7 @@ export class HeapSnapshotWorkerProxy extends Common.ObjectWrapper.ObjectWrapper<
129
129
  return;
130
130
  }
131
131
  this.checkLongRunningCalls();
132
- this.interval = window.setInterval(this.checkLongRunningCalls.bind(this), 300);
132
+ this.interval = globalThis.setInterval(this.checkLongRunningCalls.bind(this), 300);
133
133
  }
134
134
 
135
135
  checkLongRunningCalls(): void {
@@ -10,6 +10,7 @@ import * as Root from '../../core/root/root.js';
10
10
  import * as SDK from '../../core/sdk/sdk.js';
11
11
  import type * as Protocol from '../../generated/protocol.js';
12
12
  import * as EmulationModel from '../../models/emulation/emulation.js';
13
+ import * as CrUXManager from '../crux-manager/crux-manager.js';
13
14
 
14
15
  import * as Spec from './web-vitals-injected/spec/spec.js';
15
16
 
@@ -55,6 +56,7 @@ export class LiveMetrics extends Common.ObjectWrapper.ObjectWrapper<EventTypes>
55
56
  #interactions: InteractionMap = new Map();
56
57
  #interactionsByGroupId = new Map<Spec.InteractionEntryGroupId, Interaction[]>();
57
58
  #layoutShifts: LayoutShift[] = [];
59
+ #navigationType?: Spec.NavigationType;
58
60
  #lastEmulationChangeTime?: number;
59
61
  #mutex = new Common.Mutex.Mutex();
60
62
  readonly #targetManager: SDK.TargetManager.TargetManager;
@@ -69,6 +71,9 @@ export class LiveMetrics extends Common.ObjectWrapper.ObjectWrapper<EventTypes>
69
71
  this.#targetManager.addModelListener(SDK.ResourceTreeModel.ResourceTreeModel,
70
72
  SDK.ResourceTreeModel.Events.PrimaryPageChanged, this.#onPrimaryPageChanged,
71
73
  this);
74
+ Common.Settings.Settings.instance()
75
+ .moduleSetting('timeline-enable-soft-navigations')
76
+ .addChangeListener(this.#onSettingChanged, this);
72
77
  }
73
78
 
74
79
  #onPrimaryPageChanged(
@@ -106,6 +111,10 @@ export class LiveMetrics extends Common.ObjectWrapper.ObjectWrapper<EventTypes>
106
111
  return Root.DevToolsContext.globalInstance().get(LiveMetrics);
107
112
  }
108
113
 
114
+ get navigationType(): Spec.NavigationType|undefined {
115
+ return this.#navigationType;
116
+ }
117
+
109
118
  get lcpValue(): LcpValue|undefined {
110
119
  return this.#lcpValue;
111
120
  }
@@ -243,6 +252,7 @@ export class LiveMetrics extends Common.ObjectWrapper.ObjectWrapper<EventTypes>
243
252
  inp: this.#inpValue,
244
253
  interactions: this.#interactions,
245
254
  layoutShifts: this.#layoutShifts,
255
+ navigationType: this.#navigationType,
246
256
  });
247
257
  }
248
258
 
@@ -252,6 +262,7 @@ export class LiveMetrics extends Common.ObjectWrapper.ObjectWrapper<EventTypes>
252
262
  this.#inpValue = status.inp;
253
263
  this.#interactions = status.interactions;
254
264
  this.#layoutShifts = status.layoutShifts;
265
+ this.#navigationType = status.navigationType;
255
266
  this.#sendStatusUpdate();
256
267
  }
257
268
 
@@ -399,6 +410,11 @@ export class LiveMetrics extends Common.ObjectWrapper.ObjectWrapper<EventTypes>
399
410
  }
400
411
  case 'reset': {
401
412
  this.#clearMetrics();
413
+ this.#navigationType = webVitalsEvent.navigationType;
414
+ if (webVitalsEvent.url) {
415
+ CrUXManager.CrUXManager.instance().setMainDocumentURL(webVitalsEvent.url);
416
+ void CrUXManager.CrUXManager.instance().refresh();
417
+ }
402
418
  break;
403
419
  }
404
420
  }
@@ -413,6 +429,7 @@ export class LiveMetrics extends Common.ObjectWrapper.ObjectWrapper<EventTypes>
413
429
  this.#interactions.clear();
414
430
  this.#interactionsByGroupId.clear();
415
431
  this.#layoutShifts = [];
432
+ this.#navigationType = undefined;
416
433
  }
417
434
 
418
435
  #isPrimaryFrameExecutionContext(executionContextId: Protocol.Runtime.ExecutionContextId): boolean {
@@ -509,6 +526,46 @@ export class LiveMetrics extends Common.ObjectWrapper.ObjectWrapper<EventTypes>
509
526
  this.#target = undefined;
510
527
  }
511
528
 
529
+ async #injectScript(): Promise<void> {
530
+ // Extra check in case the target was removed while we were initializing.
531
+ // It's possible to be halfway-through enabling when the target is removed
532
+ // eg try loading 'devtools://devtools/bundled/devtools_app.html?ws=127.0.0.1:99/blah'
533
+ if (!this.#target) {
534
+ return;
535
+ }
536
+
537
+ // If DevTools is closed and reopened, the live metrics context from the previous
538
+ // session will persist. We should ensure any old live metrics contexts are killed
539
+ // before starting a new one.
540
+ await this.#killAllLiveMetricContexts();
541
+
542
+ // Remove any old instances of the script
543
+ if (this.#scriptIdentifier) {
544
+ await this.#target.pageAgent().invoke_removeScriptToEvaluateOnNewDocument({
545
+ identifier: this.#scriptIdentifier,
546
+ });
547
+ }
548
+
549
+ const softNavsSettingValue =
550
+ Common.Settings.Settings.instance().moduleSetting('timeline-enable-soft-navigations').get();
551
+ const source = `window.devToolsReportSoftNavs = ${softNavsSettingValue};\n` + await InjectedScript.get();
552
+
553
+ // Inject the script
554
+ const {identifier} = await this.#target.pageAgent().invoke_addScriptToEvaluateOnNewDocument({
555
+ source,
556
+ worldName: LIVE_METRICS_WORLD_NAME,
557
+ runImmediately: true,
558
+ });
559
+ this.#scriptIdentifier = identifier;
560
+ }
561
+
562
+ async #onSettingChanged(): Promise<void> {
563
+ if (!this.#target || !this.#enabled) {
564
+ return;
565
+ }
566
+ await this.#injectScript();
567
+ }
568
+
512
569
  async enable(): Promise<void> {
513
570
  if (this.#enabled) {
514
571
  return;
@@ -563,25 +620,7 @@ export class LiveMetrics extends Common.ObjectWrapper.ObjectWrapper<EventTypes>
563
620
  executionContextName: LIVE_METRICS_WORLD_NAME,
564
621
  });
565
622
 
566
- // If DevTools is closed and reopened, the live metrics context from the previous
567
- // session will persist. We should ensure any old live metrics contexts are killed
568
- // before starting a new one.
569
- await this.#killAllLiveMetricContexts();
570
-
571
- const source = await InjectedScript.get();
572
-
573
- // Extra check in case the target was removed while we were initializing.
574
- // It's possible to be halfway-through enabling when the target is removed
575
- // eg try loading 'devtools://devtools/bundled/devtools_app.html?ws=127.0.0.1:99/blah'
576
- if (!this.#target) {
577
- return;
578
- }
579
- const {identifier} = await this.#target?.pageAgent().invoke_addScriptToEvaluateOnNewDocument({
580
- source,
581
- worldName: LIVE_METRICS_WORLD_NAME,
582
- runImmediately: true,
583
- });
584
- this.#scriptIdentifier = identifier;
623
+ await this.#injectScript();
585
624
 
586
625
  this.#deviceModeModel?.addEventListener(
587
626
  EmulationModel.DeviceModeModel.Events.UPDATED, this.#onEmulationChanged, this);
@@ -687,6 +726,7 @@ export interface StatusEvent {
687
726
  inp?: InpValue;
688
727
  interactions: InteractionMap;
689
728
  layoutShifts: LayoutShift[];
729
+ navigationType?: Spec.NavigationType;
690
730
  }
691
731
 
692
732
  interface EventTypes {
@@ -1,5 +1,5 @@
1
1
  # Using the `web-vitals` library
2
2
 
3
- [`web-vitals`](https://github.com/GoogleChrome/web-vitals-extension) is a Google-maintained library that measures CWV metrics using their canonical definition. We use it to ensure we are using consistent implementations for measuring CWV across Google. It can also measure performance metrics of the current page *after* the they happen (See [`PerformanceObserver` buffered setting](https://developer.mozilla.org/en-US/docs/Web/API/PerformanceObserver/observe#buffered)). See [go/cpq:rpp-metric-observations](http://go/cpq:rpp-metric-observations) for more information.
3
+ [`web-vitals`](https://github.com/GoogleChrome/web-vitals) is a Google-maintained library that measures CWV metrics using their canonical definition. We use it to ensure we are using consistent implementations for measuring CWV across Google. It can also measure performance metrics of the current page *after* the they happen (See [`PerformanceObserver` buffered setting](https://developer.mozilla.org/en-US/docs/Web/API/PerformanceObserver/observe#buffered)). See [go/cpq:rpp-metric-observations](http://go/cpq:rpp-metric-observations) for more information.
4
4
 
5
5
  The web-vitals library needs to be run in the target page (although it can be used in an isolated execution context). We therefore need to create a JS binary that can be injected via `Page.evaluate`/`Page.addScriptToEvaluateOnNewDocument`. See `BUILD.gn` and `rollup.config.mjs` for how we bundle the JS binary.
@@ -2,7 +2,7 @@
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
- import type {INPAttribution} from '../../../../third_party/web-vitals/web-vitals.js';
5
+ import type {INPAttribution, Metric} from '../../../../third_party/web-vitals/web-vitals.js';
6
6
  import type * as Trace from '../../../trace/trace.js';
7
7
 
8
8
  export const EVENT_BINDING_NAME = '__chromium_devtools_metrics_reporter';
@@ -99,6 +99,7 @@ export interface InteractionEntryEvent {
99
99
  eventName: string;
100
100
  entryGroupId: InteractionEntryGroupId;
101
101
  startTime: number;
102
+ navigationId: number;
102
103
  nextPaintTime?: number;
103
104
  duration: Trace.Types.Timing.Milli;
104
105
  subparts: InpSubparts;
@@ -106,6 +107,8 @@ export interface InteractionEntryEvent {
106
107
  longAnimationFrameEntries: PerformanceLongAnimationFrameTimingJSON[];
107
108
  }
108
109
 
110
+ export type NavigationType = Metric['navigationType'];
111
+
109
112
  export interface LayoutShiftEvent {
110
113
  name: 'LayoutShift';
111
114
  score: number;
@@ -115,6 +118,8 @@ export interface LayoutShiftEvent {
115
118
 
116
119
  export interface ResetEvent {
117
120
  name: 'reset';
121
+ url?: string;
122
+ navigationType?: NavigationType;
118
123
  }
119
124
 
120
125
  export type WebVitalsEvent =
@@ -15,6 +15,7 @@ declare const window: Window&{
15
15
  getNodeForIndex: (index: number) => Node | undefined,
16
16
  [Spec.INTERNAL_KILL_SWITCH]: () => void,
17
17
  [Spec.EVENT_BINDING_NAME]: (payload: string) => void,
18
+ devToolsReportSoftNavs?: boolean,
18
19
  };
19
20
 
20
21
  const eventListenerCleanupController = new AbortController();
@@ -174,10 +175,15 @@ function initialize(): void {
174
175
  // callback before any others.
175
176
  WebVitals.onBFCacheRestore(() => {
176
177
  startedHidden = false;
177
- sendEventToDevTools({name: 'reset'});
178
+ sendEventToDevTools({name: 'reset', navigationType: 'back-forward-cache'});
178
179
  });
179
180
 
181
+ let lastLcpNavigationId: number|undefined;
180
182
  onLCP(metric => {
183
+ if (lastLcpNavigationId && metric.navigationId && metric.navigationId !== lastLcpNavigationId) {
184
+ sendEventToDevTools({name: 'reset', url: window.location.href, navigationType: metric.navigationType});
185
+ }
186
+ lastLcpNavigationId = metric.navigationId;
181
187
  const event: Spec.LcpChangeEvent = {
182
188
  name: 'LCP',
183
189
  value: metric.value as Trace.Types.Timing.Milli,
@@ -195,7 +201,7 @@ function initialize(): void {
195
201
  event.nodeIndex = establishNodeIndex(element);
196
202
  }
197
203
  sendEventToDevTools(event);
198
- }, {reportAllChanges: true});
204
+ }, {reportAllChanges: true, reportSoftNavs: window.devToolsReportSoftNavs});
199
205
 
200
206
  onCLS(metric => {
201
207
  const event: Spec.ClsChangeEvent = {
@@ -204,7 +210,7 @@ function initialize(): void {
204
210
  clusterShiftIds: metric.entries.map(Spec.getUniqueLayoutShiftId),
205
211
  };
206
212
  sendEventToDevTools(event);
207
- }, {reportAllChanges: true});
213
+ }, {reportAllChanges: true, reportSoftNavs: window.devToolsReportSoftNavs});
208
214
 
209
215
  function onEachInteraction(interaction: WebVitals.INPMetricWithAttribution): void {
210
216
  // Multiple `InteractionEntry` events can be emitted for the same `uniqueInteractionId`
@@ -219,6 +225,7 @@ function initialize(): void {
219
225
  presentationDelay: interaction.attribution.presentationDelay as Trace.Types.Timing.Milli,
220
226
  },
221
227
  startTime: interaction.entries[0].startTime,
228
+ navigationId: interaction.navigationId,
222
229
  entryGroupId: interaction.entries[0].interactionId as Spec.InteractionEntryGroupId,
223
230
  nextPaintTime: interaction.attribution.nextPaintTime,
224
231
  interactionType: interaction.attribution.interactionType,
@@ -252,6 +259,7 @@ function initialize(): void {
252
259
  reportAllChanges: true,
253
260
  durationThreshold: 0,
254
261
  includeProcessedEventEntries: false,
262
+ reportSoftNavs: window.devToolsReportSoftNavs,
255
263
  onEachInteraction,
256
264
  generateTarget(el) {
257
265
  if (el) {
@@ -84,7 +84,7 @@ export class LogManager implements SDK.TargetManager.SDKModelObserver<SDK.LogMod
84
84
  if (this.#targetManager.targetById(workerId)) {
85
85
  return;
86
86
  }
87
- window.setTimeout(() => {
87
+ globalThis.setTimeout(() => {
88
88
  if (!this.#targetManager.targetById(workerId)) {
89
89
  consoleModel?.addMessage(consoleMessage);
90
90
  }
@@ -318,7 +318,7 @@ export class FileSystem extends Workspace.Workspace.ProjectStore {
318
318
  this.addFile(filePaths[i]);
319
319
  }
320
320
  if (to < filePaths.length) {
321
- window.setTimeout(reportFileChunk.bind(this, to), 100);
321
+ globalThis.setTimeout(reportFileChunk.bind(this, to), 100);
322
322
  } else if (this.type() === 'filesystem') {
323
323
  Host.userMetrics.workspacesPopulated(performance.now() - startTime);
324
324
  }
@@ -216,9 +216,8 @@ function createFunctionCode(
216
216
  */
217
217
  export async function getFunctionCodeFromLocation(
218
218
  target: SDK.Target.Target, url: Platform.DevToolsPath.UrlString, line: number, column: number,
219
- options?: CreateFunctionCodeOptions,
220
- debuggerWorkspaceBinding =
221
- Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance()): Promise<FunctionCode|null> {
219
+ debuggerWorkspaceBinding: Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding,
220
+ options?: CreateFunctionCodeOptions): Promise<FunctionCode|null> {
222
221
  const debuggerModel = target.model(SDK.DebuggerModel.DebuggerModel);
223
222
  if (!debuggerModel) {
224
223
  throw new Error('missing debugger model');
@@ -246,7 +245,7 @@ export async function getFunctionCodeFromLocation(
246
245
  return null;
247
246
  }
248
247
 
249
- return await getFunctionCodeFromRawLocation(rawLocation, options, debuggerWorkspaceBinding);
248
+ return await getFunctionCodeFromRawLocation(rawLocation, debuggerWorkspaceBinding, options);
250
249
  }
251
250
 
252
251
  async function format(uiSourceCode: Workspace.UISourceCode.UISourceCode, content: string,
@@ -265,9 +264,9 @@ async function format(uiSourceCode: Workspace.UISourceCode.UISourceCode, content
265
264
  * Returns a {@link FunctionCode} for the given raw location.
266
265
  */
267
266
  export async function getFunctionCodeFromRawLocation(
268
- rawLocation: SDK.DebuggerModel.Location, options?: CreateFunctionCodeOptions,
269
- debuggerWorkspaceBinding =
270
- Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance()): Promise<FunctionCode|null> {
267
+ rawLocation: SDK.DebuggerModel.Location,
268
+ debuggerWorkspaceBinding: Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding,
269
+ options?: CreateFunctionCodeOptions): Promise<FunctionCode|null> {
271
270
  const functionBounds = await debuggerWorkspaceBinding.functionBoundsAtRawLocation(rawLocation);
272
271
  if (!functionBounds) {
273
272
  return null;
@@ -209,7 +209,7 @@ export class UISourceCodeDiff extends Common.ObjectWrapper.ObjectWrapper<UISourc
209
209
  readonly #networkPersistenceManager: Persistence.NetworkPersistenceManager.NetworkPersistenceManager;
210
210
  readonly #settings: Common.Settings.Settings;
211
211
  #requestDiffPromise: Promise<DiffResponse|null>|null = null;
212
- #pendingChanges: number|null = null;
212
+ #pendingChanges?: ReturnType<typeof setTimeout>;
213
213
  dispose = false;
214
214
  constructor(
215
215
  uiSourceCode: Workspace.UISourceCode.UISourceCode,
@@ -225,23 +225,22 @@ export class UISourceCodeDiff extends Common.ObjectWrapper.ObjectWrapper<UISourc
225
225
  }
226
226
 
227
227
  #uiSourceCodeChanged(): void {
228
- if (this.#pendingChanges) {
229
228
  clearTimeout(this.#pendingChanges);
230
- this.#pendingChanges = null;
231
- }
232
- this.#requestDiffPromise = null;
229
+ this.#pendingChanges = undefined;
233
230
 
234
- const content = this.#uiSourceCode.content();
235
- const delay = (!content || content.length < 65536) ? 0 : 200;
236
- this.#pendingChanges = window.setTimeout(emitDiffChanged.bind(this), delay);
231
+ this.#requestDiffPromise = null;
237
232
 
238
- function emitDiffChanged(this: UISourceCodeDiff): void {
239
- if (this.dispose) {
240
- return;
233
+ const content = this.#uiSourceCode.content();
234
+ const delay = (!content || content.length < 65536) ? 0 : 200;
235
+ this.#pendingChanges = globalThis.setTimeout(emitDiffChanged.bind(this), delay);
236
+
237
+ function emitDiffChanged(this: UISourceCodeDiff): void {
238
+ if (this.dispose) {
239
+ return;
240
+ }
241
+ this.dispatchEventToListeners(UISourceCodeDiffEvents.DIFF_CHANGED);
242
+ this.#pendingChanges = undefined;
241
243
  }
242
- this.dispatchEventToListeners(UISourceCodeDiffEvents.DIFF_CHANGED);
243
- this.#pendingChanges = null;
244
- }
245
244
  }
246
245
 
247
246
  requestDiff(): Promise<DiffResponse|null> {