chrome-devtools-frontend 1.0.1522145 → 1.0.1522585
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/docs/ui_engineering.md +76 -0
- package/front_end/core/sdk/EnhancedTracesParser.ts +13 -6
- package/front_end/generated/Deprecation.ts +4 -4
- package/front_end/models/ai_assistance/agents/StylingAgent.snapshot.txt +559 -0
- package/front_end/models/javascript_metadata/NativeFunctions.js +1 -1
- package/front_end/models/trace/LanternComputationData.ts +1 -0
- package/front_end/models/trace/handlers/NetworkRequestsHandler.ts +10 -0
- package/front_end/models/trace/insights/DocumentLatency.ts +9 -10
- package/front_end/models/trace/types/TraceEvents.ts +6 -5
- package/front_end/panels/protocol_monitor/ProtocolMonitor.ts +31 -32
- package/front_end/panels/timeline/TimelineFlameChartView.ts +17 -0
- package/front_end/panels/timeline/TimelinePanel.ts +5 -0
- package/front_end/panels/timeline/TimelineUIUtils.ts +12 -3
- package/front_end/panels/timeline/components/ExportTraceOptions.ts +21 -9
- package/front_end/panels/timeline/timelineDetailsView.css +5 -0
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/ui/components/tooltips/Tooltip.ts +13 -4
- package/package.json +1 -1
package/docs/ui_engineering.md
CHANGED
@@ -1223,3 +1223,79 @@ export const DEFAULT_VIEW = (input, _output, target) => {
|
|
1223
1223
|
target, {host: input});
|
1224
1224
|
};
|
1225
1225
|
```
|
1226
|
+
|
1227
|
+
## Refactoring UI.Toolbar.Provider
|
1228
|
+
|
1229
|
+
As part of the migration, sometimes classes need to be broken up into smaller pieces. Classes implementing
|
1230
|
+
`UI.Toolbar.Provider` logic are good examples of this, if they implement `View` logic in addition to their
|
1231
|
+
`UI.Toolbar.Provider` responsibilities. The View logic needs to be moved to a separate class.
|
1232
|
+
|
1233
|
+
|
1234
|
+
**Before:**
|
1235
|
+
```typescript
|
1236
|
+
export class NodeIndicator implements UI.Toolbar.Provider {
|
1237
|
+
readonly #element: Element;
|
1238
|
+
readonly #item: UI.Toolbar.ToolbarItem;
|
1239
|
+
|
1240
|
+
private constructor() {
|
1241
|
+
// Creates `this.#element` and `this.#item` imperatively (e.g. using document.createElement/createChild).
|
1242
|
+
}
|
1243
|
+
static instance(opts: { forceNew: boolean|null, } = {forceNew: null}): NodeIndicator {
|
1244
|
+
// Creates an instance of this class and returns it.
|
1245
|
+
}
|
1246
|
+
#update(input): void { /* Handles updates to `this.#element` and `this.#item`. */}
|
1247
|
+
item(): UI.Toolbar.ToolbarItem|null {
|
1248
|
+
return this.#item;
|
1249
|
+
}
|
1250
|
+
}
|
1251
|
+
```
|
1252
|
+
|
1253
|
+
**After:**
|
1254
|
+
```typescript
|
1255
|
+
export const DEFAULT_VIEW: View = (input, output, target) => {
|
1256
|
+
// Implementation of the View using Lit.render() (omitted for brevity).
|
1257
|
+
};
|
1258
|
+
|
1259
|
+
export class NodeIndicator extends UI.Widget.Widget {
|
1260
|
+
readonly #view: View;
|
1261
|
+
|
1262
|
+
constructor(element?: HTMLElement, view = DEFAULT_VIEW) {
|
1263
|
+
super(element, {useShadowDom: true});
|
1264
|
+
this.#view = view;
|
1265
|
+
}
|
1266
|
+
|
1267
|
+
override performUpdate(): void {
|
1268
|
+
const input = {
|
1269
|
+
// Whatever input the View needs.
|
1270
|
+
};
|
1271
|
+
this.#view(input, {}, this.contentElement);
|
1272
|
+
}
|
1273
|
+
}
|
1274
|
+
|
1275
|
+
let nodeIndicatorProviderInstance: NodeIndicatorProvider;
|
1276
|
+
export class NodeIndicatorProvider implements UI.Toolbar.Provider {
|
1277
|
+
#toolbarItem: UI.Toolbar.ToolbarItem;
|
1278
|
+
#widgetElement: UI.Widget.WidgetElement<NodeIndicator>;
|
1279
|
+
|
1280
|
+
private constructor() {
|
1281
|
+
this.#widgetElement = document.createElement('devtools-widget') as UI.Widget.WidgetElement<NodeIndicator>;
|
1282
|
+
this.#widgetElement.widgetConfig = UI.Widget.widgetConfig(NodeIndicator);
|
1283
|
+
|
1284
|
+
this.#toolbarItem = new UI.Toolbar.ToolbarItem(this.#widgetElement);
|
1285
|
+
this.#toolbarItem.setVisible(false);
|
1286
|
+
}
|
1287
|
+
|
1288
|
+
item(): UI.Toolbar.ToolbarItem|null {
|
1289
|
+
return this.#toolbarItem;
|
1290
|
+
}
|
1291
|
+
|
1292
|
+
static instance(opts: {forceNew: boolean|null} = {forceNew: null}): NodeIndicatorProvider {
|
1293
|
+
const {forceNew} = opts;
|
1294
|
+
if (!nodeIndicatorProviderInstance || forceNew) {
|
1295
|
+
nodeIndicatorProviderInstance = new NodeIndicatorProvider();
|
1296
|
+
}
|
1297
|
+
|
1298
|
+
return nodeIndicatorProviderInstance;
|
1299
|
+
}
|
1300
|
+
}
|
1301
|
+
```
|
@@ -35,11 +35,10 @@ export interface RundownScriptCompiled extends EventBase {
|
|
35
35
|
frameType: 'page'|'iframe',
|
36
36
|
url: string,
|
37
37
|
/**
|
38
|
-
*
|
39
|
-
*
|
40
|
-
* This applies for all `isolate` in trace events we consume.
|
38
|
+
* Older traces were a number, but this is an unsigned 64 bit value, so that was a bug.
|
39
|
+
* New traces use string instead. See https://crbug.com/447654178.
|
41
40
|
*/
|
42
|
-
isolate: number,
|
41
|
+
isolate: string|number,
|
43
42
|
/** AKA V8ContextToken. https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/inspector/inspector_trace_events.cc;l=1229;drc=3c88f61e18b043e70c225d8d57c77832a85e7f58 */
|
44
43
|
v8context: string,
|
45
44
|
origin: string,
|
@@ -60,7 +59,11 @@ export interface RundownScript extends EventBase {
|
|
60
59
|
name: 'ScriptCatchup';
|
61
60
|
args: {
|
62
61
|
data: {
|
63
|
-
|
62
|
+
/**
|
63
|
+
* Older traces were a number, but this is an unsigned 64 bit value, so that was a bug.
|
64
|
+
* New traces use string instead. See https://crbug.com/447654178.
|
65
|
+
*/
|
66
|
+
isolate: string|number,
|
64
67
|
executionContextId: Protocol.Runtime.ExecutionContextId,
|
65
68
|
scriptId: number,
|
66
69
|
isModule: boolean,
|
@@ -119,7 +122,11 @@ interface FunctionCall extends EventBase {
|
|
119
122
|
data: {
|
120
123
|
frame: Protocol.Page.FrameId,
|
121
124
|
scriptId: Protocol.Runtime.ScriptId,
|
122
|
-
|
125
|
+
/**
|
126
|
+
* Older traces were a number, but this is an unsigned 64 bit value, so that was a bug.
|
127
|
+
* New traces use string instead. See https://crbug.com/447654178.
|
128
|
+
*/
|
129
|
+
isolate?: string|number,
|
123
130
|
},
|
124
131
|
};
|
125
132
|
}
|
@@ -57,15 +57,15 @@ export const UIStrings = {
|
|
57
57
|
/**
|
58
58
|
* @description Warning displayed to developers when the Geolocation API is used from an insecure origin (one that isn't localhost or doesn't use HTTPS) to notify them that this use is no longer supported.
|
59
59
|
*/
|
60
|
-
GeolocationInsecureOrigin: "`getCurrentPosition()` and `watchPosition()` no longer work on insecure origins. To use this feature, you should consider switching your application to a secure origin, such as HTTPS. See https://
|
60
|
+
GeolocationInsecureOrigin: "`getCurrentPosition()` and `watchPosition()` no longer work on insecure origins. To use this feature, you should consider switching your application to a secure origin, such as HTTPS. See https://www.chromium.org/Home/chromium-security/deprecating-powerful-features-on-insecure-origins/ for more details.",
|
61
61
|
/**
|
62
62
|
* @description Warning displayed to developers when the Geolocation API is used from an insecure origin (one that isn't localhost or doesn't use HTTPS) to notify them that this use is deprecated.
|
63
63
|
*/
|
64
|
-
GeolocationInsecureOriginDeprecatedNotRemoved: "`getCurrentPosition()` and `watchPosition()` are deprecated on insecure origins. To use this feature, you should consider switching your application to a secure origin, such as HTTPS. See https://
|
64
|
+
GeolocationInsecureOriginDeprecatedNotRemoved: "`getCurrentPosition()` and `watchPosition()` are deprecated on insecure origins. To use this feature, you should consider switching your application to a secure origin, such as HTTPS. See https://www.chromium.org/Home/chromium-security/deprecating-powerful-features-on-insecure-origins/ for more details.",
|
65
65
|
/**
|
66
66
|
* @description This warning occurs when the `getUserMedia()` API is invoked on an insecure (e.g., HTTP) site. This is only permitted on secure sites (e.g., HTTPS).
|
67
67
|
*/
|
68
|
-
GetUserMediaInsecureOrigin: "`getUserMedia()` no longer works on insecure origins. To use this feature, you should consider switching your application to a secure origin, such as HTTPS. See https://
|
68
|
+
GetUserMediaInsecureOrigin: "`getUserMedia()` no longer works on insecure origins. To use this feature, you should consider switching your application to a secure origin, such as HTTPS. See https://www.chromium.org/Home/chromium-security/deprecating-powerful-features-on-insecure-origins/ for more details.",
|
69
69
|
/**
|
70
70
|
* @description A deprecation warning shown to developers in the DevTools Issues tab when code tries to use the deprecated hostCandidate field, guiding developers to use the equivalent information in the .address and .port fields instead.
|
71
71
|
*/
|
@@ -105,7 +105,7 @@ export const UIStrings = {
|
|
105
105
|
/**
|
106
106
|
* @description Warning displayed to developers when the Notification API is used from an insecure origin (one that isn't localhost or doesn't use HTTPS) to notify them that this use is no longer supported.
|
107
107
|
*/
|
108
|
-
NotificationInsecureOrigin: "The Notification API may no longer be used from insecure origins. You should consider switching your application to a secure origin, such as HTTPS. See https://
|
108
|
+
NotificationInsecureOrigin: "The Notification API may no longer be used from insecure origins. You should consider switching your application to a secure origin, such as HTTPS. See https://www.chromium.org/Home/chromium-security/deprecating-powerful-features-on-insecure-origins/ for more details.",
|
109
109
|
/**
|
110
110
|
* @description Warning displayed to developers when permission to use notifications has been requested by a cross-origin iframe, to notify them that this use is no longer supported.
|
111
111
|
*/
|
@@ -0,0 +1,559 @@
|
|
1
|
+
Title: StylingAgent describeElement should describe an element with no children, siblings, or parent
|
2
|
+
Content:
|
3
|
+
* Its selector is `div#myElement`
|
4
|
+
=== end content
|
5
|
+
|
6
|
+
Title: StylingAgent describeElement should describe an element with child element and text nodes
|
7
|
+
Content:
|
8
|
+
* Its selector is `div#parentElement`
|
9
|
+
* It has 2 child element nodes: `span.child1`, `span.child2`
|
10
|
+
* It only has 1 child text node
|
11
|
+
=== end content
|
12
|
+
|
13
|
+
Title: StylingAgent describeElement should describe an element with siblings and a parent
|
14
|
+
Content:
|
15
|
+
* Its selector is `div#parentElement`
|
16
|
+
* It has a next sibling and it is an element node
|
17
|
+
* It has a previous sibling and it is a non element node
|
18
|
+
* Its parent's selector is `div#grandparentElement`
|
19
|
+
* Its parent is a non element node
|
20
|
+
* Its parent has only 1 child element node
|
21
|
+
* Its parent has only 1 child text node
|
22
|
+
=== end content
|
23
|
+
|
24
|
+
Title: StylingAgent buildRequest structure matches the snapshot
|
25
|
+
Content:
|
26
|
+
{
|
27
|
+
"client": "CHROME_DEVTOOLS",
|
28
|
+
"current_message": {
|
29
|
+
"parts": [
|
30
|
+
{
|
31
|
+
"text": "test input"
|
32
|
+
}
|
33
|
+
],
|
34
|
+
"role": 1
|
35
|
+
},
|
36
|
+
"historical_contexts": [
|
37
|
+
{
|
38
|
+
"parts": [
|
39
|
+
{
|
40
|
+
"text": "QUERY: question"
|
41
|
+
}
|
42
|
+
],
|
43
|
+
"role": 1
|
44
|
+
},
|
45
|
+
{
|
46
|
+
"parts": [
|
47
|
+
{
|
48
|
+
"text": "answer"
|
49
|
+
}
|
50
|
+
],
|
51
|
+
"role": 2
|
52
|
+
}
|
53
|
+
],
|
54
|
+
"function_declarations": [
|
55
|
+
{
|
56
|
+
"name": "executeJavaScript",
|
57
|
+
"description": "This function allows you to run JavaScript code on the inspected page to access the element styles and page content.\nCall this function to gather additional information or modify the page state. Call this function enough times to investigate the user request.",
|
58
|
+
"parameters": {
|
59
|
+
"type": 6,
|
60
|
+
"description": "",
|
61
|
+
"nullable": false,
|
62
|
+
"properties": {
|
63
|
+
"code": {
|
64
|
+
"type": 1,
|
65
|
+
"description": "JavaScript code snippet to run on the inspected page. Make sure the code is formatted for readability.\n\n# Instructions\n\n* To return data, define a top-level `data` variable and populate it with data you want to get. Only JSON-serializable objects can be assigned to `data`.\n* If you modify styles on an element, ALWAYS call the pre-defined global `async setElementStyles(el: Element, styles: object)` function. This function is an internal mechanism for you and should never be presented as a command/advice to the user.\n* Use `window.getComputedStyle` to gather **computed** styles and make sure that you take the distinction between authored styles and computed styles into account.\n* **CRITICAL** Only get styles that might be relevant to the user request.\n* **CRITICAL** Call `window.getComputedStyle` only once per element and store results into a local variable. Never try to return all the styles of the element in `data`.\n* **CRITICAL** Never assume a selector for the elements unless you verified your knowledge.\n* **CRITICAL** Consider that `data` variable from the previous function calls are not available in a new function call.\n\nFor example, the code to return basic styles:\n\n```\nconst styles = window.getComputedStyle($0);\nconst data = {\n display: styles['display'],\n visibility: styles['visibility'],\n position: styles['position'],\n left: styles['right'],\n top: styles['top'],\n width: styles['width'],\n height: styles['height'],\n zIndex: styles['z-index']\n};\n```\n\nFor example, the code to change element styles:\n\n```\nawait setElementStyles($0, {\n color: 'blue',\n});\n```\n\nFor example, the code to get current and parent styles at once:\n\n```\nconst styles = window.getComputedStyle($0);\nconst parentStyles = window.getComputedStyle($0.parentElement);\nconst data = {\n currentElementStyles: {\n display: styles['display'],\n visibility: styles['visibility'],\n position: styles['position'],\n left: styles['right'],\n top: styles['top'],\n width: styles['width'],\n height: styles['height'],\n zIndex: styles['z-index'],\n },\n parentElementStyles: {\n display: parentStyles['display'],\n visibility: parentStyles['visibility'],\n position: parentStyles['position'],\n left: parentStyles['right'],\n top: parentStyles['top'],\n width: parentStyles['width'],\n height: parentStyles['height'],\n zIndex: parentStyles['z-index'],\n },\n};\n```\n\nFor example, the code to get check siblings and overlapping elements:\n\n```\nconst computedStyles = window.getComputedStyle($0);\nconst parentComputedStyles = window.getComputedStyle($0.parentElement);\nconst data = {\n numberOfChildren: $0.children.length,\n numberOfSiblings: $0.parentElement.children.length,\n hasPreviousSibling: !!$0.previousElementSibling,\n hasNextSibling: !!$0.nextElementSibling,\n elementStyles: {\n display: computedStyles['display'],\n visibility: computedStyles['visibility'],\n position: computedStyles['position'],\n clipPath: computedStyles['clip-path'],\n zIndex: computedStyles['z-index']\n },\n parentStyles: {\n display: parentComputedStyles['display'],\n visibility: parentComputedStyles['visibility'],\n position: parentComputedStyles['position'],\n clipPath: parentComputedStyles['clip-path'],\n zIndex: parentComputedStyles['z-index']\n },\n overlappingElements: Array.from(document.querySelectorAll('*'))\n .filter(el => {\n const rect = el.getBoundingClientRect();\n const popupRect = $0.getBoundingClientRect();\n return (\n el !== $0 &&\n rect.left < popupRect.right &&\n rect.right > popupRect.left &&\n rect.top < popupRect.bottom &&\n rect.bottom > popupRect.top\n );\n })\n .map(el => ({\n tagName: el.tagName,\n id: el.id,\n className: el.className,\n zIndex: window.getComputedStyle(el)['z-index']\n }))\n};\n```\n"
|
66
|
+
},
|
67
|
+
"thought": {
|
68
|
+
"type": 1,
|
69
|
+
"description": "Explain why you want to run this code"
|
70
|
+
},
|
71
|
+
"title": {
|
72
|
+
"type": 1,
|
73
|
+
"description": "Provide a summary of what the code does. For example, \"Checking related element styles\"."
|
74
|
+
}
|
75
|
+
}
|
76
|
+
}
|
77
|
+
}
|
78
|
+
],
|
79
|
+
"options": {
|
80
|
+
"model_id": "test model"
|
81
|
+
},
|
82
|
+
"metadata": {
|
83
|
+
"disable_user_content_logging": false,
|
84
|
+
"string_session_id": "sessionId",
|
85
|
+
"user_tier": 2,
|
86
|
+
"client_version": "unit_test+function_calling"
|
87
|
+
},
|
88
|
+
"functionality_type": 5,
|
89
|
+
"client_feature": 2
|
90
|
+
}
|
91
|
+
=== end content
|
92
|
+
|
93
|
+
Title: StylingAgent buildRequest builds a request with aborted query in history before a real request
|
94
|
+
Content:
|
95
|
+
[
|
96
|
+
{
|
97
|
+
"parts": [
|
98
|
+
{
|
99
|
+
"text": "# Inspected element\n\nelement-description\n\n# User request\n\nQUERY: test2"
|
100
|
+
}
|
101
|
+
],
|
102
|
+
"role": 1
|
103
|
+
},
|
104
|
+
{
|
105
|
+
"parts": [
|
106
|
+
{
|
107
|
+
"functionCall": {
|
108
|
+
"name": "executeJavaScript",
|
109
|
+
"args": {
|
110
|
+
"title": "title2",
|
111
|
+
"thought": "thought2",
|
112
|
+
"code": "action2"
|
113
|
+
}
|
114
|
+
}
|
115
|
+
}
|
116
|
+
],
|
117
|
+
"role": 2
|
118
|
+
},
|
119
|
+
{
|
120
|
+
"parts": [
|
121
|
+
{
|
122
|
+
"functionResponse": {
|
123
|
+
"name": "executeJavaScript",
|
124
|
+
"response": {
|
125
|
+
"result": "result2"
|
126
|
+
}
|
127
|
+
}
|
128
|
+
}
|
129
|
+
],
|
130
|
+
"role": 0
|
131
|
+
},
|
132
|
+
{
|
133
|
+
"parts": [
|
134
|
+
{
|
135
|
+
"text": "answer2"
|
136
|
+
}
|
137
|
+
],
|
138
|
+
"role": 2
|
139
|
+
}
|
140
|
+
]
|
141
|
+
=== end content
|
142
|
+
|
143
|
+
Title: StylingAgent run generates an answer immediately
|
144
|
+
Content:
|
145
|
+
[
|
146
|
+
{
|
147
|
+
"type": "user-query",
|
148
|
+
"query": "test"
|
149
|
+
},
|
150
|
+
{
|
151
|
+
"type": "context",
|
152
|
+
"title": "Analyzing the prompt",
|
153
|
+
"details": [
|
154
|
+
{
|
155
|
+
"title": "Data used",
|
156
|
+
"text": "* Its selector is `undefined`"
|
157
|
+
}
|
158
|
+
]
|
159
|
+
},
|
160
|
+
{
|
161
|
+
"type": "querying"
|
162
|
+
},
|
163
|
+
{
|
164
|
+
"type": "answer",
|
165
|
+
"text": "this is the answer",
|
166
|
+
"complete": true
|
167
|
+
}
|
168
|
+
]
|
169
|
+
=== end content
|
170
|
+
|
171
|
+
Title: StylingAgent run generates an answer immediately with correct historical contexts in the new request
|
172
|
+
Content:
|
173
|
+
[
|
174
|
+
{
|
175
|
+
"parts": [
|
176
|
+
{
|
177
|
+
"text": "# Inspected element\n\n* Its selector is `undefined`\n\n# User request\n\nQUERY: test"
|
178
|
+
}
|
179
|
+
],
|
180
|
+
"role": 1
|
181
|
+
},
|
182
|
+
{
|
183
|
+
"parts": [
|
184
|
+
{
|
185
|
+
"text": "this is the answer"
|
186
|
+
}
|
187
|
+
],
|
188
|
+
"role": 2
|
189
|
+
}
|
190
|
+
]
|
191
|
+
=== end content
|
192
|
+
|
193
|
+
Title: StylingAgent run correctly handles historical_contexts in AIDA requests
|
194
|
+
Content:
|
195
|
+
[
|
196
|
+
{
|
197
|
+
"text": "# Inspected element\n\n* Its selector is `undefined`\n\n# User request\n\nQUERY: test"
|
198
|
+
},
|
199
|
+
[
|
200
|
+
{
|
201
|
+
"parts": [
|
202
|
+
{
|
203
|
+
"text": "# Inspected element\n\n* Its selector is `undefined`\n\n# User request\n\nQUERY: test"
|
204
|
+
}
|
205
|
+
],
|
206
|
+
"role": 1
|
207
|
+
},
|
208
|
+
{
|
209
|
+
"parts": [
|
210
|
+
{
|
211
|
+
"functionCall": {
|
212
|
+
"name": "executeJavaScript",
|
213
|
+
"args": {
|
214
|
+
"code": "const data = {\"test\": \"observation\"}",
|
215
|
+
"thought": "I am thinking.",
|
216
|
+
"title": "thinking"
|
217
|
+
}
|
218
|
+
}
|
219
|
+
}
|
220
|
+
],
|
221
|
+
"role": 2
|
222
|
+
}
|
223
|
+
]
|
224
|
+
]
|
225
|
+
=== end content
|
226
|
+
|
227
|
+
Title: StylingAgent run generates an rpcId for the answer
|
228
|
+
Content:
|
229
|
+
[
|
230
|
+
{
|
231
|
+
"type": "user-query",
|
232
|
+
"query": "test"
|
233
|
+
},
|
234
|
+
{
|
235
|
+
"type": "context",
|
236
|
+
"title": "Analyzing the prompt",
|
237
|
+
"details": [
|
238
|
+
{
|
239
|
+
"title": "Data used",
|
240
|
+
"text": "* Its selector is `undefined`"
|
241
|
+
}
|
242
|
+
]
|
243
|
+
},
|
244
|
+
{
|
245
|
+
"type": "querying"
|
246
|
+
},
|
247
|
+
{
|
248
|
+
"type": "answer",
|
249
|
+
"text": "this is the answer",
|
250
|
+
"complete": true,
|
251
|
+
"rpcId": 123
|
252
|
+
}
|
253
|
+
]
|
254
|
+
=== end content
|
255
|
+
|
256
|
+
Title: StylingAgent run throws an error based on the attribution metadata including RecitationAction.BLOCK
|
257
|
+
Content:
|
258
|
+
[
|
259
|
+
{
|
260
|
+
"type": "user-query",
|
261
|
+
"query": "test"
|
262
|
+
},
|
263
|
+
{
|
264
|
+
"type": "context",
|
265
|
+
"title": "Analyzing the prompt",
|
266
|
+
"details": [
|
267
|
+
{
|
268
|
+
"title": "Data used",
|
269
|
+
"text": "* Its selector is `undefined`"
|
270
|
+
}
|
271
|
+
]
|
272
|
+
},
|
273
|
+
{
|
274
|
+
"type": "querying"
|
275
|
+
},
|
276
|
+
{
|
277
|
+
"type": "answer",
|
278
|
+
"text": "this is the partial answer",
|
279
|
+
"complete": false
|
280
|
+
},
|
281
|
+
{
|
282
|
+
"type": "error",
|
283
|
+
"error": "block"
|
284
|
+
}
|
285
|
+
]
|
286
|
+
=== end content
|
287
|
+
|
288
|
+
Title: StylingAgent run does not throw an error based on attribution metadata not including RecitationAction.BLOCK
|
289
|
+
Content:
|
290
|
+
[
|
291
|
+
{
|
292
|
+
"type": "user-query",
|
293
|
+
"query": "test"
|
294
|
+
},
|
295
|
+
{
|
296
|
+
"type": "context",
|
297
|
+
"title": "Analyzing the prompt",
|
298
|
+
"details": [
|
299
|
+
{
|
300
|
+
"title": "Data used",
|
301
|
+
"text": "* Its selector is `undefined`"
|
302
|
+
}
|
303
|
+
]
|
304
|
+
},
|
305
|
+
{
|
306
|
+
"type": "querying"
|
307
|
+
},
|
308
|
+
{
|
309
|
+
"type": "answer",
|
310
|
+
"text": "this is the answer",
|
311
|
+
"complete": true,
|
312
|
+
"rpcId": 123
|
313
|
+
}
|
314
|
+
]
|
315
|
+
=== end content
|
316
|
+
|
317
|
+
Title: StylingAgent run generates a response if nothing is returned
|
318
|
+
Content:
|
319
|
+
[
|
320
|
+
{
|
321
|
+
"type": "user-query",
|
322
|
+
"query": "test"
|
323
|
+
},
|
324
|
+
{
|
325
|
+
"type": "context",
|
326
|
+
"title": "Analyzing the prompt",
|
327
|
+
"details": [
|
328
|
+
{
|
329
|
+
"title": "Data used",
|
330
|
+
"text": "* Its selector is `undefined`"
|
331
|
+
}
|
332
|
+
]
|
333
|
+
},
|
334
|
+
{
|
335
|
+
"type": "querying"
|
336
|
+
},
|
337
|
+
{
|
338
|
+
"type": "error",
|
339
|
+
"error": "unknown"
|
340
|
+
}
|
341
|
+
]
|
342
|
+
=== end content
|
343
|
+
|
344
|
+
Title: StylingAgent run generates an action response if action and answer both present
|
345
|
+
Content:
|
346
|
+
[
|
347
|
+
{
|
348
|
+
"type": "user-query",
|
349
|
+
"query": "test"
|
350
|
+
},
|
351
|
+
{
|
352
|
+
"type": "context",
|
353
|
+
"title": "Analyzing the prompt",
|
354
|
+
"details": [
|
355
|
+
{
|
356
|
+
"title": "Data used",
|
357
|
+
"text": "* Its selector is `undefined`"
|
358
|
+
}
|
359
|
+
]
|
360
|
+
},
|
361
|
+
{
|
362
|
+
"type": "querying"
|
363
|
+
},
|
364
|
+
{
|
365
|
+
"type": "thought",
|
366
|
+
"thought": "I am thinking."
|
367
|
+
},
|
368
|
+
{
|
369
|
+
"type": "action",
|
370
|
+
"code": "console.log('hello');",
|
371
|
+
"output": "hello",
|
372
|
+
"canceled": false
|
373
|
+
},
|
374
|
+
{
|
375
|
+
"type": "querying"
|
376
|
+
},
|
377
|
+
{
|
378
|
+
"type": "answer",
|
379
|
+
"text": "this is the actual answer",
|
380
|
+
"complete": true
|
381
|
+
}
|
382
|
+
]
|
383
|
+
=== end content
|
384
|
+
|
385
|
+
Title: StylingAgent run generates history for multiple actions
|
386
|
+
Content:
|
387
|
+
[
|
388
|
+
{
|
389
|
+
"parts": [
|
390
|
+
{
|
391
|
+
"text": "# Inspected element\n\n* Its selector is `undefined`\n\n# User request\n\nQUERY: test"
|
392
|
+
}
|
393
|
+
],
|
394
|
+
"role": 1
|
395
|
+
},
|
396
|
+
{
|
397
|
+
"parts": [
|
398
|
+
{
|
399
|
+
"functionCall": {
|
400
|
+
"name": "executeJavaScript",
|
401
|
+
"args": {
|
402
|
+
"thought": "thought 1",
|
403
|
+
"title": "test",
|
404
|
+
"code": "console.log('test')"
|
405
|
+
}
|
406
|
+
}
|
407
|
+
}
|
408
|
+
],
|
409
|
+
"role": 2
|
410
|
+
},
|
411
|
+
{
|
412
|
+
"parts": [
|
413
|
+
{
|
414
|
+
"functionResponse": {
|
415
|
+
"name": "executeJavaScript",
|
416
|
+
"response": {
|
417
|
+
"result": "undefined"
|
418
|
+
}
|
419
|
+
}
|
420
|
+
}
|
421
|
+
],
|
422
|
+
"role": 0
|
423
|
+
},
|
424
|
+
{
|
425
|
+
"parts": [
|
426
|
+
{
|
427
|
+
"functionCall": {
|
428
|
+
"name": "executeJavaScript",
|
429
|
+
"args": {
|
430
|
+
"thought": "thought 2",
|
431
|
+
"title": "test",
|
432
|
+
"code": "console.log('test')"
|
433
|
+
}
|
434
|
+
}
|
435
|
+
}
|
436
|
+
],
|
437
|
+
"role": 2
|
438
|
+
},
|
439
|
+
{
|
440
|
+
"parts": [
|
441
|
+
{
|
442
|
+
"functionResponse": {
|
443
|
+
"name": "executeJavaScript",
|
444
|
+
"response": {
|
445
|
+
"result": "undefined"
|
446
|
+
}
|
447
|
+
}
|
448
|
+
}
|
449
|
+
],
|
450
|
+
"role": 0
|
451
|
+
},
|
452
|
+
{
|
453
|
+
"parts": [
|
454
|
+
{
|
455
|
+
"functionCall": {
|
456
|
+
"name": "executeJavaScript",
|
457
|
+
"args": {
|
458
|
+
"thought": "thought 3",
|
459
|
+
"title": "test",
|
460
|
+
"code": "console.log('test')"
|
461
|
+
}
|
462
|
+
}
|
463
|
+
}
|
464
|
+
],
|
465
|
+
"role": 2
|
466
|
+
},
|
467
|
+
{
|
468
|
+
"parts": [
|
469
|
+
{
|
470
|
+
"functionResponse": {
|
471
|
+
"name": "executeJavaScript",
|
472
|
+
"response": {
|
473
|
+
"result": "undefined"
|
474
|
+
}
|
475
|
+
}
|
476
|
+
}
|
477
|
+
],
|
478
|
+
"role": 0
|
479
|
+
},
|
480
|
+
{
|
481
|
+
"parts": [
|
482
|
+
{
|
483
|
+
"text": "this is the answer"
|
484
|
+
}
|
485
|
+
],
|
486
|
+
"role": 2
|
487
|
+
}
|
488
|
+
]
|
489
|
+
=== end content
|
490
|
+
|
491
|
+
Title: StylingAgent enhanceQuery does not add multimodal input evaluation prompt when multimodal is disabled
|
492
|
+
Content:
|
493
|
+
# Inspected element
|
494
|
+
|
495
|
+
* Its selector is `div#myElement`
|
496
|
+
|
497
|
+
# User request
|
498
|
+
|
499
|
+
QUERY: test query
|
500
|
+
=== end content
|
501
|
+
|
502
|
+
Title: StylingAgent enhanceQuery does not add multimodal input evaluation prompt when multimodal is enabled but multimodalInputType is missing
|
503
|
+
Content:
|
504
|
+
# Inspected element
|
505
|
+
|
506
|
+
* Its selector is `div#myElement`
|
507
|
+
|
508
|
+
# User request
|
509
|
+
|
510
|
+
QUERY: test query
|
511
|
+
=== end content
|
512
|
+
|
513
|
+
Title: StylingAgent enhanceQuery adds multimodal input evaluation prompt when multimodal is enabled and multimodalInputType is screenshot
|
514
|
+
Content:
|
515
|
+
The user has provided you a screenshot of the page (as visible in the viewport) in base64-encoded format. You SHOULD use it while answering user's queries.
|
516
|
+
|
517
|
+
* Try to connect the screenshot to actual DOM elements in the page.
|
518
|
+
# Considerations for evaluating image:
|
519
|
+
* Pay close attention to the spatial details as well as the visual appearance of the selected element in the image, particularly in relation to layout, spacing, and styling.
|
520
|
+
* Analyze the image to identify the layout structure surrounding the element, including the positioning of neighboring elements.
|
521
|
+
* Extract visual information from the image, such as colors, fonts, spacing, and sizes, that might be relevant to the user's query.
|
522
|
+
* If the image suggests responsiveness issues (e.g., cropped content, overlapping elements), consider those in your response.
|
523
|
+
* Consider the surrounding elements and overall layout in the image, but prioritize the selected element's styling and positioning.
|
524
|
+
* **CRITICAL** When the user provides image input, interpret and use content and information from the image STRICTLY for web site debugging purposes.
|
525
|
+
|
526
|
+
* As part of THOUGHT, evaluate the image to gather data that might be needed to answer the question.
|
527
|
+
In case query is related to the image, ALWAYS first use image evaluation to get all details from the image. ONLY after you have all data needed from image, you should move to other steps.
|
528
|
+
|
529
|
+
# Inspected element
|
530
|
+
|
531
|
+
* Its selector is `div#myElement`
|
532
|
+
|
533
|
+
# User request
|
534
|
+
|
535
|
+
QUERY: test query
|
536
|
+
=== end content
|
537
|
+
|
538
|
+
Title: StylingAgent enhanceQuery adds multimodal input evaluation prompt when multimodal is enabled and multimodalInputType is uploaded image
|
539
|
+
Content:
|
540
|
+
The user has uploaded an image in base64-encoded format. You SHOULD use it while answering user's queries.
|
541
|
+
# Considerations for evaluating image:
|
542
|
+
* Pay close attention to the spatial details as well as the visual appearance of the selected element in the image, particularly in relation to layout, spacing, and styling.
|
543
|
+
* Analyze the image to identify the layout structure surrounding the element, including the positioning of neighboring elements.
|
544
|
+
* Extract visual information from the image, such as colors, fonts, spacing, and sizes, that might be relevant to the user's query.
|
545
|
+
* If the image suggests responsiveness issues (e.g., cropped content, overlapping elements), consider those in your response.
|
546
|
+
* Consider the surrounding elements and overall layout in the image, but prioritize the selected element's styling and positioning.
|
547
|
+
* **CRITICAL** When the user provides image input, interpret and use content and information from the image STRICTLY for web site debugging purposes.
|
548
|
+
|
549
|
+
* As part of THOUGHT, evaluate the image to gather data that might be needed to answer the question.
|
550
|
+
In case query is related to the image, ALWAYS first use image evaluation to get all details from the image. ONLY after you have all data needed from image, you should move to other steps.
|
551
|
+
|
552
|
+
# Inspected element
|
553
|
+
|
554
|
+
* Its selector is `div#myElement`
|
555
|
+
|
556
|
+
# User request
|
557
|
+
|
558
|
+
QUERY: test query
|
559
|
+
=== end content
|
@@ -192,6 +192,7 @@ function createLanternRequest(
|
|
192
192
|
priority: request.args.data.priority,
|
193
193
|
frameId: request.args.data.frame,
|
194
194
|
fromWorker,
|
195
|
+
serverResponseTime: request.args.data.lrServerResponseTime ?? undefined,
|
195
196
|
// Set later.
|
196
197
|
redirects: undefined,
|
197
198
|
redirectSource: undefined,
|
@@ -295,6 +295,7 @@ export async function finalize(): Promise<void> {
|
|
295
295
|
*
|
296
296
|
* See `_updateTimingsForLightrider` in Lighthouse for more detail.
|
297
297
|
*/
|
298
|
+
let lrServerResponseTime;
|
298
299
|
if (isLightrider && request.receiveResponse?.args.data.headers) {
|
299
300
|
timing = {
|
300
301
|
requestTime: Helpers.Timing.microToSeconds(request.sendRequests.at(0)?.ts ?? 0 as Types.Timing.Micro),
|
@@ -330,6 +331,14 @@ export async function finalize(): Promise<void> {
|
|
330
331
|
timing.connectEnd = TCPMs as Types.Timing.Milli;
|
331
332
|
timing.sslEnd = TCPMs as Types.Timing.Milli;
|
332
333
|
}
|
334
|
+
|
335
|
+
// Lightrider does not have any equivalent for `sendEnd` timing values. The
|
336
|
+
// closest we can get to the server response time is from a header that
|
337
|
+
// Lightrider sets.
|
338
|
+
const ResponseMsHeader = request.receiveResponse.args.data.headers.find(h => h.name === 'X-ResponseMs');
|
339
|
+
if (ResponseMsHeader) {
|
340
|
+
lrServerResponseTime = Math.max(0, parseInt(ResponseMsHeader.value, 10)) as Types.Timing.Milli;
|
341
|
+
}
|
333
342
|
}
|
334
343
|
|
335
344
|
// TODO: consider allowing chrome / about.
|
@@ -526,6 +535,7 @@ export async function finalize(): Promise<void> {
|
|
526
535
|
initiator: finalSendRequest.args.data.initiator,
|
527
536
|
stackTrace: finalSendRequest.args.data.stackTrace,
|
528
537
|
timing,
|
538
|
+
lrServerResponseTime,
|
529
539
|
url,
|
530
540
|
failed: request.resourceFinish?.args.data.didFail ?? false,
|
531
541
|
finished: Boolean(request.resourceFinish),
|
@@ -95,15 +95,14 @@ export type DocumentLatencyInsightModel = InsightModel<typeof UIStrings, {
|
|
95
95
|
},
|
96
96
|
}>;
|
97
97
|
|
98
|
-
function getServerResponseTime(
|
99
|
-
|
100
|
-
//
|
101
|
-
//
|
102
|
-
//
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
return lanternRequest.serverResponseTime as Types.Timing.Milli;
|
98
|
+
function getServerResponseTime(request: Types.Events.SyntheticNetworkRequest): Types.Timing.Milli|null {
|
99
|
+
// For technical reasons, Lightrider does not have `sendEnd` timing values. The
|
100
|
+
// closest we can get to the server response time is from a header that Lightrider
|
101
|
+
// sets.
|
102
|
+
// @ts-expect-error
|
103
|
+
const isLightrider = globalThis.isLightrider;
|
104
|
+
if (isLightrider) {
|
105
|
+
return request.args.data.lrServerResponseTime ?? null;
|
107
106
|
}
|
108
107
|
|
109
108
|
const timing = request.args.data.timing;
|
@@ -202,7 +201,7 @@ export function generateInsight(
|
|
202
201
|
return finalize({warnings: [InsightWarning.NO_DOCUMENT_REQUEST]});
|
203
202
|
}
|
204
203
|
|
205
|
-
const serverResponseTime = getServerResponseTime(documentRequest
|
204
|
+
const serverResponseTime = getServerResponseTime(documentRequest);
|
206
205
|
if (serverResponseTime === null) {
|
207
206
|
throw new Error('missing document request timing');
|
208
207
|
}
|
@@ -124,10 +124,10 @@ export interface TraceFrame {
|
|
124
124
|
processId: ProcessID;
|
125
125
|
url: string;
|
126
126
|
parent?: string;
|
127
|
-
// Added to
|
127
|
+
// Added to Chrome in April 2024:
|
128
128
|
// crrev.com/c/5424783
|
129
129
|
isOutermostMainFrame?: boolean;
|
130
|
-
// Added to
|
130
|
+
// Added to Chrome in June 2024:
|
131
131
|
// crrev.com/c/5595033
|
132
132
|
isInPrimaryMainFrame?: boolean;
|
133
133
|
}
|
@@ -415,6 +415,8 @@ export interface SyntheticNetworkRequest extends Complete, SyntheticBased<Phase.
|
|
415
415
|
initiator?: Initiator,
|
416
416
|
requestMethod?: string,
|
417
417
|
timing?: ResourceReceiveResponseTimingData,
|
418
|
+
/** Server response time according to Lightrider. */
|
419
|
+
lrServerResponseTime?: Milli,
|
418
420
|
},
|
419
421
|
};
|
420
422
|
cat: 'loading';
|
@@ -549,7 +551,6 @@ export function isAuctionWorkletDoneWithProcess(event: Event): event is AuctionW
|
|
549
551
|
* consuming screenshot events from the ScreenshotHandler, you must make sure
|
550
552
|
* to have your code deal with the two different formats.
|
551
553
|
*/
|
552
|
-
// These are nullable because in January 2025 a CL in Chromium
|
553
554
|
export interface LegacyScreenshot extends Event {
|
554
555
|
/**
|
555
556
|
* @deprecated This value is incorrect. Use ScreenshotHandler.getPresentationTimestamp()
|
@@ -745,7 +746,7 @@ export interface LargestContentfulPaintCandidate extends Mark {
|
|
745
746
|
nodeId: Protocol.DOM.BackendNodeId,
|
746
747
|
loadingAttr: string,
|
747
748
|
type?: string,
|
748
|
-
// Landed in
|
749
|
+
// Landed in Chrome M140: crrev.com/c/6702010
|
749
750
|
nodeName?: string,
|
750
751
|
},
|
751
752
|
};
|
@@ -990,7 +991,7 @@ export interface SyntheticLayoutShift extends Omit<LayoutShift, 'name'>, Synthet
|
|
990
991
|
export const NO_NAVIGATION = 'NO_NAVIGATION';
|
991
992
|
|
992
993
|
/**
|
993
|
-
* This maybe be a navigation id string from
|
994
|
+
* This maybe be a navigation id string from Chrome, or `NO_NAVIGATION`, which represents the
|
994
995
|
* portion of the trace for which we don't have any navigation event for (as it happeneded prior
|
995
996
|
* to the trace start).
|
996
997
|
*/
|
@@ -173,17 +173,17 @@ export interface ViewInput {
|
|
173
173
|
filterKeys: string[];
|
174
174
|
filter: string;
|
175
175
|
parseFilter: (filter: string) => TextUtils.TextUtils.ParsedFilter[];
|
176
|
-
onRecord: (
|
176
|
+
onRecord: (record: boolean) => void;
|
177
177
|
onClear: () => void;
|
178
178
|
onSave: () => void;
|
179
|
-
onSplitChange: (
|
179
|
+
onSplitChange: (onlyMain: boolean) => void;
|
180
180
|
onSelect: (e: CustomEvent<HTMLElement|null>) => void;
|
181
181
|
onContextMenu: (e: CustomEvent<{menu: UI.ContextMenu.ContextMenu, element: HTMLElement}>) => void;
|
182
|
-
onFilterChanged: (
|
183
|
-
onCommandChange: (
|
184
|
-
onCommandSubmitted: (
|
185
|
-
onTargetChange: (
|
186
|
-
onToggleSidebar: (
|
182
|
+
onFilterChanged: (filter: string) => void;
|
183
|
+
onCommandChange: (command: string) => void;
|
184
|
+
onCommandSubmitted: (input: string) => void;
|
185
|
+
onTargetChange: (targetId: string) => void;
|
186
|
+
onToggleSidebar: () => void;
|
187
187
|
targets: SDK.Target.Target[];
|
188
188
|
selectedTargetId: string;
|
189
189
|
}
|
@@ -203,7 +203,7 @@ export const DEFAULT_VIEW: View = (input, output, target) => {
|
|
203
203
|
direction="column"
|
204
204
|
sidebar-initial-size="400"
|
205
205
|
sidebar-visibility=${input.sidebarVisible ? 'visible' : 'hidden'}
|
206
|
-
@change=${input.onSplitChange}>
|
206
|
+
@change=${(e: CustomEvent<string>) => input.onSplitChange(e.detail === 'OnlyMain')}>
|
207
207
|
<div slot="main" class="vbox protocol-monitor-main">
|
208
208
|
<devtools-toolbar class="protocol-monitor-toolbar"
|
209
209
|
jslog=${VisualLogging.toolbar('top')}>
|
@@ -214,22 +214,23 @@ export const DEFAULT_VIEW: View = (input, output, target) => {
|
|
214
214
|
.variant=${Buttons.Button.Variant.ICON_TOGGLE}
|
215
215
|
.toggleType=${Buttons.Button.ToggleType.RED}
|
216
216
|
.toggled=${true}
|
217
|
-
@click=${input.onRecord}
|
217
|
+
@click=${(e: Event) => input.onRecord((e.target as Buttons.Button.Button).toggled)}>
|
218
|
+
</devtools-button>
|
218
219
|
<devtools-button title=${i18nString(UIStrings.clearAll)}
|
219
220
|
.iconName=${'clear'}
|
220
221
|
.variant=${Buttons.Button.Variant.TOOLBAR}
|
221
222
|
.jslogContext=${'protocol-monitor.clear-all'}
|
222
|
-
@click=${input.onClear}></devtools-button>
|
223
|
+
@click=${() => input.onClear()}></devtools-button>
|
223
224
|
<devtools-button title=${i18nString(UIStrings.save)}
|
224
225
|
.iconName=${'download'}
|
225
226
|
.variant=${Buttons.Button.Variant.TOOLBAR}
|
226
227
|
.jslogContext=${'protocol-monitor.save'}
|
227
|
-
@click=${input.onSave}></devtools-button>
|
228
|
+
@click=${() => input.onSave()}></devtools-button>
|
228
229
|
<devtools-toolbar-input type="filter"
|
229
230
|
list="filter-suggestions"
|
230
231
|
style="flex-grow: 1"
|
231
232
|
value=${input.filter}
|
232
|
-
@change=${input.onFilterChanged}>
|
233
|
+
@change=${(e: Event) => input.onFilterChanged((e.target as HTMLInputElement).value)}>
|
233
234
|
<datalist id="filter-suggestions">
|
234
235
|
${input.filterKeys.map(key => html`
|
235
236
|
<option value=${key + ':'}></option>
|
@@ -320,7 +321,7 @@ export const DEFAULT_VIEW: View = (input, output, target) => {
|
|
320
321
|
.iconName=${input.sidebarVisible ? 'left-panel-close' : 'left-panel-open'}
|
321
322
|
.variant=${Buttons.Button.Variant.TOOLBAR}
|
322
323
|
.jslogContext=${'protocol-monitor.toggle-command-editor'}
|
323
|
-
@click=${input.onToggleSidebar}></devtools-button>
|
324
|
+
@click=${() => input.onToggleSidebar()}></devtools-button>
|
324
325
|
</devtools-button>
|
325
326
|
<devtools-toolbar-input id="command-input"
|
326
327
|
style=${styleMap({
|
@@ -330,8 +331,8 @@ export const DEFAULT_VIEW: View = (input, output, target) => {
|
|
330
331
|
list="command-input-suggestions"
|
331
332
|
placeholder=${i18nString(UIStrings.sendRawCDPCommand)}
|
332
333
|
title=${i18nString(UIStrings.sendRawCDPCommandExplanation)}
|
333
|
-
@change=${input.onCommandChange}
|
334
|
-
@submit=${input.onCommandSubmitted}>
|
334
|
+
@change=${(e: Event) => input.onCommandChange((e.target as HTMLInputElement).value)}
|
335
|
+
@submit=${(e: Event) => input.onCommandSubmitted((e.target as HTMLInputElement).value)}>
|
335
336
|
<datalist id="command-input-suggestions">
|
336
337
|
${input.commandSuggestions.map(c => html`<option value=${c}></option>`)}
|
337
338
|
</datalist>
|
@@ -340,7 +341,7 @@ export const DEFAULT_VIEW: View = (input, output, target) => {
|
|
340
341
|
title=${i18nString(UIStrings.selectTarget)}
|
341
342
|
style=${styleMap({display: input.sidebarVisible ? 'none' : 'flex'})}
|
342
343
|
jslog=${VisualLogging.dropDown('target-selector').track({change: true})}
|
343
|
-
@change=${input.onTargetChange}>
|
344
|
+
@change=${(e: Event) => input.onTargetChange((e.target as HTMLSelectElement).value)}>
|
344
345
|
${input.targets.map(target => html`
|
345
346
|
<option jslog=${VisualLogging.item('target').track({click: true})}
|
346
347
|
value=${target.id()} ?selected=${target.id() === input.selectedTargetId}>
|
@@ -416,8 +417,8 @@ export class ProtocolMonitorImpl extends UI.Panel.Panel {
|
|
416
417
|
filterKeys: this.#filterKeys,
|
417
418
|
filter: this.#filter,
|
418
419
|
parseFilter: this.filterParser.parse.bind(this.filterParser),
|
419
|
-
onSplitChange: (
|
420
|
-
if (
|
420
|
+
onSplitChange: (onlyMain: boolean) => {
|
421
|
+
if (onlyMain) {
|
421
422
|
this.#populateToolbarInput();
|
422
423
|
this.#sidebarVisible = false;
|
423
424
|
} else {
|
@@ -427,8 +428,8 @@ export class ProtocolMonitorImpl extends UI.Panel.Panel {
|
|
427
428
|
}
|
428
429
|
this.requestUpdate();
|
429
430
|
},
|
430
|
-
onRecord: (
|
431
|
-
this.setRecording(
|
431
|
+
onRecord: (recording: boolean) => {
|
432
|
+
this.setRecording(recording);
|
432
433
|
},
|
433
434
|
onClear: () => {
|
434
435
|
this.#messages = [];
|
@@ -449,24 +450,22 @@ export class ProtocolMonitorImpl extends UI.Panel.Panel {
|
|
449
450
|
this.#populateContextMenu(e.detail.menu, message);
|
450
451
|
}
|
451
452
|
},
|
452
|
-
onCommandChange: (
|
453
|
-
this.#command =
|
453
|
+
onCommandChange: (command: string) => {
|
454
|
+
this.#command = command;
|
454
455
|
},
|
455
|
-
onCommandSubmitted: (
|
456
|
-
this.#commandAutocompleteSuggestionProvider.addEntry(
|
457
|
-
const {command, parameters} = parseCommandInput(
|
456
|
+
onCommandSubmitted: (input: string) => {
|
457
|
+
this.#commandAutocompleteSuggestionProvider.addEntry(input);
|
458
|
+
const {command, parameters} = parseCommandInput(input);
|
458
459
|
this.onCommandSend(command, parameters, this.#selectedTargetId);
|
459
460
|
},
|
460
|
-
onFilterChanged: (
|
461
|
-
this.#filter =
|
461
|
+
onFilterChanged: (filter: string) => {
|
462
|
+
this.#filter = filter;
|
462
463
|
this.requestUpdate();
|
463
464
|
},
|
464
|
-
onTargetChange: (
|
465
|
-
|
466
|
-
this.#selectedTargetId = e.target.value;
|
467
|
-
}
|
465
|
+
onTargetChange: (targetId: string) => {
|
466
|
+
this.#selectedTargetId = targetId;
|
468
467
|
},
|
469
|
-
onToggleSidebar: (
|
468
|
+
onToggleSidebar: () => {
|
470
469
|
this.#sidebarVisible = !this.#sidebarVisible;
|
471
470
|
this.requestUpdate();
|
472
471
|
},
|
@@ -1430,6 +1430,23 @@ export class TimelineFlameChartView extends Common.ObjectWrapper.eventMixin<Even
|
|
1430
1430
|
}
|
1431
1431
|
}
|
1432
1432
|
|
1433
|
+
zoomEvent(event: Trace.Types.Events.Event): void {
|
1434
|
+
const traceBounds = TraceBounds.TraceBounds.BoundsManager.instance().state()?.micro.entireTraceBounds;
|
1435
|
+
if (!traceBounds) {
|
1436
|
+
return;
|
1437
|
+
}
|
1438
|
+
|
1439
|
+
this.#expandEntryTrack(event);
|
1440
|
+
this.revealEventVertically(event);
|
1441
|
+
const entryWindow = Trace.Helpers.Timing.traceWindowFromMicroSeconds(
|
1442
|
+
event.ts,
|
1443
|
+
Trace.Types.Timing.Micro(event.ts + (event.dur ?? 0)),
|
1444
|
+
);
|
1445
|
+
const expandedBounds = Trace.Helpers.Timing.expandWindowByPercentOrToOneMillisecond(entryWindow, traceBounds, 100);
|
1446
|
+
TraceBounds.TraceBounds.BoundsManager.instance().setTimelineVisibleWindow(
|
1447
|
+
expandedBounds, {ignoreMiniMapBounds: true, shouldAnimate: true});
|
1448
|
+
}
|
1449
|
+
|
1433
1450
|
revealEvent(event: Trace.Types.Events.Event): void {
|
1434
1451
|
const mainIndex = this.mainDataProvider.indexForEvent(event);
|
1435
1452
|
const networkIndex = this.networkDataProvider.indexForEvent(event);
|
@@ -660,6 +660,10 @@ export class TimelinePanel extends Common.ObjectWrapper.eventMixin<EventTypes, t
|
|
660
660
|
);
|
661
661
|
}
|
662
662
|
|
663
|
+
zoomEvent(event: Trace.Types.Events.Event): void {
|
664
|
+
this.flameChart.zoomEvent(event);
|
665
|
+
}
|
666
|
+
|
663
667
|
/**
|
664
668
|
* Activates an insight and ensures the sidebar is open too.
|
665
669
|
* Pass `highlightInsight: true` to flash the insight with the background highlight colour.
|
@@ -3156,6 +3160,7 @@ export const rowHeight = 18;
|
|
3156
3160
|
export const headerHeight = 20;
|
3157
3161
|
export interface TimelineModeViewDelegate {
|
3158
3162
|
select(selection: TimelineSelection|null): void;
|
3163
|
+
zoomEvent(event: Trace.Types.Events.Event): void;
|
3159
3164
|
element: Element;
|
3160
3165
|
set3PCheckboxDisabled(disabled: boolean): void;
|
3161
3166
|
selectEntryAtTime(events: Trace.Types.Events.Event[]|null, time: number): void;
|
@@ -967,7 +967,7 @@ export class TimelineUIUtils {
|
|
967
967
|
const isMarker = parsedTrace && isMarkerEvent(parsedTrace, event);
|
968
968
|
const color = isMarker ? TimelineUIUtils.markerStyleForEvent(event).color : defaultColorForEvent;
|
969
969
|
|
970
|
-
contentHelper.addSection(TimelineUIUtils.eventTitle(event), color);
|
970
|
+
contentHelper.addSection(TimelineUIUtils.eventTitle(event), color, event);
|
971
971
|
|
972
972
|
// TODO: as part of the removal of the old engine, produce a typesafe way
|
973
973
|
// to look up args and data for events.
|
@@ -2374,7 +2374,7 @@ export class TimelineDetailsContentHelper {
|
|
2374
2374
|
this.fragment.appendChild(this.element);
|
2375
2375
|
}
|
2376
2376
|
|
2377
|
-
addSection(title: string, swatchColor?: string): void {
|
2377
|
+
addSection(title: string, swatchColor?: string, event?: Trace.Types.Events.Event): void {
|
2378
2378
|
if (!this.tableElement.hasChildNodes()) {
|
2379
2379
|
this.element.removeChildren();
|
2380
2380
|
} else {
|
@@ -2388,7 +2388,16 @@ export class TimelineDetailsContentHelper {
|
|
2388
2388
|
if (swatchColor) {
|
2389
2389
|
titleElement.createChild('div').style.backgroundColor = swatchColor;
|
2390
2390
|
}
|
2391
|
-
|
2391
|
+
|
2392
|
+
const textChild = titleElement.createChild('span');
|
2393
|
+
textChild.textContent = title;
|
2394
|
+
|
2395
|
+
if (event) {
|
2396
|
+
textChild.classList.add('timeline-details-chip-title-reveal-entry');
|
2397
|
+
textChild.addEventListener('click', function() {
|
2398
|
+
TimelinePanel.instance().zoomEvent(event);
|
2399
|
+
});
|
2400
|
+
}
|
2392
2401
|
}
|
2393
2402
|
|
2394
2403
|
this.tableElement = this.element.createChild('div', 'vbox timeline-details-chip-body');
|
@@ -49,10 +49,6 @@ const UIStrings = {
|
|
49
49
|
* @description Text for the save trace button
|
50
50
|
*/
|
51
51
|
saveButtonTitle: 'Save',
|
52
|
-
/**
|
53
|
-
* @description Title for the information icon showing more information about an option
|
54
|
-
*/
|
55
|
-
moreInfoTitle: 'More information',
|
56
52
|
/**
|
57
53
|
* @description Text shown in the information pop-up next to the "Include script content" option.
|
58
54
|
*/
|
@@ -60,7 +56,11 @@ const UIStrings = {
|
|
60
56
|
/**
|
61
57
|
* @description Text shown in the information pop-up next to the "Include script sourcemaps" option.
|
62
58
|
*/
|
63
|
-
sourceMapsContentPrivacyInfo: 'Includes available source maps, which may expose authored code.'
|
59
|
+
sourceMapsContentPrivacyInfo: 'Includes available source maps, which may expose authored code.',
|
60
|
+
/**
|
61
|
+
* @description Text used as the start of the accessible label for the information button which shows additional context when the user focuses / hovers.
|
62
|
+
*/
|
63
|
+
moreInfoLabel: 'Additional information:',
|
64
64
|
} as const;
|
65
65
|
|
66
66
|
const str_ = i18n.i18n.registerUIStrings('panels/timeline/components/ExportTraceOptions.ts', UIStrings);
|
@@ -204,6 +204,17 @@ export class ExportTraceOptions extends HTMLElement {
|
|
204
204
|
this.state = newState;
|
205
205
|
}
|
206
206
|
|
207
|
+
#accessibleLabelForInfoCheckbox(checkboxId: CheckboxId): string {
|
208
|
+
if (checkboxId === 'script-source-maps') {
|
209
|
+
return i18nString(UIStrings.moreInfoLabel) + ' ' + i18nString(UIStrings.sourceMapsContentPrivacyInfo);
|
210
|
+
}
|
211
|
+
|
212
|
+
if (checkboxId === 'script-content') {
|
213
|
+
return i18nString(UIStrings.moreInfoLabel) + ' ' + i18nString(UIStrings.scriptContentPrivacyInfo);
|
214
|
+
}
|
215
|
+
|
216
|
+
return '';
|
217
|
+
}
|
207
218
|
#renderCheckbox(
|
208
219
|
checkboxId: CheckboxId, checkboxWithLabel: UI.UIUtils.CheckboxLabel, title: Common.UIString.LocalizedString,
|
209
220
|
checked: boolean): Lit.TemplateResult {
|
@@ -224,8 +235,8 @@ export class ExportTraceOptions extends HTMLElement {
|
|
224
235
|
${checkboxesWithInfoDialog.has(checkboxId) ? html`
|
225
236
|
<devtools-button
|
226
237
|
aria-details=${`export-trace-tooltip-${checkboxId}`}
|
238
|
+
aria-label=${this.#accessibleLabelForInfoCheckbox(checkboxId)}
|
227
239
|
class="pen-icon"
|
228
|
-
.title=${UIStrings.moreInfoTitle}
|
229
240
|
.iconName=${'info'}
|
230
241
|
.variant=${Buttons.Button.Variant.ICON}
|
231
242
|
></devtools-button>
|
@@ -275,8 +286,10 @@ export class ExportTraceOptions extends HTMLElement {
|
|
275
286
|
closeButton: false,
|
276
287
|
dialogTitle: i18nString(UIStrings.exportTraceOptionsDialogTitle),
|
277
288
|
state: this.#state.dialogState,
|
289
|
+
closeOnESC: true,
|
278
290
|
} as Dialogs.ButtonDialog.ButtonDialogData}>
|
279
291
|
<div class='export-trace-options-content'>
|
292
|
+
|
280
293
|
${this.#state.displayAnnotationsCheckbox ? this.#renderCheckbox('annotations', this.#includeAnnotationsCheckbox,
|
281
294
|
i18nString(UIStrings.includeAnnotations),
|
282
295
|
this.#state.includeAnnotations): ''}
|
@@ -296,11 +309,10 @@ export class ExportTraceOptions extends HTMLElement {
|
|
296
309
|
} as Buttons.Button.ButtonData}
|
297
310
|
>${i18nString(UIStrings.saveButtonTitle)}</devtools-button>
|
298
311
|
</div>
|
312
|
+
${this.#state.displayScriptContentCheckbox ? this.#renderInfoTooltip('script-content') : Lit.nothing}
|
313
|
+
${this.#state.displayScriptContentCheckbox && this.#state.displaySourceMapsCheckbox ? this.#renderInfoTooltip('script-source-maps') : Lit.nothing}
|
299
314
|
</div>
|
300
315
|
</devtools-button-dialog>
|
301
|
-
|
302
|
-
${this.#state.displayScriptContentCheckbox ? this.#renderInfoTooltip('script-content') : Lit.nothing}
|
303
|
-
${this.#state.displayScriptContentCheckbox && this.#state.displaySourceMapsCheckbox ? this.#renderInfoTooltip('script-source-maps') : Lit.nothing}
|
304
316
|
`;
|
305
317
|
// clang-format on
|
306
318
|
Lit.render(output, this.#shadow, {host: this});
|
@@ -47,6 +47,11 @@
|
|
47
47
|
align-items: center;
|
48
48
|
}
|
49
49
|
|
50
|
+
.timeline-details-chip-title-reveal-entry:hover {
|
51
|
+
background: var(--sys-color-state-hover-on-subtle);
|
52
|
+
cursor: pointer;
|
53
|
+
}
|
54
|
+
|
50
55
|
.timeline-details-view-block:first-child > .timeline-details-chip-title {
|
51
56
|
font-size: 13px;
|
52
57
|
}
|
@@ -1,7 +1,7 @@
|
|
1
1
|
Name: Dependencies sourced from the upstream `chromium` repository
|
2
2
|
URL: https://source.chromium.org/chromium/chromium/src/+/main:components/variations/proto/devtools/
|
3
3
|
Version: N/A
|
4
|
-
Revision:
|
4
|
+
Revision: c7c3037a902ef52ab055674cd106721ea54d13d1
|
5
5
|
Update Mechanism: Manual (https://crbug.com/428069060)
|
6
6
|
License: BSD-3-Clause
|
7
7
|
License File: LICENSE
|
@@ -480,7 +480,13 @@ export class Tooltip extends HTMLElement {
|
|
480
480
|
};
|
481
481
|
|
482
482
|
#keyDown = (event: KeyboardEvent): void => {
|
483
|
-
|
483
|
+
// There are two scenarios when we care about keydown:
|
484
|
+
// 1. The tooltip is open, and the user presses ESC
|
485
|
+
// 2. "use-hotkey" is set, and the user presses Alt+ArrowDown.
|
486
|
+
const shouldToggleVisibility =
|
487
|
+
(event.key === 'Escape' && this.open) || (this.useHotkey && event.altKey && event.key === 'ArrowDown');
|
488
|
+
|
489
|
+
if (shouldToggleVisibility) {
|
484
490
|
this.#openedViaHotkey = !this.open;
|
485
491
|
this.toggle();
|
486
492
|
event.consume(true);
|
@@ -489,15 +495,18 @@ export class Tooltip extends HTMLElement {
|
|
489
495
|
|
490
496
|
#registerEventListeners(): void {
|
491
497
|
if (this.#anchor) {
|
498
|
+
// We bind the keydown listener regardless of if use-hotkey is enabled
|
499
|
+
// as we always want to support ESC to close.
|
500
|
+
this.#anchor.addEventListener('keydown', this.#keyDown);
|
501
|
+
|
492
502
|
if (this.useClick) {
|
493
503
|
this.#anchor.addEventListener('click', this.toggle);
|
494
504
|
} else {
|
495
505
|
this.#anchor.addEventListener('mouseenter', this.showTooltip);
|
496
|
-
if (this.useHotkey) {
|
497
|
-
this.#anchor.addEventListener('keydown', this.#keyDown);
|
498
|
-
} else {
|
506
|
+
if (!this.useHotkey) {
|
499
507
|
this.#anchor.addEventListener('focus', this.showTooltip);
|
500
508
|
}
|
509
|
+
|
501
510
|
this.#anchor.addEventListener('blur', this.hideTooltip);
|
502
511
|
this.#anchor.addEventListener('mouseleave', this.hideTooltip);
|
503
512
|
this.addEventListener('mouseleave', this.hideTooltip);
|
package/package.json
CHANGED