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.
- package/front_end/core/host/AidaClient.ts +6 -4
- package/front_end/core/host/InspectorFrontendHostAPI.ts +1 -0
- package/front_end/core/host/InspectorFrontendHostStub.ts +3 -3
- package/front_end/core/host/UserMetrics.ts +49 -1
- package/front_end/core/sdk/CSSMatchedStyles.ts +2 -5
- package/front_end/core/sdk/CSSPropertyParserMatchers.ts +12 -0
- package/front_end/core/sdk/Connections.ts +1 -1
- package/front_end/core/sdk/DOMModel.test.api.ts +104 -0
- package/front_end/core/sdk/DOMModel.ts +23 -11
- package/front_end/core/sdk/DebuggerModel.ts +9 -9
- package/front_end/core/sdk/IsolateManager.ts +1 -1
- package/front_end/core/sdk/NetworkManager.ts +105 -4
- package/front_end/core/sdk/OverlayModel.ts +15 -14
- package/front_end/core/sdk/RemoteObject.ts +1 -1
- package/front_end/devtools_compatibility.js +1 -0
- package/front_end/entrypoints/main/MainImpl.ts +5 -2
- package/front_end/entrypoints/main/SimpleApp.ts +9 -3
- package/front_end/foundation/Universe.ts +6 -0
- package/front_end/models/ai_assistance/contexts/PerformanceTraceContext.ts +23 -10
- package/front_end/models/computed_style/ComputedStyleModel.ts +4 -7
- package/front_end/models/crux-manager/CrUXManager.ts +4 -0
- package/front_end/models/extensions/ExtensionAPI.ts +3 -3
- package/front_end/models/heap_snapshot/HeapSnapshotProxy.ts +2 -2
- package/front_end/models/live-metrics/LiveMetrics.ts +59 -19
- package/front_end/models/live-metrics/web-vitals-injected/README.md +1 -1
- package/front_end/models/live-metrics/web-vitals-injected/spec/spec.ts +6 -1
- package/front_end/models/live-metrics/web-vitals-injected/web-vitals-injected.ts +11 -3
- package/front_end/models/logs/LogManager.ts +1 -1
- package/front_end/models/persistence/FileSystemWorkspaceBinding.ts +1 -1
- package/front_end/models/source_map_scopes/FunctionCodeResolver.ts +6 -7
- package/front_end/models/workspace_diff/WorkspaceDiff.ts +13 -14
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +15 -7
- package/front_end/panels/application/BackgroundServiceView.ts +18 -20
- package/front_end/panels/autofill/AutofillView.ts +17 -17
- package/front_end/panels/autofill/autofillView.css +7 -8
- package/front_end/panels/browser_debugger/CategorizedBreakpointsSidebarPane.ts +31 -31
- package/front_end/panels/browser_debugger/DOMBreakpointsSidebarPane.ts +15 -15
- package/front_end/panels/browser_debugger/XHRBreakpointsSidebarPane.ts +12 -12
- package/front_end/panels/browser_debugger/browser_debugger-meta.ts +27 -27
- package/front_end/panels/common/AiCodeCompletionDisclaimer.ts +14 -6
- package/front_end/panels/common/AiCodeCompletionSummaryToolbar.ts +14 -6
- package/front_end/panels/common/AiCodeCompletionTeaser.ts +13 -6
- package/front_end/panels/common/GeminiRebrandPromoDialog.ts +1 -1
- package/front_end/panels/console/ConsoleInsightTeaser.ts +13 -6
- package/front_end/panels/console/ConsoleView.ts +6 -6
- package/front_end/panels/console_counters/WarningErrorCounter.ts +4 -4
- package/front_end/panels/coverage/CoverageModel.ts +11 -1
- package/front_end/panels/elements/StylePropertiesSection.ts +50 -17
- package/front_end/panels/elements/StylesAiCodeCompletionProvider.ts +18 -7
- package/front_end/panels/emulation/AdvancedApp.ts +18 -8
- package/front_end/panels/event_listeners/EventListenersView.ts +8 -8
- package/front_end/panels/explain/components/ConsoleInsight.ts +15 -7
- package/front_end/panels/network/NetworkLogView.ts +13 -14
- package/front_end/panels/screencast/ScreencastApp.ts +12 -6
- package/front_end/panels/screencast/ScreencastView.ts +37 -11
- package/front_end/panels/sensors/locationsSettingsTab.css +4 -0
- package/front_end/panels/settings/AISettingsTab.ts +13 -6
- package/front_end/panels/sources/DebuggerPlugin.ts +13 -2
- package/front_end/panels/timeline/components/LiveMetricsView.ts +14 -1
- package/front_end/panels/timeline/components/cpuThrottlingSelector.css +4 -0
- package/front_end/panels/timeline/components/liveMetricsView.css +28 -0
- package/front_end/panels/timeline/timeline-meta.ts +13 -0
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/ui/components/menus/menu.css +4 -0
- package/front_end/ui/components/menus/menuItem.css +4 -0
- package/front_end/ui/components/menus/selectMenu.css +4 -0
- package/front_end/ui/components/menus/selectMenuButton.css +4 -0
- package/front_end/ui/components/text_editor/AiCodeCompletionProvider.ts +21 -10
- package/front_end/ui/components/text_editor/AiCodeGenerationProvider.ts +22 -10
- package/front_end/ui/components/text_editor/config.ts +7 -0
- package/front_end/ui/legacy/AppProvider.ts +2 -1
- package/front_end/ui/legacy/RootView.ts +8 -1
- package/front_end/ui/legacy/UniverseRequestEvent.ts +19 -0
- package/front_end/ui/legacy/Widget.ts +49 -6
- package/front_end/ui/legacy/inspectorCommon.css +1 -1
- package/front_end/ui/legacy/legacy.ts +2 -0
- package/front_end/ui/visual_logging/KnownContextValues.ts +2 -0
- 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
|
-
|
|
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(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
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,
|
|
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?:
|
|
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
|
-
|
|
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
|
-
|
|
113
|
+
this.frameResizedTimer = undefined;
|
|
115
114
|
}
|
|
116
115
|
|
|
117
|
-
if (this.frameResizedTimer) {
|
|
118
116
|
clearTimeout(this.frameResizedTimer);
|
|
119
|
-
}
|
|
120
117
|
|
|
121
|
-
|
|
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:
|
|
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 =
|
|
1604
|
+
forwardTimer = globalThis.setTimeout(forwardEventQueue, 0);
|
|
1605
1605
|
}
|
|
1606
1606
|
}
|
|
1607
1607
|
|
|
1608
1608
|
function forwardEventQueue(): void {
|
|
1609
|
-
forwardTimer =
|
|
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?:
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
220
|
-
|
|
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,
|
|
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,
|
|
269
|
-
debuggerWorkspaceBinding
|
|
270
|
-
|
|
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
|
|
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 =
|
|
231
|
-
}
|
|
232
|
-
this.#requestDiffPromise = null;
|
|
229
|
+
this.#pendingChanges = undefined;
|
|
233
230
|
|
|
234
|
-
|
|
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
|
-
|
|
239
|
-
|
|
240
|
-
|
|
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> {
|