chrome-devtools-frontend 1.0.1643099 → 1.0.1645245
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/eslint.config.mjs +3 -1
- package/extension-api/ExtensionAPI.d.ts +83 -12
- package/front_end/core/host/UserMetrics.ts +3 -3
- package/front_end/core/root/ExperimentNames.ts +0 -1
- package/front_end/core/sdk/CSSPropertyParserMatchers.ts +2 -3
- package/front_end/core/sdk/ConsoleModel.ts +4 -0
- package/front_end/core/sdk/NetworkRequest.ts +12 -1
- package/front_end/core/sdk/SourceMap.ts +15 -18
- package/front_end/entrypoints/greendev_floaty/FloatyEntrypoint.ts +0 -2
- package/front_end/entrypoints/greendev_floaty/greendev_floaty.ts +0 -2
- package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshot.ts +37 -0
- package/front_end/entrypoints/main/MainImpl.ts +0 -6
- package/front_end/generated/SupportedCSSProperties.js +4 -2
- package/front_end/models/ai_assistance/AiAgent2.ts +23 -12
- package/front_end/models/ai_assistance/README.md +5 -4
- package/front_end/models/ai_assistance/agents/AccessibilityAgent.ts +22 -16
- package/front_end/models/ai_assistance/agents/AiAgent.ts +19 -6
- package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +5 -5
- package/front_end/models/ai_assistance/agents/ExecuteJavascript.ts +1 -94
- package/front_end/models/ai_assistance/agents/NetworkAgent.ts +25 -0
- package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +37 -0
- package/front_end/models/ai_assistance/agents/README.md +57 -0
- package/front_end/models/ai_assistance/agents/StorageAgent.ts +54 -1
- package/front_end/models/ai_assistance/agents/StylingAgent.snapshot.txt +1 -2
- package/front_end/models/ai_assistance/agents/StylingAgent.ts +31 -3
- package/front_end/models/ai_assistance/tools/ExecuteJavaScript.ts +12 -21
- package/front_end/models/ai_assistance/tools/GetStyles.ts +19 -12
- package/front_end/models/ai_assistance/tools/README.md +45 -0
- package/front_end/models/ai_assistance/tools/Tool.ts +78 -9
- package/front_end/models/ai_assistance/tools/ToolRegistry.ts +21 -5
- package/front_end/models/bindings/DebuggerWorkspaceBinding.ts +6 -9
- package/front_end/models/bindings/DefaultScriptMapping.ts +2 -1
- package/front_end/models/bindings/SymbolizedError.ts +45 -35
- package/front_end/models/extensions/ExtensionAPI.ts +138 -47
- package/front_end/models/har/Importer.ts +1 -0
- package/front_end/models/heap_snapshot/HeapSnapshotModel.ts +18 -2
- package/front_end/models/heap_snapshot/HeapSnapshotProxy.ts +4 -0
- package/front_end/models/source_map_scopes/FunctionCodeResolver.ts +12 -2
- package/front_end/models/stack_trace/DetailedErrorStackParser.ts +58 -52
- package/front_end/models/stack_trace/StackTrace.ts +7 -0
- package/front_end/models/stack_trace/StackTraceImpl.ts +13 -4
- package/front_end/models/stack_trace/StackTraceModel.ts +43 -9
- package/front_end/models/trace/Styles.ts +29 -7
- package/front_end/models/trace/handlers/PageLoadMetricsHandler.ts +33 -4
- package/front_end/models/trace/helpers/Timing.ts +10 -0
- package/front_end/models/trace/types/TraceEvents.ts +22 -2
- package/front_end/panels/accessibility/AccessibilitySidebarView.ts +2 -1
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +9 -2
- package/front_end/panels/ai_assistance/ai_assistance-meta.ts +16 -0
- package/front_end/panels/application/ApplicationPanelSidebar.ts +83 -0
- package/front_end/panels/application/ApplicationPanelTreeElement.ts +39 -0
- package/front_end/panels/application/CookieItemsView.ts +2 -2
- package/front_end/panels/application/ServiceWorkersView.ts +2 -2
- package/front_end/panels/application/WebMCPView.ts +0 -1
- package/front_end/panels/application/components/BackForwardCacheView.ts +1 -2
- package/front_end/panels/application/resourcesSidebar.css +11 -0
- package/front_end/panels/console/ConsoleView.ts +6 -1
- package/front_end/panels/console/ConsoleViewMessage.ts +46 -213
- package/front_end/panels/console/SymbolizedErrorWidget.ts +14 -8
- package/front_end/panels/elements/AdoptedStyleSheetTreeElement.ts +0 -1
- package/front_end/panels/elements/ElementsTreeElement.ts +0 -2
- package/front_end/panels/elements/PropertyRenderer.ts +0 -1
- package/front_end/panels/elements/StylesSidebarPane.ts +9 -2
- package/front_end/panels/issues/AffectedResourcesView.ts +1 -1
- package/front_end/panels/issues/AffectedSourcesView.ts +1 -1
- package/front_end/panels/lighthouse/LighthouseReportRenderer.ts +0 -1
- package/front_end/panels/mobile_throttling/ThrottlingSettingsTab.ts +10 -0
- package/front_end/panels/network/NetworkDataGridNode.ts +1 -2
- package/front_end/panels/network/NetworkLogView.ts +34 -7
- package/front_end/panels/profiler/HeapProfileView.ts +0 -1
- package/front_end/panels/profiler/HeapSnapshotGridNodes.ts +0 -1
- package/front_end/panels/profiler/HeapSnapshotView.ts +1 -1
- package/front_end/panels/settings/components/SyncSection.ts +1 -1
- package/front_end/panels/settings/emulation/DevicesSettingsTab.ts +1 -0
- package/front_end/panels/sources/SourcesPanel.ts +2 -1
- package/front_end/panels/timeline/TimelineFlameChartView.ts +5 -4
- package/front_end/panels/timeline/TimelinePanel.ts +7 -0
- package/front_end/panels/timeline/TimelineUIUtils.ts +13 -14
- package/front_end/panels/timeline/TimingsTrackAppender.ts +7 -5
- package/front_end/panels/timeline/components/LayoutShiftDetails.ts +0 -1
- package/front_end/panels/timeline/components/NetworkRequestDetails.ts +0 -2
- package/front_end/panels/timeline/components/insights/ForcedReflow.ts +0 -1
- package/front_end/panels/timeline/components/insights/NodeLink.ts +0 -1
- package/front_end/panels/timeline/overlays/OverlaysImpl.ts +2 -0
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/ui/helpers/OpenInNewTab.ts +3 -3
- package/front_end/ui/kit/link/Link.ts +16 -2
- package/front_end/ui/legacy/InspectorDrawerView.ts +14 -5
- package/front_end/ui/legacy/InspectorView.ts +4 -1
- package/front_end/ui/legacy/PlusButton.ts +6 -1
- package/front_end/ui/legacy/StackedPane.ts +229 -0
- package/front_end/ui/legacy/ViewManager.ts +59 -169
- package/front_end/ui/legacy/Widget.ts +19 -1
- package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +95 -31
- package/front_end/ui/legacy/components/utils/JSPresentationUtils.ts +0 -1
- package/front_end/ui/legacy/components/utils/Linkifier.ts +2 -16
- package/front_end/ui/legacy/legacy.ts +3 -1
- package/front_end/ui/visual_logging/KnownContextValues.ts +5 -0
- package/package.json +1 -1
|
@@ -203,7 +203,14 @@ function createFunctionCode(
|
|
|
203
203
|
}
|
|
204
204
|
|
|
205
205
|
/**
|
|
206
|
-
*
|
|
206
|
+
* Resolves the function code and its surrounding context for a given location.
|
|
207
|
+
*
|
|
208
|
+
* The input location (line, column) may be either an authored (source-mapped)
|
|
209
|
+
* location or a raw location. The function will attempt to resolve it to a
|
|
210
|
+
* raw location regardless. This is necessary because callers (such as AI
|
|
211
|
+
* assistance) may work with either format.
|
|
212
|
+
*
|
|
213
|
+
* We filter projects by `target` to prevent cross-origin leaks.
|
|
207
214
|
*/
|
|
208
215
|
export async function getFunctionCodeFromLocation(
|
|
209
216
|
target: SDK.Target.Target, url: Platform.DevToolsPath.UrlString, line: number, column: number,
|
|
@@ -213,10 +220,13 @@ export async function getFunctionCodeFromLocation(
|
|
|
213
220
|
throw new Error('missing debugger model');
|
|
214
221
|
}
|
|
215
222
|
|
|
216
|
-
let uiSourceCode;
|
|
223
|
+
let uiSourceCode: Workspace.UISourceCode.UISourceCode|null = null;
|
|
217
224
|
const debuggerWorkspaceBinding = Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance();
|
|
218
225
|
const projects = debuggerWorkspaceBinding.workspace.projectsForType(Workspace.Workspace.projectTypes.Network);
|
|
219
226
|
for (const project of projects) {
|
|
227
|
+
if (Bindings.NetworkProject.NetworkProject.getTargetForProject(project) !== target) {
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
220
230
|
uiSourceCode = project.uiSourceCodeForURL(url);
|
|
221
231
|
if (uiSourceCode) {
|
|
222
232
|
break;
|
|
@@ -10,13 +10,15 @@ import type {RawFrame} from './Trie.js';
|
|
|
10
10
|
|
|
11
11
|
const CALL_FRAME_REGEX = /^\s*at\s+/;
|
|
12
12
|
|
|
13
|
+
export type ResolveURLCallback = (url: Platform.DevToolsPath.UrlString) => Platform.DevToolsPath.UrlString|null;
|
|
14
|
+
|
|
13
15
|
/**
|
|
14
16
|
* Takes a V8 Error#stack string and extracts structured information.
|
|
15
17
|
*
|
|
16
18
|
* @returns Null if the provided string has an unexpected format. A
|
|
17
19
|
* populated `RawFrame[]` otherwise.
|
|
18
20
|
*/
|
|
19
|
-
export function parseRawFramesFromErrorStack(stack: string): RawFrame[]|null {
|
|
21
|
+
export function parseRawFramesFromErrorStack(stack: string, resolveURL?: ResolveURLCallback): RawFrame[]|null {
|
|
20
22
|
const lines = stack.split('\n');
|
|
21
23
|
const firstAtLineIndex = findFramesStartLine(lines);
|
|
22
24
|
const rawFrames: RawFrame[] = [];
|
|
@@ -62,60 +64,70 @@ export function parseRawFramesFromErrorStack(stack: string): RawFrame[]|null {
|
|
|
62
64
|
let evalOrigin: RawFrame|undefined;
|
|
63
65
|
|
|
64
66
|
const openParenIndex = lineContent.indexOf(' (');
|
|
67
|
+
let location = '';
|
|
65
68
|
if (lineContent.endsWith(')') && openParenIndex !== -1) {
|
|
66
69
|
functionName = lineContent.substring(0, openParenIndex).trim();
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if (commaIndex !== -1) {
|
|
74
|
-
evalOriginStr = location.substring(0, commaIndex);
|
|
75
|
-
location = location.substring(commaIndex + 2);
|
|
76
|
-
} else {
|
|
77
|
-
location = '';
|
|
78
|
-
}
|
|
70
|
+
location = lineContent.substring(openParenIndex + 2, lineContent.length - 1);
|
|
71
|
+
} else if (lineContent.startsWith('(') && lineContent.endsWith(')')) {
|
|
72
|
+
location = lineContent.substring(1, lineContent.length - 1);
|
|
73
|
+
} else {
|
|
74
|
+
location = lineContent;
|
|
75
|
+
}
|
|
79
76
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
evalOrigin = parseRawFramesFromErrorStack(` at ${evalFunctionName} (${evalLocation})`)?.[0];
|
|
90
|
-
} else {
|
|
91
|
-
evalOrigin = parseRawFramesFromErrorStack(` at ${evalFunctionName}`)?.[0];
|
|
92
|
-
}
|
|
77
|
+
if (location.startsWith('eval at ')) {
|
|
78
|
+
isEval = true;
|
|
79
|
+
const commaIndex = location.lastIndexOf(', ');
|
|
80
|
+
let evalOriginStr = location;
|
|
81
|
+
if (commaIndex !== -1) {
|
|
82
|
+
evalOriginStr = location.substring(0, commaIndex);
|
|
83
|
+
location = location.substring(commaIndex + 2);
|
|
84
|
+
} else {
|
|
85
|
+
location = '';
|
|
93
86
|
}
|
|
94
87
|
|
|
95
|
-
if (
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
wasmFunctionIndex = parseInt(wasmMatch[2], 10);
|
|
106
|
-
columnNumber = parseInt(wasmMatch[3], 16);
|
|
107
|
-
}
|
|
88
|
+
if (evalOriginStr.startsWith('eval at ')) {
|
|
89
|
+
evalOriginStr = evalOriginStr.substring(8);
|
|
90
|
+
}
|
|
91
|
+
const innerOpenParen = evalOriginStr.indexOf(' (');
|
|
92
|
+
let evalFunctionName = evalOriginStr;
|
|
93
|
+
let evalLocation = '';
|
|
94
|
+
if (innerOpenParen !== -1) {
|
|
95
|
+
evalFunctionName = evalOriginStr.substring(0, innerOpenParen).trim();
|
|
96
|
+
evalLocation = evalOriginStr.substring(innerOpenParen + 2, evalOriginStr.length - 1);
|
|
97
|
+
evalOrigin = parseRawFramesFromErrorStack(` at ${evalFunctionName} (${evalLocation})`, resolveURL)?.[0];
|
|
108
98
|
} else {
|
|
109
|
-
|
|
110
|
-
url = splitResult.url;
|
|
111
|
-
lineNumber = splitResult.lineNumber ?? -1;
|
|
112
|
-
columnNumber = splitResult.columnNumber ?? -1;
|
|
99
|
+
evalOrigin = parseRawFramesFromErrorStack(` at ${evalFunctionName}`, resolveURL)?.[0];
|
|
113
100
|
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (location.startsWith('index ')) {
|
|
104
|
+
promiseIndex = parseInt(location.substring(6), 10);
|
|
105
|
+
url = '';
|
|
106
|
+
} else if (location === '<anonymous>' || location === 'native') {
|
|
107
|
+
url = '';
|
|
108
|
+
} else if (location.includes(':wasm-function[')) {
|
|
109
|
+
isWasm = true;
|
|
110
|
+
const wasmMatch = /^(.*):wasm-function\[(\d+)\]:(0x[0-9a-fA-F]+)$/.exec(location);
|
|
111
|
+
if (wasmMatch) {
|
|
112
|
+
url = wasmMatch[1];
|
|
113
|
+
wasmFunctionIndex = parseInt(wasmMatch[2], 10);
|
|
114
|
+
columnNumber = parseInt(wasmMatch[3], 16);
|
|
115
|
+
lineNumber = 0;
|
|
116
|
+
}
|
|
117
|
+
} else if (location) {
|
|
118
|
+
const splitResult = Common.ParsedURL.ParsedURL.splitLineAndColumn(location);
|
|
117
119
|
lineNumber = splitResult.lineNumber ?? -1;
|
|
118
120
|
columnNumber = splitResult.columnNumber ?? -1;
|
|
121
|
+
|
|
122
|
+
if (resolveURL && splitResult.url !== '<anonymous>' && splitResult.url !== 'native') {
|
|
123
|
+
const resolved = resolveURL(splitResult.url);
|
|
124
|
+
if (!resolved) {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
url = resolved;
|
|
128
|
+
} else {
|
|
129
|
+
url = splitResult.url;
|
|
130
|
+
}
|
|
119
131
|
}
|
|
120
132
|
|
|
121
133
|
// Handle "typeName.methodName [as alias]"
|
|
@@ -181,13 +193,7 @@ export function parseMessage(stack: string): string {
|
|
|
181
193
|
export function augmentRawFramesWithScriptIds(
|
|
182
194
|
rawFrames: RawFrame[], protocolStackTrace: Protocol.Runtime.StackTrace): void {
|
|
183
195
|
function augmentFrame(rawFrame: RawFrame): void {
|
|
184
|
-
const isWasm = rawFrame.isWasm;
|
|
185
196
|
const protocolFrame = protocolStackTrace.callFrames.find(frame => {
|
|
186
|
-
if (isWasm) {
|
|
187
|
-
// The parser parses Wasm offsets into the `columnNumber` field. The `lineNumber` is always -1.
|
|
188
|
-
// In the protocol trace, the `lineNumber` is 0 (for Wasm) and `columnNumber` is the bytecode offset.
|
|
189
|
-
return rawFrame.url === frame.url && rawFrame.columnNumber === frame.columnNumber;
|
|
190
|
-
}
|
|
191
197
|
return rawFrame.url === frame.url && rawFrame.lineNumber === frame.lineNumber &&
|
|
192
198
|
rawFrame.columnNumber === frame.columnNumber;
|
|
193
199
|
});
|
|
@@ -48,6 +48,13 @@ export interface Frame {
|
|
|
48
48
|
* Whether the corresponding raw frame is JS or WASM.
|
|
49
49
|
*/
|
|
50
50
|
readonly isWasm?: boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Whether this frame is an inlined frame. Used by SymbolizedErrorWidget
|
|
53
|
+
* to render the translated name (i.e. `name`) for inlined frames, and
|
|
54
|
+
* the physical name (i.e. `rawName`) for normal frames to preserve existing
|
|
55
|
+
* behavior.
|
|
56
|
+
*/
|
|
57
|
+
readonly isInline?: boolean;
|
|
51
58
|
}
|
|
52
59
|
|
|
53
60
|
export interface ParsedErrorStackFrame extends Frame {
|
|
@@ -89,11 +89,12 @@ export class FrameImpl implements StackTrace.StackTrace.Frame {
|
|
|
89
89
|
readonly missingDebugInfo?: StackTrace.StackTrace.MissingDebugInfo;
|
|
90
90
|
readonly rawName?: string;
|
|
91
91
|
readonly isWasm?: boolean;
|
|
92
|
+
readonly isInline?: boolean;
|
|
92
93
|
|
|
93
|
-
constructor(
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
94
|
+
constructor(url: string|undefined, uiSourceCode: Workspace.UISourceCode.UISourceCode|undefined,
|
|
95
|
+
name: string|undefined, line: number, column: number,
|
|
96
|
+
missingDebugInfo?: StackTrace.StackTrace.MissingDebugInfo, rawName?: string, isWasm?: boolean,
|
|
97
|
+
isInline?: boolean) {
|
|
97
98
|
this.url = url;
|
|
98
99
|
this.uiSourceCode = uiSourceCode;
|
|
99
100
|
this.name = name;
|
|
@@ -102,6 +103,7 @@ export class FrameImpl implements StackTrace.StackTrace.Frame {
|
|
|
102
103
|
this.missingDebugInfo = missingDebugInfo;
|
|
103
104
|
this.rawName = rawName;
|
|
104
105
|
this.isWasm = isWasm;
|
|
106
|
+
this.isInline = isInline;
|
|
105
107
|
}
|
|
106
108
|
}
|
|
107
109
|
|
|
@@ -197,6 +199,9 @@ export class ParsedErrorStackFrameImpl implements StackTrace.StackTrace.ParsedEr
|
|
|
197
199
|
get isWasm(): boolean|undefined {
|
|
198
200
|
return this.#frame.isWasm;
|
|
199
201
|
}
|
|
202
|
+
get isInline(): boolean|undefined {
|
|
203
|
+
return this.#frame.isInline;
|
|
204
|
+
}
|
|
200
205
|
get wasmModuleName(): string|undefined {
|
|
201
206
|
return this.#parsedFrameInfo?.wasmModuleName;
|
|
202
207
|
}
|
|
@@ -291,6 +296,10 @@ export class DebuggableFrameImpl implements StackTrace.StackTrace.DebuggableFram
|
|
|
291
296
|
return this.#frame.isWasm;
|
|
292
297
|
}
|
|
293
298
|
|
|
299
|
+
get isInline(): boolean|undefined {
|
|
300
|
+
return this.#frame.isInline;
|
|
301
|
+
}
|
|
302
|
+
|
|
294
303
|
get sdkFrame(): SDK.DebuggerModel.CallFrame {
|
|
295
304
|
return this.#sdkFrame;
|
|
296
305
|
}
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// found in the LICENSE file.
|
|
4
4
|
|
|
5
5
|
import * as Common from '../../core/common/common.js';
|
|
6
|
+
import type * as Platform from '../../core/platform/platform.js';
|
|
6
7
|
import * as SDK from '../../core/sdk/sdk.js';
|
|
7
8
|
import type * as Protocol from '../../generated/protocol.js';
|
|
8
9
|
|
|
@@ -65,7 +66,17 @@ export class StackTraceModel extends SDK.SDKModel.SDKModel<unknown> {
|
|
|
65
66
|
async createFromErrorStackLikeString(
|
|
66
67
|
stack: string, rawFramesToUIFrames: TranslateRawFrames,
|
|
67
68
|
exceptionDetails?: Protocol.Runtime.ExceptionDetails): Promise<StackTrace.StackTrace.ParsedErrorStackTrace|null> {
|
|
68
|
-
const
|
|
69
|
+
const debuggerModel = this.target().model(SDK.DebuggerModel.DebuggerModel) as SDK.DebuggerModel.DebuggerModel;
|
|
70
|
+
const baseURL = this.target().inspectedURL();
|
|
71
|
+
const resolveURL = (url: Platform.DevToolsPath.UrlString): Platform.DevToolsPath.UrlString|null => {
|
|
72
|
+
let urlWithScheme = parseOrScriptMatch(debuggerModel, url);
|
|
73
|
+
if (!urlWithScheme && Common.ParsedURL.ParsedURL.isRelativeURL(url)) {
|
|
74
|
+
urlWithScheme = parseOrScriptMatch(debuggerModel, Common.ParsedURL.ParsedURL.completeURL(baseURL, url));
|
|
75
|
+
}
|
|
76
|
+
return urlWithScheme;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const rawFrames = parseRawFramesFromErrorStack(stack, resolveURL);
|
|
69
80
|
if (!rawFrames) {
|
|
70
81
|
return null;
|
|
71
82
|
}
|
|
@@ -211,10 +222,11 @@ export class StackTraceModel extends SDK.SDKModel.SDKModel<unknown> {
|
|
|
211
222
|
let i = 0;
|
|
212
223
|
let evalI = 0;
|
|
213
224
|
for (const node of fragment.node.getCallStack()) {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
225
|
+
const group = uiFrames[i++];
|
|
226
|
+
node.frames =
|
|
227
|
+
group.map((frame, index) => new FrameImpl(frame.url, frame.uiSourceCode, frame.name, frame.line, frame.column,
|
|
228
|
+
frame.missingDebugInfo, node.rawFrame.functionName,
|
|
229
|
+
node.rawFrame.isWasm, index < group.length - 1));
|
|
218
230
|
|
|
219
231
|
if (node.parsedFrameInfo?.evalOrigin) {
|
|
220
232
|
node.evalOrigin = evalOrigins[evalI++];
|
|
@@ -253,10 +265,10 @@ async function translateEvalOrigin(
|
|
|
253
265
|
rawFrame: RawFrame, rawFramesToUIFrames: TranslateRawFrames,
|
|
254
266
|
target: SDK.Target.Target): Promise<EvalOrigin|undefined> {
|
|
255
267
|
const uiFrames = await rawFramesToUIFrames([rawFrame], target);
|
|
256
|
-
const
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
268
|
+
const group = uiFrames[0];
|
|
269
|
+
const frames = group.map((frame, index) => new FrameImpl(frame.url, frame.uiSourceCode, frame.name, frame.line,
|
|
270
|
+
frame.column, frame.missingDebugInfo, rawFrame.functionName,
|
|
271
|
+
rawFrame.isWasm, index < group.length - 1));
|
|
260
272
|
|
|
261
273
|
let parentEvalOrigin: EvalOrigin|undefined;
|
|
262
274
|
if (rawFrame.parsedFrameInfo?.evalOrigin) {
|
|
@@ -266,4 +278,26 @@ async function translateEvalOrigin(
|
|
|
266
278
|
return new EvalOrigin(frames, parentEvalOrigin);
|
|
267
279
|
}
|
|
268
280
|
|
|
281
|
+
function parseOrScriptMatch(debuggerModel: SDK.DebuggerModel.DebuggerModel,
|
|
282
|
+
url: Platform.DevToolsPath.UrlString|null): Platform.DevToolsPath.UrlString|null {
|
|
283
|
+
if (!url) {
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
286
|
+
if (Common.ParsedURL.ParsedURL.isValidUrlString(url)) {
|
|
287
|
+
return url;
|
|
288
|
+
}
|
|
289
|
+
if (debuggerModel.scriptsForSourceURL(url).length) {
|
|
290
|
+
return url;
|
|
291
|
+
}
|
|
292
|
+
// nodejs stack traces contain (absolute) file paths, but v8 reports them as file: urls.
|
|
293
|
+
try {
|
|
294
|
+
const fileUrl = new URL(url, 'file://');
|
|
295
|
+
if (debuggerModel.scriptsForSourceURL(fileUrl.href as Platform.DevToolsPath.UrlString).length) {
|
|
296
|
+
return fileUrl.href as Platform.DevToolsPath.UrlString;
|
|
297
|
+
}
|
|
298
|
+
} catch {
|
|
299
|
+
}
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
302
|
+
|
|
269
303
|
SDK.SDKModel.SDKModel.register(StackTraceModel, {capabilities: SDK.Target.Capability.NONE, autostart: false});
|
|
@@ -291,6 +291,10 @@ const UIStrings = {
|
|
|
291
291
|
* @description Text in Timeline UIUtils of the Performance panel
|
|
292
292
|
*/
|
|
293
293
|
frameStartedLoading: 'Frame started loading',
|
|
294
|
+
/**
|
|
295
|
+
* @description Text in Timeline UIUtils of the Performance panel
|
|
296
|
+
*/
|
|
297
|
+
softNavigationStart: 'Soft navigation start',
|
|
294
298
|
/**
|
|
295
299
|
* @description Text in Timeline UIUtils of the Performance panel
|
|
296
300
|
*/
|
|
@@ -307,6 +311,10 @@ const UIStrings = {
|
|
|
307
311
|
* @description Text in Timeline UIUtils of the Performance panel
|
|
308
312
|
*/
|
|
309
313
|
firstContentfulPaint: 'First Contentful Paint',
|
|
314
|
+
/**
|
|
315
|
+
* @description Text in Timeline UIUtils of the Performance panel
|
|
316
|
+
*/
|
|
317
|
+
softFirstContentfulPaint: 'Soft First Contentful Paint',
|
|
310
318
|
/**
|
|
311
319
|
* @description Text in Timeline UIUtils of the Performance panel
|
|
312
320
|
*/
|
|
@@ -866,6 +874,12 @@ export function maybeInitSylesMap(): EventStylesMap {
|
|
|
866
874
|
true,
|
|
867
875
|
),
|
|
868
876
|
|
|
877
|
+
[Types.Events.Name.SOFT_NAVIGATION_START]: new TimelineRecordStyle(
|
|
878
|
+
i18nString(UIStrings.softNavigationStart),
|
|
879
|
+
defaultCategoryStyles.loading,
|
|
880
|
+
true,
|
|
881
|
+
),
|
|
882
|
+
|
|
869
883
|
[Types.Events.Name.MARK_FIRST_PAINT]: new TimelineRecordStyle(
|
|
870
884
|
i18nString(UIStrings.firstPaint),
|
|
871
885
|
defaultCategoryStyles.painting,
|
|
@@ -878,6 +892,12 @@ export function maybeInitSylesMap(): EventStylesMap {
|
|
|
878
892
|
true,
|
|
879
893
|
),
|
|
880
894
|
|
|
895
|
+
[Types.Events.Name.MARK_SOFT_FCP]: new TimelineRecordStyle(
|
|
896
|
+
i18nString(UIStrings.softFirstContentfulPaint),
|
|
897
|
+
defaultCategoryStyles.rendering,
|
|
898
|
+
true,
|
|
899
|
+
),
|
|
900
|
+
|
|
881
901
|
[Types.Events.Name.MARK_LCP_CANDIDATE]: new TimelineRecordStyle(
|
|
882
902
|
i18nString(UIStrings.largestContentfulPaint),
|
|
883
903
|
defaultCategoryStyles.rendering,
|
|
@@ -1024,11 +1044,11 @@ export function maybeInitSylesMap(): EventStylesMap {
|
|
|
1024
1044
|
[Types.Events.Name.ASYNC_TASK]:
|
|
1025
1045
|
new TimelineRecordStyle(i18nString(UIStrings.asyncTask), defaultCategoryStyles.async),
|
|
1026
1046
|
|
|
1027
|
-
[Types.Events.Name.LAYOUT_SHIFT]:
|
|
1028
|
-
i18nString(UIStrings.layoutShift), defaultCategoryStyles.experience,
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1047
|
+
[Types.Events.Name.LAYOUT_SHIFT]:
|
|
1048
|
+
new TimelineRecordStyle(i18nString(UIStrings.layoutShift), defaultCategoryStyles.experience,
|
|
1049
|
+
/* Mark LayoutShifts as hidden; in the timeline we render
|
|
1050
|
+
* SyntheticLayoutShifts so those are the ones visible to the user */
|
|
1051
|
+
true),
|
|
1032
1052
|
|
|
1033
1053
|
[Types.Events.Name.SYNTHETIC_LAYOUT_SHIFT]:
|
|
1034
1054
|
new TimelineRecordStyle(i18nString(UIStrings.layoutShift), defaultCategoryStyles.experience),
|
|
@@ -1123,9 +1143,11 @@ export function markerDetailsForEvent(event: Types.Events.Event): {
|
|
|
1123
1143
|
} {
|
|
1124
1144
|
let title = '';
|
|
1125
1145
|
let color = 'var(--color-text-primary)';
|
|
1126
|
-
if (Types.Events.
|
|
1146
|
+
if (Types.Events.isAnyFirstContentfulPaint(event)) {
|
|
1127
1147
|
color = 'var(--sys-color-green-bright)';
|
|
1128
|
-
title =
|
|
1148
|
+
title = (Types.Events.isSoftFirstContentfulPaint(event)) ?
|
|
1149
|
+
Handlers.ModelHandlers.PageLoadMetrics.MetricName.SOFT_FCP :
|
|
1150
|
+
Handlers.ModelHandlers.PageLoadMetrics.MetricName.FCP;
|
|
1129
1151
|
}
|
|
1130
1152
|
if (Types.Events.isAnyLargestContentfulPaintCandidate(event)) {
|
|
1131
1153
|
color = 'var(--sys-color-green)';
|
|
@@ -75,6 +75,28 @@ export function handleEvent(event: Types.Events.Event): void {
|
|
|
75
75
|
return;
|
|
76
76
|
}
|
|
77
77
|
pageLoadEventsArray.push(event);
|
|
78
|
+
|
|
79
|
+
// A soft nav entry includes the Soft FCP details but we want to process both
|
|
80
|
+
// so push a separate Soft FCP event
|
|
81
|
+
if (Types.Events.isSoftNavigationStart(event) && event.args?.context?.firstContentfulPaint) {
|
|
82
|
+
const syntheticSoftFcpEvent = Helpers.SyntheticEvents.SyntheticEventsManager
|
|
83
|
+
.registerSyntheticEvent<Types.Events.SyntheticSoftFirstContentfulPaint>({
|
|
84
|
+
name: Types.Events.Name.MARK_SOFT_FCP,
|
|
85
|
+
ph: Types.Events.Phase.MARK,
|
|
86
|
+
rawSourceEvent: event,
|
|
87
|
+
pid: event.pid,
|
|
88
|
+
tid: event.tid,
|
|
89
|
+
ts: Types.Timing.Micro(event.args.context.firstContentfulPaint),
|
|
90
|
+
cat: event.cat,
|
|
91
|
+
args: {
|
|
92
|
+
frame: event.args.frame,
|
|
93
|
+
context: {
|
|
94
|
+
...event.args.context,
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
pageLoadEventsArray.push(syntheticSoftFcpEvent);
|
|
99
|
+
}
|
|
78
100
|
}
|
|
79
101
|
|
|
80
102
|
function storePageLoadMetricAgainstNavigationId(
|
|
@@ -101,7 +123,7 @@ function storePageLoadMetricAgainstNavigationId(
|
|
|
101
123
|
return;
|
|
102
124
|
}
|
|
103
125
|
|
|
104
|
-
if (Types.Events.
|
|
126
|
+
if (Types.Events.isAnyFirstContentfulPaint(event)) {
|
|
105
127
|
const fcpTime = Types.Timing.Micro(event.ts - navigation.ts);
|
|
106
128
|
const classification = scoreClassificationForFirstContentfulPaint(fcpTime);
|
|
107
129
|
const metricScore = {event, metricName: MetricName.FCP, classification, navigation, timing: fcpTime};
|
|
@@ -226,7 +248,7 @@ function storeMetricScore(frameId: string, navigation: AnyNavigationStart, metri
|
|
|
226
248
|
}
|
|
227
249
|
|
|
228
250
|
export function getFrameIdForPageLoadEvent(event: Types.Events.PageLoadEvent): string {
|
|
229
|
-
if (Types.Events.
|
|
251
|
+
if (Types.Events.isAnyFirstContentfulPaint(event) || Types.Events.isInteractiveTime(event) ||
|
|
230
252
|
Types.Events.isAnyLargestContentfulPaintCandidate(event) || Types.Events.isNavigationStart(event) ||
|
|
231
253
|
Types.Events.isSoftNavigationStart(event) || Types.Events.isLayoutShift(event) ||
|
|
232
254
|
Types.Events.isFirstPaint(event)) {
|
|
@@ -243,7 +265,7 @@ export function getFrameIdForPageLoadEvent(event: Types.Events.PageLoadEvent): s
|
|
|
243
265
|
}
|
|
244
266
|
|
|
245
267
|
function getNavigationForPageLoadEvent(event: Types.Events.PageLoadEvent): AnyNavigationStart|null {
|
|
246
|
-
if (Types.Events.
|
|
268
|
+
if (Types.Events.isAnyFirstContentfulPaint(event) || Types.Events.isAnyLargestContentfulPaintCandidate(event) ||
|
|
247
269
|
Types.Events.isFirstPaint(event)) {
|
|
248
270
|
const {navigationsByNavigationId, softNavigationsById} = metaHandlerData();
|
|
249
271
|
|
|
@@ -255,6 +277,12 @@ function getNavigationForPageLoadEvent(event: Types.Events.PageLoadEvent): AnyNa
|
|
|
255
277
|
// The most recent soft navigation must have been before the trace started.
|
|
256
278
|
return null;
|
|
257
279
|
}
|
|
280
|
+
} else if (Types.Events.isSoftFirstContentfulPaint(event) && event.args.context?.performanceTimelineNavigationId) {
|
|
281
|
+
navigation = softNavigationsById.get(event.args.context.performanceTimelineNavigationId);
|
|
282
|
+
if (!navigation) {
|
|
283
|
+
// The most recent soft navigation must have been before the trace started.
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
258
286
|
} else {
|
|
259
287
|
const navigationId = event.args.data?.navigationId;
|
|
260
288
|
if (!navigationId) {
|
|
@@ -428,7 +456,7 @@ export async function finalize(): Promise<void> {
|
|
|
428
456
|
// Filter out LCP candidates to use only definitive LCP values
|
|
429
457
|
const allEventsButLCP =
|
|
430
458
|
pageLoadEventsArray.filter(event => !Types.Events.isAnyLargestContentfulPaintCandidate(event));
|
|
431
|
-
const markerEvents = [...
|
|
459
|
+
const markerEvents = [...allEventsButLCP, ...allFinalLCPEvents].filter(Types.Events.isMarkerEvent);
|
|
432
460
|
// Filter by main frame and sort.
|
|
433
461
|
allMarkerEvents =
|
|
434
462
|
markerEvents.filter(event => getFrameIdForPageLoadEvent(event) === mainFrame).sort((a, b) => a.ts - b.ts);
|
|
@@ -495,6 +523,7 @@ export const enum MetricName {
|
|
|
495
523
|
NAV = 'Nav',
|
|
496
524
|
// Soft Navigation and Soft Metrics
|
|
497
525
|
SOFT_NAV = 'Nav*',
|
|
526
|
+
SOFT_FCP = 'FCP*',
|
|
498
527
|
SOFT_LCP = 'LCP*',
|
|
499
528
|
// Note: INP is handled in UserInteractionsHandler
|
|
500
529
|
}
|
|
@@ -32,6 +32,16 @@ export function timeStampForEventAdjustedByClosestNavigation(
|
|
|
32
32
|
if (navigationForEvent) {
|
|
33
33
|
eventTimeStamp = event.ts - navigationForEvent.ts;
|
|
34
34
|
}
|
|
35
|
+
} else if (Types.Events.isSoftFirstContentfulPaint(event) && event.args?.context?.performanceTimelineNavigationId) {
|
|
36
|
+
const navigationForEvent = softNavigationsById.get(event.args.context.performanceTimelineNavigationId);
|
|
37
|
+
if (navigationForEvent) {
|
|
38
|
+
eventTimeStamp = event.ts - navigationForEvent.ts;
|
|
39
|
+
}
|
|
40
|
+
} else if (Types.Events.isSoftNavigationStart(event)) {
|
|
41
|
+
const navigationForEvent = getNavigationForTraceEvent(event, event.args.frame, navigationsByFrameId);
|
|
42
|
+
if (navigationForEvent) {
|
|
43
|
+
eventTimeStamp = event.ts - navigationForEvent.ts;
|
|
44
|
+
}
|
|
35
45
|
} else if (event.args?.data?.navigationId) {
|
|
36
46
|
const navigationForEvent = navigationsByNavigationId.get(event.args.data.navigationId);
|
|
37
47
|
if (navigationForEvent) {
|
|
@@ -737,6 +737,16 @@ export interface FirstContentfulPaint extends Mark {
|
|
|
737
737
|
};
|
|
738
738
|
}
|
|
739
739
|
|
|
740
|
+
// Soft FCP is basically a copy of SoftNavigationStart but with a different name
|
|
741
|
+
// and a different ts.
|
|
742
|
+
export interface SyntheticSoftFirstContentfulPaint extends Omit<SoftNavigationStart, 'name'|'ph'>,
|
|
743
|
+
Omit<SyntheticBased, 'name'|'ph'|'args'> {
|
|
744
|
+
name: Name.MARK_SOFT_FCP;
|
|
745
|
+
ph: Phase.MARK;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
export type AnyFirstContentfulPaint = FirstContentfulPaint|SyntheticSoftFirstContentfulPaint;
|
|
749
|
+
|
|
740
750
|
export interface FirstPaint extends Mark {
|
|
741
751
|
name: Name.MARK_FIRST_PAINT;
|
|
742
752
|
args: Args&{
|
|
@@ -747,14 +757,14 @@ export interface FirstPaint extends Mark {
|
|
|
747
757
|
};
|
|
748
758
|
}
|
|
749
759
|
|
|
750
|
-
export type PageLoadEvent =
|
|
760
|
+
export type PageLoadEvent = AnyFirstContentfulPaint|MarkDOMContent|InteractiveTime|AnyLargestContentfulPaintCandidate|
|
|
751
761
|
LayoutShift|FirstPaint|MarkLoad|NavigationStart|SoftNavigationStart;
|
|
752
762
|
|
|
753
763
|
const markerTypeGuards = [
|
|
754
764
|
isMarkDOMContent,
|
|
755
765
|
isMarkLoad,
|
|
756
766
|
isFirstPaint,
|
|
757
|
-
|
|
767
|
+
isAnyFirstContentfulPaint,
|
|
758
768
|
isAnyLargestContentfulPaintCandidate,
|
|
759
769
|
isNavigationStart,
|
|
760
770
|
isSoftNavigationStart,
|
|
@@ -765,6 +775,7 @@ export const MarkerName = [
|
|
|
765
775
|
Name.MARK_LOAD,
|
|
766
776
|
Name.MARK_FIRST_PAINT,
|
|
767
777
|
Name.MARK_FCP,
|
|
778
|
+
Name.MARK_SOFT_FCP,
|
|
768
779
|
Name.MARK_LCP_CANDIDATE,
|
|
769
780
|
Name.MARK_LCP_CANDIDATE_FOR_SOFT_NAVIGATION,
|
|
770
781
|
Name.NAVIGATION_START,
|
|
@@ -2285,6 +2296,14 @@ export function isFirstContentfulPaint(event: Event): event is FirstContentfulPa
|
|
|
2285
2296
|
return event.name === Name.MARK_FCP;
|
|
2286
2297
|
}
|
|
2287
2298
|
|
|
2299
|
+
export function isSoftFirstContentfulPaint(event: Event): event is SyntheticSoftFirstContentfulPaint {
|
|
2300
|
+
return event.name === Name.MARK_SOFT_FCP;
|
|
2301
|
+
}
|
|
2302
|
+
|
|
2303
|
+
export function isAnyFirstContentfulPaint(event: Event): event is AnyFirstContentfulPaint {
|
|
2304
|
+
return event.name === Name.MARK_FCP || event.name === Name.MARK_SOFT_FCP;
|
|
2305
|
+
}
|
|
2306
|
+
|
|
2288
2307
|
export function isAnyLargestContentfulPaintCandidate(event: Event): event is AnyLargestContentfulPaintCandidate {
|
|
2289
2308
|
return event.name === Name.MARK_LCP_CANDIDATE || event.name === Name.MARK_LCP_CANDIDATE_FOR_SOFT_NAVIGATION;
|
|
2290
2309
|
}
|
|
@@ -3168,6 +3187,7 @@ export const enum Name {
|
|
|
3168
3187
|
MARK_DOM_CONTENT = 'MarkDOMContent',
|
|
3169
3188
|
MARK_FIRST_PAINT = 'firstPaint',
|
|
3170
3189
|
MARK_FCP = 'firstContentfulPaint',
|
|
3190
|
+
MARK_SOFT_FCP = 'SyntheticSoftFirstContentfulPaint',
|
|
3171
3191
|
MARK_LCP_CANDIDATE = 'largestContentfulPaint::Candidate',
|
|
3172
3192
|
MARK_LCP_CANDIDATE_FOR_SOFT_NAVIGATION = 'largestContentfulPaint::CandidateForSoftNavigation',
|
|
3173
3193
|
MARK_LCP_INVALIDATE = 'largestContentfulPaint::Invalidate',
|
|
@@ -33,7 +33,7 @@ export class AccessibilitySidebarView extends UI.Widget.VBox {
|
|
|
33
33
|
#node: SDK.DOMModel.DOMNode|null;
|
|
34
34
|
#axNode: SDK.AccessibilityModel.AccessibilityNode|null;
|
|
35
35
|
private skipNextPullNode: boolean;
|
|
36
|
-
private readonly sidebarPaneStack: UI.
|
|
36
|
+
private readonly sidebarPaneStack: UI.ViewManager.StackLocation;
|
|
37
37
|
private readonly ariaSubPane: ARIAAttributesPane;
|
|
38
38
|
private readonly axNodeSubPane: AXNodeSubPane;
|
|
39
39
|
private readonly sourceOrderSubPane: SourceOrderPane;
|
|
@@ -159,6 +159,7 @@ export class AccessibilitySidebarView extends UI.Widget.VBox {
|
|
|
159
159
|
|
|
160
160
|
private updateToggle(): void {
|
|
161
161
|
const isToggled = this.toggleAction.toggled();
|
|
162
|
+
this.sidebarPaneStack.notifyVisibilityChanged(isToggled);
|
|
162
163
|
// eslint-disable-next-line @devtools/no-lit-render-outside-of-view
|
|
163
164
|
render(
|
|
164
165
|
html`
|
|
@@ -1575,7 +1575,12 @@ export class AiAssistancePanel extends UI.Panel.Panel {
|
|
|
1575
1575
|
break;
|
|
1576
1576
|
}
|
|
1577
1577
|
case 'ai-assistance.storage-floating-button': {
|
|
1578
|
-
Host.userMetrics.actionTaken(Host.UserMetrics.Action.
|
|
1578
|
+
Host.userMetrics.actionTaken(Host.UserMetrics.Action.AiAssistanceOpenedFromApplicationPanelFloatingButton);
|
|
1579
|
+
targetConversationType = AiAssistanceModel.AiHistoryStorage.ConversationType.STORAGE;
|
|
1580
|
+
break;
|
|
1581
|
+
}
|
|
1582
|
+
case 'ai-assistance.application-panel-context': {
|
|
1583
|
+
Host.userMetrics.actionTaken(Host.UserMetrics.Action.AiAssistanceOpenedFromApplicationPanel);
|
|
1579
1584
|
targetConversationType = AiAssistanceModel.AiHistoryStorage.ConversationType.STORAGE;
|
|
1580
1585
|
break;
|
|
1581
1586
|
}
|
|
@@ -2115,7 +2120,9 @@ export class ActionDelegate implements UI.ActionRegistration.ActionDelegate {
|
|
|
2115
2120
|
case 'drjones.network-panel-context':
|
|
2116
2121
|
case 'drjones.performance-panel-context':
|
|
2117
2122
|
case 'drjones.sources-floating-button':
|
|
2118
|
-
case 'drjones.sources-panel-context':
|
|
2123
|
+
case 'drjones.sources-panel-context':
|
|
2124
|
+
case 'ai-assistance.storage-floating-button':
|
|
2125
|
+
case 'ai-assistance.application-panel-context': {
|
|
2119
2126
|
void (async () => {
|
|
2120
2127
|
const view = UI.ViewManager.ViewManager.instance().view(
|
|
2121
2128
|
AiAssistancePanel.panelName,
|
|
@@ -313,3 +313,19 @@ UI.ActionRegistration.registerActionExtension({
|
|
|
313
313
|
condition: config =>
|
|
314
314
|
isStorageAgentFeatureAvailable(config) && !isPolicyRestricted(config) && !isGeoRestricted(config),
|
|
315
315
|
});
|
|
316
|
+
|
|
317
|
+
UI.ActionRegistration.registerActionExtension({
|
|
318
|
+
actionId: 'ai-assistance.application-panel-context',
|
|
319
|
+
contextTypes(): [] {
|
|
320
|
+
return [];
|
|
321
|
+
},
|
|
322
|
+
category: UI.ActionRegistration.ActionCategory.GLOBAL,
|
|
323
|
+
title: i18nAiBrandedString(UIStrings.debugWithGemini, UIStrings.debugWithAi),
|
|
324
|
+
configurableBindings: false,
|
|
325
|
+
async loadActionDelegate() {
|
|
326
|
+
const AiAssistance = await loadAiAssistanceModule();
|
|
327
|
+
return new AiAssistance.ActionDelegate();
|
|
328
|
+
},
|
|
329
|
+
condition: config =>
|
|
330
|
+
isStorageAgentFeatureAvailable(config) && !isPolicyRestricted(config) && !isGeoRestricted(config),
|
|
331
|
+
});
|