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.
@@ -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
- * isolate is a `uint64_t`, which is too much for JS number to represent exactly.
39
- * TODO: consider adjusting the trace event impl to either string or reduced precision. see https://crrev.com/c/6300647
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
- isolate: number,
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
- isolate?: number,
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://goo.gle/chrome-insecure-origins for more details.",
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://goo.gle/chrome-insecure-origins for more details.",
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://goo.gle/chrome-insecure-origins for more details.",
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://goo.gle/chrome-insecure-origins for more details.",
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
@@ -7982,7 +7982,7 @@ export const NativeFunctions = [
7982
7982
  },
7983
7983
  {
7984
7984
  name: "constant",
7985
- signatures: [["tensor"],["desc","buffer"]]
7985
+ signatures: [["tensor"],["desc","buffer"],["type","value"]]
7986
7986
  },
7987
7987
  {
7988
7988
  name: "argMin",
@@ -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
- request: Types.Events.SyntheticNetworkRequest, context: InsightSetContext): Types.Timing.Milli|null {
100
- // Prefer the value as given by the Lantern provider.
101
- // For PSI, Lighthouse uses this to set a better value for the server response
102
- // time. For technical reasons, in Lightrider we do not have `sendEnd` timing
103
- // values. See Lighthouse's `asLanternNetworkRequest` function for more.
104
- const lanternRequest = context.navigation && context.lantern?.requests.find(r => r.rawRequest === request);
105
- if (lanternRequest?.serverResponseTime !== undefined) {
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, context);
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 Chromium in April 2024:
127
+ // Added to Chrome in April 2024:
128
128
  // crrev.com/c/5424783
129
129
  isOutermostMainFrame?: boolean;
130
- // Added to Chromium in June 2024:
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 Chromium M140: crrev.com/c/6702010
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 Chromium, or `NO_NAVIGATION`, which represents the
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: (e: Event) => void;
176
+ onRecord: (record: boolean) => void;
177
177
  onClear: () => void;
178
178
  onSave: () => void;
179
- onSplitChange: (e: CustomEvent<string>) => void;
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: (e: CustomEvent<string>) => void;
183
- onCommandChange: (e: CustomEvent<string>) => void;
184
- onCommandSubmitted: (e: CustomEvent<string>) => void;
185
- onTargetChange: (e: Event) => void;
186
- onToggleSidebar: (e: Event) => void;
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}></devtools-button>
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: (e: CustomEvent<string>) => {
420
- if (e.detail === 'OnlyMain') {
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: (e: Event) => {
431
- this.setRecording((e.target as Buttons.Button.Button).toggled);
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: (e: CustomEvent<string>) => {
453
- this.#command = e.detail;
453
+ onCommandChange: (command: string) => {
454
+ this.#command = command;
454
455
  },
455
- onCommandSubmitted: (e: CustomEvent<string>) => {
456
- this.#commandAutocompleteSuggestionProvider.addEntry(e.detail);
457
- const {command, parameters} = parseCommandInput(e.detail);
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: (e: CustomEvent<string>) => {
461
- this.#filter = e.detail;
461
+ onFilterChanged: (filter: string) => {
462
+ this.#filter = filter;
462
463
  this.requestUpdate();
463
464
  },
464
- onTargetChange: (e: Event) => {
465
- if (e.target instanceof HTMLSelectElement) {
466
- this.#selectedTargetId = e.target.value;
467
- }
465
+ onTargetChange: (targetId: string) => {
466
+ this.#selectedTargetId = targetId;
468
467
  },
469
- onToggleSidebar: (_e: Event) => {
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
- UI.UIUtils.createTextChild(titleElement, title);
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: e8bb55e7f56140e285b00abfd3811f1c3656ca4c
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
- if ((event.altKey && event.key === 'ArrowDown') || (event.key === 'Escape' && this.open)) {
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
@@ -102,5 +102,5 @@
102
102
  "@eslint/core": "0.15.1"
103
103
  }
104
104
  },
105
- "version": "1.0.1522145"
105
+ "version": "1.0.1522585"
106
106
  }