chrome-ai-bridge 1.0.2 → 1.0.4
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/build/node_modules/chrome-devtools-frontend/front_end/core/common/Base64.js +20 -2
- package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Gzip.js +11 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Object.js +6 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/core/common/ParsedURL.js +3 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/core/common/ResourceType.js +6 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Settings.js +18 -8
- package/build/node_modules/chrome-devtools-frontend/front_end/core/host/InspectorFrontendHostStub.js +3 -3
- package/build/node_modules/chrome-devtools-frontend/front_end/core/host/ResourceLoader.js +1 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/core/host/UserMetrics.js +17 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/core/platform/ArrayUtilities.js +10 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/core/platform/StringUtilities.js +63 -12
- package/build/node_modules/chrome-devtools-frontend/front_end/core/protocol_client/CDPConnection.js +1 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/core/protocol_client/InspectorBackend.js +4 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSMatchedStyles.js +44 -9
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSMetadata.js +6 -6
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSModel.js +1 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/DOMModel.js +169 -12
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/DebuggerModel.js +2 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/IsolateManager.js +6 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/NetworkManager.js +18 -4
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/NetworkRequest.js +7 -21
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/OverlayModel.js +17 -5
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/RehydratingConnection.js +5 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/ResourceTreeModel.js +8 -5
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/SourceMap.js +14 -2
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/SourceMapManager.js +1 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/SourceMapScopesInfo.js +11 -4
- package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/Target.js +3 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/generated/ARIAProperties.js +1 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/generated/Deprecation.js +1 -16
- package/build/node_modules/chrome-devtools-frontend/front_end/generated/InspectorBackendCommands.js +35 -14
- package/build/node_modules/chrome-devtools-frontend/front_end/generated/SupportedCSSProperties.js +197 -101
- package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/NetworkRequestFormatter.js +2 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.js +10 -16
- package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.js +97 -26
- package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/performance/AICallTree.js +35 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/CompilerScriptMapping.js +5 -3
- package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/DebuggerWorkspaceBinding.js +7 -3
- package/build/node_modules/chrome-devtools-frontend/front_end/models/emulation/DeviceModeModel.js +1 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/models/emulation/EmulatedDevices.js +14 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/formatter/FormatterWorkerPool.js +8 -5
- package/build/node_modules/chrome-devtools-frontend/front_end/models/stack_trace/StackTraceImpl.js +70 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/models/stack_trace/StackTraceModel.js +82 -30
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/EventsSerializer.js +7 -2
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/LanternComputationData.js +2 -2
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/Processor.js +18 -19
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/Styles.js +12 -4
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/extras/Initiators.js +46 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/extras/TraceTree.js +4 -3
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/extras/extras.js +1 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/LargestImagePaintHandler.js +2 -2
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/LayoutShiftsHandler.js +1 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/MetaHandler.js +6 -0
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/NetworkRequestsHandler.js +10 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/PageLoadMetricsHandler.js +44 -27
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/helpers/Timing.js +9 -2
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/Common.js +1 -6
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/LCPBreakdown.js +2 -2
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/LCPDiscovery.js +2 -4
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/NetworkDependencyTree.js +3 -2
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/RenderBlocking.js +1 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/types/TraceEvents.js +30 -11
- package/build/node_modules/chrome-devtools-frontend/front_end/third_party/source-map-scopes-codec/package/src/decode/decode.js +28 -13
- package/build/node_modules/chrome-devtools-frontend/front_end/third_party/source-map-scopes-codec/package/src/encode/encoder.js +1 -1
- package/build/node_modules/chrome-devtools-frontend/front_end/third_party/source-map-scopes-codec/package/src/scopes.js +4 -0
- package/build/src/tools/chatgpt-web.js +102 -64
- package/build/src/tools/gemini-web.js +43 -16
- package/build/src/tools/pages.js +0 -1
- package/package.json +1 -1
package/build/node_modules/chrome-devtools-frontend/front_end/models/stack_trace/StackTraceImpl.js
CHANGED
|
@@ -9,11 +9,13 @@ export class StackTraceImpl extends Common.ObjectWrapper.ObjectWrapper {
|
|
|
9
9
|
super();
|
|
10
10
|
this.syncFragment = syncFragment;
|
|
11
11
|
this.asyncFragments = asyncFragments;
|
|
12
|
-
syncFragment.
|
|
12
|
+
const fragment = syncFragment instanceof DebuggableFragmentImpl ? syncFragment.fragment : syncFragment;
|
|
13
|
+
fragment.stackTraces.add(this);
|
|
13
14
|
this.asyncFragments.forEach(asyncFragment => asyncFragment.fragment.stackTraces.add(this));
|
|
14
15
|
}
|
|
15
16
|
}
|
|
16
17
|
export class FragmentImpl {
|
|
18
|
+
static EMPTY_FRAGMENT = new FragmentImpl();
|
|
17
19
|
node;
|
|
18
20
|
stackTraces = new Set();
|
|
19
21
|
/**
|
|
@@ -31,6 +33,9 @@ export class FragmentImpl {
|
|
|
31
33
|
this.node = node;
|
|
32
34
|
}
|
|
33
35
|
get frames() {
|
|
36
|
+
if (!this.node) {
|
|
37
|
+
return [];
|
|
38
|
+
}
|
|
34
39
|
const frames = [];
|
|
35
40
|
for (const node of this.node.getCallStack()) {
|
|
36
41
|
frames.push(...node.frames);
|
|
@@ -65,3 +70,67 @@ export class FrameImpl {
|
|
|
65
70
|
this.missingDebugInfo = missingDebugInfo;
|
|
66
71
|
}
|
|
67
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* A DebuggableFragmentImpl wraps an existing FragmentImpl. This is important: We can pause at the
|
|
75
|
+
* same location multiple times and the paused information changes each and everytime while the underlying
|
|
76
|
+
* FragmentImpl will stay the same.
|
|
77
|
+
*/
|
|
78
|
+
export class DebuggableFragmentImpl {
|
|
79
|
+
fragment;
|
|
80
|
+
callFrames;
|
|
81
|
+
constructor(fragment, callFrames) {
|
|
82
|
+
this.fragment = fragment;
|
|
83
|
+
this.callFrames = callFrames;
|
|
84
|
+
}
|
|
85
|
+
get frames() {
|
|
86
|
+
if (!this.fragment.node) {
|
|
87
|
+
return [];
|
|
88
|
+
}
|
|
89
|
+
const frames = [];
|
|
90
|
+
let index = 0;
|
|
91
|
+
for (const node of this.fragment.node.getCallStack()) {
|
|
92
|
+
for (const [inlineIdx, frame] of node.frames.entries()) {
|
|
93
|
+
// Create virtual frames for inlined frames.
|
|
94
|
+
const sdkFrame = inlineIdx === 0 ? this.callFrames[index] :
|
|
95
|
+
this.callFrames[index].createVirtualCallFrame(inlineIdx, frame.name ?? '');
|
|
96
|
+
frames.push(new DebuggableFrameImpl(frame, sdkFrame));
|
|
97
|
+
}
|
|
98
|
+
index++;
|
|
99
|
+
}
|
|
100
|
+
return frames;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* A DebuggableFrameImpl wraps an existing FrameImpl. This is important: We can pause at the
|
|
105
|
+
* same location multiple times and the paused information changes each and everytime while the underlying
|
|
106
|
+
* FrameImpl will stay the same.
|
|
107
|
+
*/
|
|
108
|
+
export class DebuggableFrameImpl {
|
|
109
|
+
#frame;
|
|
110
|
+
#sdkFrame;
|
|
111
|
+
constructor(frame, sdkFrame) {
|
|
112
|
+
this.#frame = frame;
|
|
113
|
+
this.#sdkFrame = sdkFrame;
|
|
114
|
+
}
|
|
115
|
+
get url() {
|
|
116
|
+
return this.#frame.url;
|
|
117
|
+
}
|
|
118
|
+
get uiSourceCode() {
|
|
119
|
+
return this.#frame.uiSourceCode;
|
|
120
|
+
}
|
|
121
|
+
get name() {
|
|
122
|
+
return this.#frame.name;
|
|
123
|
+
}
|
|
124
|
+
get line() {
|
|
125
|
+
return this.#frame.line;
|
|
126
|
+
}
|
|
127
|
+
get column() {
|
|
128
|
+
return this.#frame.column;
|
|
129
|
+
}
|
|
130
|
+
get missingDebugInfo() {
|
|
131
|
+
return this.#frame.missingDebugInfo;
|
|
132
|
+
}
|
|
133
|
+
get sdkFrame() {
|
|
134
|
+
return this.#sdkFrame;
|
|
135
|
+
}
|
|
136
|
+
}
|
package/build/node_modules/chrome-devtools-frontend/front_end/models/stack_trace/StackTraceModel.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
// Copyright 2025 The Chromium Authors
|
|
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
|
+
var _a;
|
|
5
|
+
import * as Common from '../../core/common/common.js';
|
|
4
6
|
import * as SDK from '../../core/sdk/sdk.js';
|
|
5
7
|
// eslint-disable-next-line @devtools/es-modules-import
|
|
6
8
|
import * as StackTrace from './stack_trace.js';
|
|
7
|
-
import { AsyncFragmentImpl, FragmentImpl, FrameImpl, StackTraceImpl } from './StackTraceImpl.js';
|
|
9
|
+
import { AsyncFragmentImpl, DebuggableFragmentImpl, FragmentImpl, FrameImpl, StackTraceImpl } from './StackTraceImpl.js';
|
|
8
10
|
import { Trie } from './Trie.js';
|
|
9
11
|
/**
|
|
10
12
|
* The {@link StackTraceModel} is a thin wrapper around a fragment trie.
|
|
@@ -13,54 +15,103 @@ import { Trie } from './Trie.js';
|
|
|
13
15
|
*/
|
|
14
16
|
export class StackTraceModel extends SDK.SDKModel.SDKModel {
|
|
15
17
|
#trie = new Trie();
|
|
18
|
+
#mutex = new Common.Mutex.Mutex();
|
|
16
19
|
/** @returns the {@link StackTraceModel} for the target, or the model for the primaryPageTarget when passing null/undefined */
|
|
17
20
|
static #modelForTarget(target) {
|
|
18
|
-
const model = (target ?? SDK.TargetManager.TargetManager.instance().primaryPageTarget())?.model(
|
|
21
|
+
const model = (target ?? SDK.TargetManager.TargetManager.instance().primaryPageTarget())?.model(_a);
|
|
19
22
|
if (!model) {
|
|
20
23
|
throw new Error('Unable to find StackTraceModel');
|
|
21
24
|
}
|
|
22
25
|
return model;
|
|
23
26
|
}
|
|
24
27
|
async createFromProtocolRuntime(stackTrace, rawFramesToUIFrames) {
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
const [syncFragment, asyncFragments] = await Promise.all([
|
|
29
|
+
this.#createFragment(stackTrace.callFrames, rawFramesToUIFrames),
|
|
30
|
+
this.#createAsyncFragments(stackTrace, rawFramesToUIFrames),
|
|
31
|
+
]);
|
|
32
|
+
return new StackTraceImpl(syncFragment, asyncFragments);
|
|
33
|
+
}
|
|
34
|
+
async createFromDebuggerPaused(pausedDetails, rawFramesToUIFrames) {
|
|
35
|
+
const [syncFragment, asyncFragments] = await Promise.all([
|
|
36
|
+
this.#createDebuggableFragment(pausedDetails, rawFramesToUIFrames),
|
|
37
|
+
this.#createAsyncFragments(pausedDetails, rawFramesToUIFrames),
|
|
38
|
+
]);
|
|
39
|
+
return new StackTraceImpl(syncFragment, asyncFragments);
|
|
40
|
+
}
|
|
41
|
+
/** Trigger re-translation of all fragments with the provide script in their call stack */
|
|
42
|
+
async scriptInfoChanged(script, translateRawFrames) {
|
|
43
|
+
const release = await this.#mutex.acquire();
|
|
44
|
+
try {
|
|
45
|
+
const translatePromises = [];
|
|
46
|
+
let stackTracesToUpdate = new Set();
|
|
47
|
+
for (const fragment of this.#affectedFragments(script)) {
|
|
48
|
+
// We trigger re-translation only for fragments of leaf-nodes. Any fragment along the ancestor-chain
|
|
49
|
+
// is re-translated as a side-effect.
|
|
50
|
+
// We just need to remember the stack traces of the skipped over fragments, so we can send the
|
|
51
|
+
// UPDATED event also to them.
|
|
52
|
+
if (fragment.node?.children.length === 0) {
|
|
53
|
+
translatePromises.push(this.#translateFragment(fragment, translateRawFrames));
|
|
54
|
+
}
|
|
55
|
+
stackTracesToUpdate = stackTracesToUpdate.union(fragment.stackTraces);
|
|
56
|
+
}
|
|
57
|
+
await Promise.all(translatePromises);
|
|
58
|
+
for (const stackTrace of stackTracesToUpdate) {
|
|
59
|
+
stackTrace.dispatchEventToListeners("UPDATED" /* StackTrace.StackTrace.Events.UPDATED */);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
finally {
|
|
63
|
+
release();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
async #createDebuggableFragment(pausedDetails, rawFramesToUIFrames) {
|
|
67
|
+
const fragment = await this.#createFragment(pausedDetails.callFrames.map(frame => ({
|
|
68
|
+
scriptId: frame.script.scriptId,
|
|
69
|
+
url: frame.script.sourceURL,
|
|
70
|
+
functionName: frame.functionName,
|
|
71
|
+
lineNumber: frame.location().lineNumber,
|
|
72
|
+
columnNumber: frame.location().columnNumber,
|
|
73
|
+
})), rawFramesToUIFrames);
|
|
74
|
+
return new DebuggableFragmentImpl(fragment, pausedDetails.callFrames);
|
|
75
|
+
}
|
|
76
|
+
async #createAsyncFragments(stackTraceOrPausedEvent, rawFramesToUIFrames) {
|
|
28
77
|
const asyncFragments = [];
|
|
29
78
|
const debuggerModel = this.target().model(SDK.DebuggerModel.DebuggerModel);
|
|
30
79
|
if (debuggerModel) {
|
|
31
|
-
for await (const { stackTrace: asyncStackTrace, target } of debuggerModel.iterateAsyncParents(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
80
|
+
for await (const { stackTrace: asyncStackTrace, target } of debuggerModel.iterateAsyncParents(stackTraceOrPausedEvent)) {
|
|
81
|
+
if (asyncStackTrace.callFrames.length === 0) {
|
|
82
|
+
// Skip empty async fragments, they don't add value.
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
const model = _a.#modelForTarget(target);
|
|
86
|
+
const asyncFragmentPromise = model.#createFragment(asyncStackTrace.callFrames, rawFramesToUIFrames)
|
|
87
|
+
.then(fragment => new AsyncFragmentImpl(asyncStackTrace.description ?? '', fragment));
|
|
88
|
+
asyncFragments.push(asyncFragmentPromise);
|
|
36
89
|
}
|
|
37
90
|
}
|
|
38
|
-
await Promise.all(
|
|
39
|
-
return new StackTraceImpl(fragment, asyncFragments);
|
|
91
|
+
return await Promise.all(asyncFragments);
|
|
40
92
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if (
|
|
51
|
-
|
|
93
|
+
async #createFragment(frames, rawFramesToUIFrames) {
|
|
94
|
+
if (frames.length === 0) {
|
|
95
|
+
return FragmentImpl.EMPTY_FRAGMENT;
|
|
96
|
+
}
|
|
97
|
+
const release = await this.#mutex.acquire();
|
|
98
|
+
try {
|
|
99
|
+
const node = this.#trie.insert(frames);
|
|
100
|
+
const requiresTranslation = !Boolean(node.fragment);
|
|
101
|
+
const fragment = FragmentImpl.getOrCreate(node);
|
|
102
|
+
if (requiresTranslation) {
|
|
103
|
+
await this.#translateFragment(fragment, rawFramesToUIFrames);
|
|
52
104
|
}
|
|
53
|
-
|
|
105
|
+
return fragment;
|
|
54
106
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
stackTrace.dispatchEventToListeners("UPDATED" /* StackTrace.StackTrace.Events.UPDATED */);
|
|
107
|
+
finally {
|
|
108
|
+
release();
|
|
58
109
|
}
|
|
59
110
|
}
|
|
60
|
-
#createFragment(frames) {
|
|
61
|
-
return FragmentImpl.getOrCreate(this.#trie.insert(frames));
|
|
62
|
-
}
|
|
63
111
|
async #translateFragment(fragment, rawFramesToUIFrames) {
|
|
112
|
+
if (!fragment.node) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
64
115
|
const rawFrames = fragment.node.getCallStack().map(node => node.rawFrame).toArray();
|
|
65
116
|
const uiFrames = await rawFramesToUIFrames(rawFrames, this.target());
|
|
66
117
|
console.assert(rawFrames.length === uiFrames.length, 'Broken rawFramesToUIFrames implementation');
|
|
@@ -94,4 +145,5 @@ export class StackTraceModel extends SDK.SDKModel.SDKModel {
|
|
|
94
145
|
return fragments;
|
|
95
146
|
}
|
|
96
147
|
}
|
|
148
|
+
_a = StackTraceModel;
|
|
97
149
|
SDK.SDKModel.SDKModel.register(StackTraceModel, { capabilities: 0 /* SDK.Target.Capability.NONE */, autostart: false });
|
package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/EventsSerializer.js
CHANGED
|
@@ -13,9 +13,14 @@ export class EventsSerializer {
|
|
|
13
13
|
return `${"l" /* Types.File.EventKeyType.LEGACY_TIMELINE_FRAME */}-${event.index}`;
|
|
14
14
|
}
|
|
15
15
|
const rawEvents = Helpers.SyntheticEvents.SyntheticEventsManager.getActiveManager().getRawTraceEvents();
|
|
16
|
+
const isSynthetic = Types.Events.isSyntheticBased(event);
|
|
17
|
+
const index = rawEvents.indexOf(isSynthetic ? event.rawSourceEvent : event);
|
|
18
|
+
if (index === -1) {
|
|
19
|
+
throw new Error(`Unknown trace event: ${event.name}`);
|
|
20
|
+
}
|
|
16
21
|
const key = Types.Events.isSyntheticBased(event) ?
|
|
17
|
-
`${"s" /* Types.File.EventKeyType.SYNTHETIC_EVENT */}-${
|
|
18
|
-
`${"r" /* Types.File.EventKeyType.RAW_EVENT */}-${
|
|
22
|
+
`${"s" /* Types.File.EventKeyType.SYNTHETIC_EVENT */}-${index}` :
|
|
23
|
+
`${"r" /* Types.File.EventKeyType.RAW_EVENT */}-${index}`;
|
|
19
24
|
if (key.length < 3) {
|
|
20
25
|
return null;
|
|
21
26
|
}
|
package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/LanternComputationData.js
CHANGED
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
// found in the LICENSE file.
|
|
4
4
|
import * as Handlers from './handlers/handlers.js';
|
|
5
5
|
import * as Lantern from './lantern/lantern.js';
|
|
6
|
-
function createProcessedNavigation(data, frameId,
|
|
6
|
+
function createProcessedNavigation(data, frameId, navigation) {
|
|
7
7
|
const scoresByNav = data.PageLoadMetrics.metricScoresByFrameId.get(frameId);
|
|
8
8
|
if (!scoresByNav) {
|
|
9
9
|
throw new Lantern.Core.LanternError('missing metric scores for frame');
|
|
10
10
|
}
|
|
11
|
-
const scores = scoresByNav.get(
|
|
11
|
+
const scores = scoresByNav.get(navigation);
|
|
12
12
|
if (!scores) {
|
|
13
13
|
throw new Lantern.Core.LanternError('missing metric scores for specified navigation');
|
|
14
14
|
}
|
|
@@ -212,7 +212,7 @@ export class TraceProcessor extends EventTarget {
|
|
|
212
212
|
}
|
|
213
213
|
return this.#insights;
|
|
214
214
|
}
|
|
215
|
-
#createLanternContext(data, traceEvents, frameId,
|
|
215
|
+
#createLanternContext(data, traceEvents, frameId, navigation, options) {
|
|
216
216
|
// Check for required handlers.
|
|
217
217
|
if (!data.NetworkRequests || !data.Workers || !data.PageLoadMetrics) {
|
|
218
218
|
return;
|
|
@@ -221,7 +221,7 @@ export class TraceProcessor extends EventTarget {
|
|
|
221
221
|
throw new Lantern.Core.LanternError('No network requests found in trace');
|
|
222
222
|
}
|
|
223
223
|
const navStarts = data.Meta.navigationsByFrameId.get(frameId);
|
|
224
|
-
const navStartIndex = navStarts?.findIndex(n => n
|
|
224
|
+
const navStartIndex = navStarts?.findIndex(n => n === navigation);
|
|
225
225
|
if (!navStarts || navStartIndex === undefined || navStartIndex === -1) {
|
|
226
226
|
throw new Lantern.Core.LanternError('Could not find navigation start');
|
|
227
227
|
}
|
|
@@ -235,7 +235,7 @@ export class TraceProcessor extends EventTarget {
|
|
|
235
235
|
};
|
|
236
236
|
const requests = LanternComputationData.createNetworkRequests(trace, data, startTime, endTime);
|
|
237
237
|
const graph = LanternComputationData.createGraph(requests, trace, data);
|
|
238
|
-
const processedNavigation = LanternComputationData.createProcessedNavigation(data, frameId,
|
|
238
|
+
const processedNavigation = LanternComputationData.createProcessedNavigation(data, frameId, navigation);
|
|
239
239
|
const networkAnalysis = Lantern.Core.NetworkAnalyzer.analyze(requests);
|
|
240
240
|
if (!networkAnalysis) {
|
|
241
241
|
return;
|
|
@@ -304,10 +304,10 @@ export class TraceProcessor extends EventTarget {
|
|
|
304
304
|
const observedInpScore = Insights.Common.evaluateINPMetricScore(observedInp);
|
|
305
305
|
const observedClsScore = Insights.Common.evaluateCLSMetricScore(observedCls);
|
|
306
306
|
const insightToSortingRank = new Map();
|
|
307
|
-
for (const [name,
|
|
308
|
-
const lcp =
|
|
309
|
-
const inp =
|
|
310
|
-
const cls =
|
|
307
|
+
for (const [name, insight] of Object.entries(insightSet.model)) {
|
|
308
|
+
const lcp = insight.metricSavings?.LCP ?? 0;
|
|
309
|
+
const inp = insight.metricSavings?.INP ?? 0;
|
|
310
|
+
const cls = insight.metricSavings?.CLS ?? 0;
|
|
311
311
|
const lcpPostSavings = observedLcp !== undefined ? Math.max(0, observedLcp - lcp) : undefined;
|
|
312
312
|
const inpPostSavings = Math.max(0, observedInp - inp);
|
|
313
313
|
const clsPostSavings = Math.max(0, observedCls - cls);
|
|
@@ -365,28 +365,28 @@ export class TraceProcessor extends EventTarget {
|
|
|
365
365
|
urlString = data.Meta.finalDisplayUrlByNavigationId.get('') ?? data.Meta.mainFrameURL;
|
|
366
366
|
}
|
|
367
367
|
const insightSetModel = {};
|
|
368
|
+
const insightSetModelErrors = {};
|
|
368
369
|
for (const [name, insight] of Object.entries(_a.getInsightRunners())) {
|
|
369
|
-
let model;
|
|
370
370
|
try {
|
|
371
371
|
logger?.start(`insights:${name}`);
|
|
372
|
-
model = insight.generateInsight(data, context);
|
|
372
|
+
const model = insight.generateInsight(data, context);
|
|
373
373
|
model.frameId = context.frameId;
|
|
374
374
|
const navId = context.navigation?.args.data?.navigationId;
|
|
375
375
|
if (navId) {
|
|
376
|
-
model.
|
|
376
|
+
model.navigation = context.navigation;
|
|
377
377
|
}
|
|
378
378
|
model.createOverlays = () => {
|
|
379
379
|
// @ts-expect-error: model is a union of all possible insight model types.
|
|
380
380
|
return insight.createOverlays(model);
|
|
381
381
|
};
|
|
382
|
+
Object.assign(insightSetModel, { [name]: model });
|
|
382
383
|
}
|
|
383
384
|
catch (err) {
|
|
384
|
-
|
|
385
|
+
Object.assign(insightSetModelErrors, { [name]: err });
|
|
385
386
|
}
|
|
386
387
|
finally {
|
|
387
388
|
logger?.end(`insights:${name}`);
|
|
388
389
|
}
|
|
389
|
-
Object.assign(insightSetModel, { [name]: model });
|
|
390
390
|
}
|
|
391
391
|
// We may choose to exclude the insightSet if it's trivial. Trivial means:
|
|
392
392
|
// 1. There's no navigation (it's an initial trace period)
|
|
@@ -396,12 +396,10 @@ export class TraceProcessor extends EventTarget {
|
|
|
396
396
|
// Generally, these cases are the short time ranges before a page reload starts.
|
|
397
397
|
const isNavigation = id === Types.Events.NO_NAVIGATION;
|
|
398
398
|
const trivialThreshold = Helpers.Timing.milliToMicro(Types.Timing.Milli(5000));
|
|
399
|
-
const everyInsightPasses = Object.values(insightSetModel)
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
const
|
|
403
|
-
const noInp = !insightSetModel.INPBreakdown.longestInteractionEvent;
|
|
404
|
-
const noLayoutShifts = insightSetModel.CLSCulprits.shifts?.size === 0;
|
|
399
|
+
const everyInsightPasses = Object.values(insightSetModel).every(model => model && model.state === 'pass');
|
|
400
|
+
const noLcp = !insightSetModel.LCPBreakdown?.lcpEvent;
|
|
401
|
+
const noInp = !insightSetModel.INPBreakdown?.longestInteractionEvent;
|
|
402
|
+
const noLayoutShifts = insightSetModel.CLSCulprits?.shifts?.size === 0;
|
|
405
403
|
const shouldExclude = isNavigation && context.bounds.range < trivialThreshold && everyInsightPasses && noLcp &&
|
|
406
404
|
noInp && noLayoutShifts;
|
|
407
405
|
if (shouldExclude) {
|
|
@@ -423,6 +421,7 @@ export class TraceProcessor extends EventTarget {
|
|
|
423
421
|
frameId: context.frameId,
|
|
424
422
|
bounds: context.bounds,
|
|
425
423
|
model: insightSetModel,
|
|
424
|
+
modelErrors: insightSetModelErrors,
|
|
426
425
|
};
|
|
427
426
|
this.#insights.set(insightSet.id, insightSet);
|
|
428
427
|
this.sortInsightSet(insightSet, context.options.metadata ?? null);
|
|
@@ -474,7 +473,7 @@ export class TraceProcessor extends EventTarget {
|
|
|
474
473
|
let lantern;
|
|
475
474
|
try {
|
|
476
475
|
options.logger?.start('insights:createLanternContext');
|
|
477
|
-
lantern = this.#createLanternContext(data, traceEvents, frameId,
|
|
476
|
+
lantern = this.#createLanternContext(data, traceEvents, frameId, navigation, options);
|
|
478
477
|
}
|
|
479
478
|
catch (e) {
|
|
480
479
|
// Handle Lantern errors gracefully
|
|
@@ -307,6 +307,10 @@ const UIStrings = {
|
|
|
307
307
|
* @description Text in Timeline UIUtils of the Performance panel
|
|
308
308
|
*/
|
|
309
309
|
largestContentfulPaint: 'Largest Contentful Paint',
|
|
310
|
+
/**
|
|
311
|
+
* @description Text in Timeline UIUtils of the Performance panel
|
|
312
|
+
*/
|
|
313
|
+
softLargestContentfulPaint: 'Soft Largest Contentful Paint',
|
|
310
314
|
/**
|
|
311
315
|
* @description Text for timestamps of items
|
|
312
316
|
*/
|
|
@@ -684,6 +688,7 @@ export function maybeInitSylesMap() {
|
|
|
684
688
|
["firstPaint" /* Types.Events.Name.MARK_FIRST_PAINT */]: new TimelineRecordStyle(i18nString(UIStrings.firstPaint), defaultCategoryStyles.painting, true),
|
|
685
689
|
["firstContentfulPaint" /* Types.Events.Name.MARK_FCP */]: new TimelineRecordStyle(i18nString(UIStrings.firstContentfulPaint), defaultCategoryStyles.rendering, true),
|
|
686
690
|
["largestContentfulPaint::Candidate" /* Types.Events.Name.MARK_LCP_CANDIDATE */]: new TimelineRecordStyle(i18nString(UIStrings.largestContentfulPaint), defaultCategoryStyles.rendering, true),
|
|
691
|
+
["largestContentfulPaint::CandidateForSoftNavigation" /* Types.Events.Name.MARK_LCP_CANDIDATE_FOR_SOFT_NAVIGATION */]: new TimelineRecordStyle(i18nString(UIStrings.softLargestContentfulPaint), defaultCategoryStyles.rendering, true),
|
|
687
692
|
["TimeStamp" /* Types.Events.Name.TIME_STAMP */]: new TimelineRecordStyle(i18nString(UIStrings.timestamp), defaultCategoryStyles.scripting),
|
|
688
693
|
["ConsoleTime" /* Types.Events.Name.CONSOLE_TIME */]: new TimelineRecordStyle(i18nString(UIStrings.consoleTime), defaultCategoryStyles.scripting),
|
|
689
694
|
["UserTiming" /* Types.Events.Name.USER_TIMING */]: new TimelineRecordStyle(i18nString(UIStrings.userTiming), defaultCategoryStyles.scripting),
|
|
@@ -795,13 +800,16 @@ export function markerDetailsForEvent(event) {
|
|
|
795
800
|
color = 'var(--sys-color-green-bright)';
|
|
796
801
|
title = "FCP" /* Handlers.ModelHandlers.PageLoadMetrics.MetricName.FCP */;
|
|
797
802
|
}
|
|
798
|
-
if (Types.Events.
|
|
803
|
+
if (Types.Events.isAnyLargestContentfulPaintCandidate(event)) {
|
|
799
804
|
color = 'var(--sys-color-green)';
|
|
800
|
-
title =
|
|
805
|
+
title = Types.Events.isSoftLargestContentfulPaintCandidate(event) ?
|
|
806
|
+
"LCP*" /* Handlers.ModelHandlers.PageLoadMetrics.MetricName.SOFT_LCP */ :
|
|
807
|
+
"LCP" /* Handlers.ModelHandlers.PageLoadMetrics.MetricName.LCP */;
|
|
801
808
|
}
|
|
802
|
-
if (Types.Events.isNavigationStart(event)) {
|
|
809
|
+
if (Types.Events.isNavigationStart(event) || Types.Events.isSoftNavigationStart(event)) {
|
|
803
810
|
color = 'var(--color-text-primary)';
|
|
804
|
-
title = "Nav" /* Handlers.ModelHandlers.PageLoadMetrics.MetricName.
|
|
811
|
+
title = Types.Events.isSoftNavigationStart(event) ? "Nav*" /* Handlers.ModelHandlers.PageLoadMetrics.MetricName.SOFT_NAV */ :
|
|
812
|
+
"Nav" /* Handlers.ModelHandlers.PageLoadMetrics.MetricName.NAV */;
|
|
805
813
|
}
|
|
806
814
|
if (Types.Events.isMarkDOMContent(event)) {
|
|
807
815
|
color = 'var(--color-text-disabled)';
|
package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/extras/Initiators.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { get as getStackTrace } from './StackTraceForEvent.js';
|
|
2
|
+
/**
|
|
3
|
+
* There are bugs in the backend tracing that means that network requests are
|
|
4
|
+
* often incorrectly tied to an initiator. This function exists as a utility to
|
|
5
|
+
* look up an event's initiator regardless of the type of event, but also to
|
|
6
|
+
* provide a post-parsing fix for network initiators.
|
|
7
|
+
* The TL;DR is that images injected by a script will incorrectly have their
|
|
8
|
+
* initiator set to the root document. To fix this, we look at the stack trace
|
|
9
|
+
* when the request was sent, and use that.
|
|
10
|
+
*/
|
|
11
|
+
export function getNetworkInitiator(data, event) {
|
|
12
|
+
const networkHandlerInitiator = data.NetworkRequests.incompleteInitiator.get(event);
|
|
13
|
+
if (networkHandlerInitiator?.args.data.mimeType === 'text/css') {
|
|
14
|
+
// The bugs in tracing & initiators apply mostly to scripts; we have not
|
|
15
|
+
// seen a case where the trace events identify a CSS stylesheet as the
|
|
16
|
+
// initiator that is incorrect. Therefore, if a stylesheet is identified as
|
|
17
|
+
// the initiator, we trust that it is accurate and can exit early.
|
|
18
|
+
return networkHandlerInitiator;
|
|
19
|
+
}
|
|
20
|
+
// For network requests, it is more reliable to calculate the initiator via
|
|
21
|
+
// the stack trace if we have one.
|
|
22
|
+
// We have to use the raw source event (`ResourceSendRequest`) as that is
|
|
23
|
+
// the event with the `sampleStackId` property which is required to
|
|
24
|
+
// calculate this stacktrace correctly.
|
|
25
|
+
const stack = getStackTrace(event.rawSourceEvent, data);
|
|
26
|
+
// If the resource was injected by a script, it will have a parent call
|
|
27
|
+
// frame that points to the script. Otherwise, there is no parent and
|
|
28
|
+
// therefore we fallthrough to looking at the initiator directly on the
|
|
29
|
+
// network request.
|
|
30
|
+
const initiatorCallFrame = stack?.parent?.callFrames.at(0);
|
|
31
|
+
if (!initiatorCallFrame) {
|
|
32
|
+
return networkHandlerInitiator;
|
|
33
|
+
}
|
|
34
|
+
// Find all the requests for the URL we are searching for. Most of the time
|
|
35
|
+
// there is only 1, but there can be multiple requests for the same URL. The
|
|
36
|
+
// filtering by the timestamp ensures that we can never pick an initiator
|
|
37
|
+
// that happened after the initiated event.
|
|
38
|
+
const matchingRequestIds = data.NetworkRequests.requestIdsByURL.get(initiatorCallFrame.url) ?? [];
|
|
39
|
+
const matchingRequests = matchingRequestIds.map(id => data.NetworkRequests.byId.get(id))
|
|
40
|
+
.filter(req => req !== undefined)
|
|
41
|
+
.filter(req => req.ts < event.ts);
|
|
42
|
+
// Now we have filtered and have a list of requests that are before the
|
|
43
|
+
// event, we take the last one - the one closest to the initiated event.
|
|
44
|
+
// In the case that there are >1 requests, this is an educated guess.
|
|
45
|
+
return matchingRequests.at(-1);
|
|
46
|
+
}
|
package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/extras/TraceTree.js
CHANGED
|
@@ -413,8 +413,9 @@ export class BottomUpRootNode extends Node {
|
|
|
413
413
|
node.totalTime += totalTimeById.get(id) || 0;
|
|
414
414
|
totalTimeById.delete(id);
|
|
415
415
|
}
|
|
416
|
-
//
|
|
417
|
-
|
|
416
|
+
// An item on this stack means that this current node has a caller. Therefore,
|
|
417
|
+
// in a bottom-up view it has children.
|
|
418
|
+
if (idStack.length > 0) {
|
|
418
419
|
node.setHasChildren(true);
|
|
419
420
|
}
|
|
420
421
|
}
|
|
@@ -587,7 +588,7 @@ export function generateEventID(event) {
|
|
|
587
588
|
SamplesIntegrator.nativeGroup(event.callFrame.functionName) :
|
|
588
589
|
event.callFrame.functionName;
|
|
589
590
|
const location = event.callFrame.scriptId || event.callFrame.url || '';
|
|
590
|
-
return `f:${name}@${location}`;
|
|
591
|
+
return `f:${name}@${location}:${event.callFrame.lineNumber}:${event.callFrame.columnNumber}`;
|
|
591
592
|
}
|
|
592
593
|
if (Types.Events.isConsoleTimeStamp(event) && event.args.data) {
|
|
593
594
|
return `${event.name}:${event.args.data.name}`;
|
|
@@ -2,6 +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
|
export * as FilmStrip from './FilmStrip.js';
|
|
5
|
+
export * as Initiators from './Initiators.js';
|
|
5
6
|
export * as MainThreadActivity from './MainThreadActivity.js';
|
|
6
7
|
export * as ScriptDuplication from './ScriptDuplication.js';
|
|
7
8
|
export * as StackTraceForEvent from './StackTraceForEvent.js';
|
|
@@ -43,9 +43,9 @@ export async function finalize() {
|
|
|
43
43
|
const { traceBounds, navigationsByNavigationId } = metaData();
|
|
44
44
|
const metricScoresByFrameId = pageLoadMetricsData().metricScoresByFrameId;
|
|
45
45
|
for (const [navigationId, navigation] of navigationsByNavigationId) {
|
|
46
|
-
const lcpMetric = metricScoresByFrameId.get(navigation.args.frame)?.get(
|
|
46
|
+
const lcpMetric = metricScoresByFrameId.get(navigation.args.frame)?.get(navigation)?.get("LCP" /* MetricName.LCP */);
|
|
47
47
|
const lcpEvent = lcpMetric?.event;
|
|
48
|
-
if (!lcpEvent || !Types.Events.
|
|
48
|
+
if (!lcpEvent || !Types.Events.isAnyLargestContentfulPaintCandidate(lcpEvent)) {
|
|
49
49
|
continue;
|
|
50
50
|
}
|
|
51
51
|
const nodeId = lcpEvent.args.data?.nodeId;
|
|
@@ -395,7 +395,7 @@ async function buildLayoutShiftsClusters() {
|
|
|
395
395
|
// Update the cluster's worst layout shift.
|
|
396
396
|
if (worstShiftEvent) {
|
|
397
397
|
cluster.worstShiftEvent = worstShiftEvent;
|
|
398
|
-
cluster.rawSourceEvent = worstShiftEvent;
|
|
398
|
+
cluster.rawSourceEvent = worstShiftEvent.rawSourceEvent;
|
|
399
399
|
}
|
|
400
400
|
// layout shifts are already sorted by time ascending.
|
|
401
401
|
// Capture the time range of the cluster.
|
package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/MetaHandler.js
CHANGED
|
@@ -47,6 +47,7 @@ let traceBounds = makeNewTraceBounds();
|
|
|
47
47
|
*/
|
|
48
48
|
let navigationsByFrameId = new Map();
|
|
49
49
|
let navigationsByNavigationId = new Map();
|
|
50
|
+
let softNavigationsById = new Map();
|
|
50
51
|
let finalDisplayUrlByNavigationId = new Map();
|
|
51
52
|
let mainFrameNavigations = [];
|
|
52
53
|
// Represents all the threads in the trace, organized by process. This is mostly for internal
|
|
@@ -76,6 +77,7 @@ const CHROME_WEB_TRACE_EVENTS = new Set([
|
|
|
76
77
|
export function reset() {
|
|
77
78
|
navigationsByFrameId = new Map();
|
|
78
79
|
navigationsByNavigationId = new Map();
|
|
80
|
+
softNavigationsById = new Map();
|
|
79
81
|
finalDisplayUrlByNavigationId = new Map();
|
|
80
82
|
processNames = new Map();
|
|
81
83
|
mainFrameNavigations = [];
|
|
@@ -276,6 +278,9 @@ export function handleEvent(event) {
|
|
|
276
278
|
}
|
|
277
279
|
return;
|
|
278
280
|
}
|
|
281
|
+
if (Types.Events.isSoftNavigationStart(event)) {
|
|
282
|
+
softNavigationsById.set(event.args.context.performanceTimelineNavigationId, event);
|
|
283
|
+
}
|
|
279
284
|
// Update `finalDisplayUrlByNavigationId` to reflect the latest redirect for each navigation.
|
|
280
285
|
if (Types.Events.isResourceSendRequest(event)) {
|
|
281
286
|
if (event.args.data.resourceType !== 'Document') {
|
|
@@ -393,6 +398,7 @@ export function data() {
|
|
|
393
398
|
mainFrameURL,
|
|
394
399
|
navigationsByFrameId,
|
|
395
400
|
navigationsByNavigationId,
|
|
401
|
+
softNavigationsById,
|
|
396
402
|
finalDisplayUrlByNavigationId,
|
|
397
403
|
threadsInProcess,
|
|
398
404
|
rendererProcessesByFrame: rendererProcessesByFrameId,
|
|
@@ -13,6 +13,10 @@ let linkPreconnectEvents = [];
|
|
|
13
13
|
let requestMap = new Map();
|
|
14
14
|
let requestsById = new Map();
|
|
15
15
|
let requestsByTime = [];
|
|
16
|
+
/**
|
|
17
|
+
* URL => RequestId[]. There can be multiple requests for a single URL.
|
|
18
|
+
*/
|
|
19
|
+
let requestIdsByURL = new Map();
|
|
16
20
|
let networkRequestEventByInitiatorUrl = new Map();
|
|
17
21
|
let eventToInitiatorMap = new Map();
|
|
18
22
|
/**
|
|
@@ -61,6 +65,7 @@ export function reset() {
|
|
|
61
65
|
networkRequestEventByInitiatorUrl = new Map();
|
|
62
66
|
eventToInitiatorMap = new Map();
|
|
63
67
|
webSocketData = new Map();
|
|
68
|
+
requestIdsByURL = new Map();
|
|
64
69
|
entityMappings = {
|
|
65
70
|
eventsByEntity: new Map(),
|
|
66
71
|
entityByEvent: new Map(),
|
|
@@ -476,6 +481,9 @@ export async function finalize() {
|
|
|
476
481
|
// the captured requests, so here we store all of them together.
|
|
477
482
|
requestsByTime.push(networkEvent);
|
|
478
483
|
requestsById.set(networkEvent.args.data.requestId, networkEvent);
|
|
484
|
+
const requestsForUrl = requestIdsByURL.get(networkEvent.args.data.url) ?? [];
|
|
485
|
+
requestsForUrl.push(networkEvent.args.data.requestId);
|
|
486
|
+
requestIdsByURL.set(networkEvent.args.data.url, requestsForUrl);
|
|
479
487
|
// Update entity relationships for network events
|
|
480
488
|
HandlerHelpers.addNetworkRequestToEntityMapping(networkEvent, entityMappings, request);
|
|
481
489
|
// Establish initiator relationships
|
|
@@ -501,7 +509,8 @@ export function data() {
|
|
|
501
509
|
return {
|
|
502
510
|
byId: requestsById,
|
|
503
511
|
byTime: requestsByTime,
|
|
504
|
-
|
|
512
|
+
requestIdsByURL,
|
|
513
|
+
incompleteInitiator: eventToInitiatorMap,
|
|
505
514
|
webSocket: [...webSocketData.values()],
|
|
506
515
|
entityMappings: {
|
|
507
516
|
entityByEvent: entityMappings.entityByEvent,
|