chrome-devtools-frontend 1.0.1153166 → 1.0.1155899

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.
Files changed (61) hide show
  1. package/config/gni/devtools_grd_files.gni +3 -2
  2. package/front_end/core/common/Revealer.ts +1 -1
  3. package/front_end/core/host/InspectorFrontendHostAPI.ts +2 -0
  4. package/front_end/core/host/UserMetrics.ts +32 -12
  5. package/front_end/core/platform/array-utilities.ts +25 -9
  6. package/front_end/core/sdk/ChildTargetManager.ts +2 -1
  7. package/front_end/core/sdk/FilmStripModel.ts +35 -25
  8. package/front_end/devtools_compatibility.js +2 -0
  9. package/front_end/entrypoints/lighthouse_worker/LighthouseWorkerService.ts +8 -2
  10. package/front_end/entrypoints/worker_app/worker_app.ts +0 -1
  11. package/front_end/generated/InspectorBackendCommands.js +2 -1
  12. package/front_end/generated/protocol.ts +36 -0
  13. package/front_end/models/bindings/PresentationConsoleMessageHelper.ts +90 -73
  14. package/front_end/models/issues_manager/IssuesManager.ts +5 -0
  15. package/front_end/models/issues_manager/SourceFrameIssuesManager.ts +31 -61
  16. package/front_end/models/issues_manager/StylesheetLoadingIssue.ts +69 -0
  17. package/front_end/models/issues_manager/descriptions/stylesheetLateImport.md +4 -0
  18. package/front_end/models/issues_manager/descriptions/stylesheetRequestFailed.md +3 -0
  19. package/front_end/models/issues_manager/issues_manager.ts +2 -0
  20. package/front_end/models/timeline_model/TimelineModel.ts +4 -0
  21. package/front_end/models/trace/ModelImpl.ts +1 -0
  22. package/front_end/models/trace/README.md +73 -17
  23. package/front_end/models/trace/handlers/NetworkRequestsHandler.ts +1 -1
  24. package/front_end/models/trace/handlers/RendererHandler.ts +33 -143
  25. package/front_end/models/trace/handlers/UserTimings.md +1 -1
  26. package/front_end/models/trace/types/TraceEvents.ts +3 -2
  27. package/front_end/panels/application/ApplicationPanelSidebar.ts +9 -6
  28. package/front_end/panels/application/PreloadingTreeElement.ts +25 -7
  29. package/front_end/panels/application/preloading/PreloadingView.ts +64 -31
  30. package/front_end/panels/application/preloading/components/UsedPreloadingView.ts +19 -9
  31. package/front_end/panels/console/ConsoleViewMessage.ts +14 -2
  32. package/front_end/panels/elements/ElementsPanel.ts +2 -3
  33. package/front_end/panels/elements/components/LayoutPane.ts +256 -60
  34. package/front_end/panels/elements/elements-legacy.ts +0 -3
  35. package/front_end/panels/elements/elements-meta.ts +10 -2
  36. package/front_end/panels/elements/elements.ts +0 -2
  37. package/front_end/panels/network/NetworkDataGridNode.ts +8 -0
  38. package/front_end/panels/network/NetworkLogView.ts +2 -1
  39. package/front_end/panels/network/NetworkPanel.ts +12 -1
  40. package/front_end/panels/recorder/components/ExtensionView.ts +1 -1
  41. package/front_end/panels/sources/DebuggerPlugin.ts +7 -4
  42. package/front_end/panels/sources/SourcesPanel.ts +1 -1
  43. package/front_end/panels/sources/components/BreakpointsView.ts +406 -89
  44. package/front_end/panels/sources/sources-meta.ts +13 -4
  45. package/front_end/panels/sources/sources.ts +0 -2
  46. package/front_end/panels/timeline/TimelineFlameChartDataProvider.ts +1 -1
  47. package/front_end/panels/timeline/TimelineFlameChartNetworkDataProvider.ts +106 -95
  48. package/front_end/panels/timeline/TimelineFlameChartView.ts +1 -1
  49. package/front_end/panels/timeline/TimelinePaintProfilerView.ts +5 -0
  50. package/front_end/panels/timeline/TimelinePanel.ts +8 -8
  51. package/front_end/ui/legacy/UIUtils.ts +1 -1
  52. package/front_end/ui/legacy/components/perf_ui/.eslintrc.js +18 -0
  53. package/front_end/ui/legacy/components/perf_ui/FlameChart.ts +5 -1
  54. package/front_end/ui/legacy/components/perf_ui/TimelineOverviewPane.ts +5 -2
  55. package/front_end/ui/legacy/components/utils/Linkifier.ts +10 -0
  56. package/package.json +1 -1
  57. package/scripts/build/generate_deprecations.py +3 -0
  58. package/front_end/panels/elements/LayoutSidebarPane.ts +0 -249
  59. package/front_end/panels/sources/BreakpointsSidebarPane.ts +0 -480
  60. package/front_end/ui/components/docs/layout_pane/basic.html +0 -25
  61. package/front_end/ui/components/docs/layout_pane/basic.ts +0 -78
@@ -2,29 +2,89 @@
2
2
 
3
3
  This folder contains the new trace engine that was first implemented for the Performance Insights panel and is now being repurposed as the primary trace engine that we use within DevTools.
4
4
 
5
+ ## High level architecture
6
+
7
+ ```
8
+ ┌──────────────┐
9
+ │ Model#parse ├───┐
10
+ └──────────────┘ │
11
+
12
+ ┌──────────▼──────────┐
13
+ │async processor#parse│
14
+ └──────────┬──────────┘
15
+
16
+ ┌──────────▼────────────┐
17
+ │for handler of handlers│
18
+ └───┬────────────────┬──┘
19
+ │ │
20
+ ┌────────────────▼────┐ ┌─────▼────────────────┐
21
+ │NetworkRequestHandler│ │...many more handlers │
22
+ │ │ │ │
23
+ │ reset() │ │ │
24
+ │ initialize() │ │ │
25
+ │ │ │ │
26
+ │ handleEvent() │ │ │
27
+ │ │ │ │
28
+ │ finalize() │ │ │
29
+ │ │ │ │
30
+ │ data() │ │ │ │
31
+ └─────────────────────┘ │ └──────────────────────┘
32
+
33
+
34
+ ┌──────────────────▼─────────────────┐
35
+ │const data = model.traceParsedData()│
36
+ └────────────────────────────────────┘
37
+ ```
38
+
39
+ `Model#parse` is the entrypoint into the engine and is the public interface that consumers use to initiate tracing and to fetch data back.
40
+
41
+ All the processing is done by the `Processor`. The processor contains a series of *Handlers*, each of which is responsible for parsing events of a particular category.
42
+
43
+ The trace processor loops over every event in the trace and calls each handler in turn (done this way so we only loop over the trace file once, rather than doing it once-per-handler). A Handler is a file that exposes a set of methods, most importantly `handleEvent()` and `data()`. The `handleEvent` function will be called for each event in the trace, and it is up to an individual handler to do something with that event if it needs to. The `data` method should return the final data that has been parsed and generated by the handler.
44
+
45
+ Once processing is done (read on for more details on how to track this), you can use the `traceParsedData()` method to fetch the result of parsing a given trace.
46
+
47
+ ## Enabled handlers and creating a model
48
+
49
+ At the time of writing (06 June 2023) we are midway through a migration of the Performanc Panel's trace engine from the SDK.TracingModel to the new TraceEngine. Consequently, not all of the TraceEngine's handlers are currently active, because we don't want to run expensive handlers whose data we do not yet use in the UI.
50
+
51
+ Therefore, to create a model instance, we use `Model.createWithRequiredHandlersForMigration()`, which initializes a model configured correctly with the right handlers.
52
+
53
+ Once the migration is done, we can swap to `Model.createWithAllHandlers()` and remove the code that enables a subset of the handlers to run.
54
+
55
+ If you want to strictly control the set of handlers that are run (for example, if you only want to run one particular handler), you can initialize the model yourself and pass in the set of handlers:
56
+
57
+ ```ts
58
+ const model = new Model({
59
+ NetworkRequestHandler: Handlers.ModelHandlers.NetworkRequestHandler,
60
+ })
61
+ ```
62
+
5
63
  ## Parsing a trace and getting data back
6
64
 
7
65
  Once you have an instance of the model, you can call the `parse` method to take a set of raw events and parse them. Once parsed, you then have to call the `traceParsedData` method, providing an index of the trace you want to have the data for. This is because any model can store a number of traces. Each trace is given an index, which starts at 0 and increments by one as a new trace is parsed.
8
66
 
9
67
  If you are managing multiple traces, you should store them in some form of indexed data structure so you can easily know which index to use to fetch any data from the model. You may delete a trace with `deleteTraceByIndex`, which will then update the indexes of all other traces too.
10
68
 
11
- ## Updates from the model
69
+ If you need to check how many traces you have, you can call `model.size()`. The latest trace's index is therefore always `model.size() - 1`.
70
+
71
+ ## Waiting for updates from the model
12
72
 
13
73
  When you call `parse` you have two options. You can `await` it, which will wait until the trace is fully parsed:
14
74
 
15
- ```
75
+ ```ts
16
76
  await this.model.parse();
17
77
  ```
18
78
 
19
79
  But it's likely preferable to instead use events, to avoid blocking the UI whilst parsing is in progress. You can listen to the `ModelUpdateEvent` for updates:
20
80
 
21
- ```
81
+ ```ts
22
82
  this.model.addEventListener(Model.ModelUpdateEvent.eventName, event => {
23
83
  const {data} = event as Model.ModelUpdateEvent;
24
84
 
25
85
  if (data.data === 'done') {
26
86
  // trace is complete
27
- const newestData = this.model.traceParsedData(this.model.getRecordingsAvailable().length - 1);
87
+ const newestData = this.model.traceParsedData(this.model.size() - 1);
28
88
  } else {
29
89
  // data.data will be an object: { index: X, total: Y}, which represents how many events (X) have been processed out of a total (Y).
30
90
  // This can be used to show a progress bar, for example.
@@ -32,18 +92,14 @@ this.model.addEventListener(Model.ModelUpdateEvent.eventName, event => {
32
92
  })
33
93
  ```
34
94
 
35
- ## Handlers
95
+ ## The structure of the final data object
36
96
 
37
- Trace Parsing is split into a series of Handlers. The trace processor loops over every event in the trace and calls each handler in turn (done this way so we only loop over the trace file once, rather than doing it once-per-handler). A Handler is a file that exposes a set of methods, most importantly `handleEvent()` and `data()`. The `handleEvent` function will be called for each event in the trace, and it is up to an individual handler to do something with that event if it needs to. The `data` method should return the final data that has been parsed and generated by the handler.
97
+ The object returned from `traceParsedData()` is an object of key-value pairs where each key is the name of a handler, and the value is the data that was parsed and returned from that handler.
38
98
 
39
- Each handler can be individually unit tested by feeding it events, and asserting on the final result of its `data()` function.
40
-
41
- The final data object that is returned by the `Processor` contains the result of calling `data()` on each handler. The key of each item in the object will be the name of the handler. Once this data is returned, it is considered immutable and should not be changed.
42
-
43
- ## Running in a worker
44
-
45
- When you create an instance of the Trace Model it will automatically create an instance of the trace worker. This worker is where the actual parsing is done. This is to ensure that parsing a large trace does not cause the main thread to freeze. The fact that the tracing occurs in a different thread is an implementation detail and should not impact how you interface with the trace model itself.
46
-
47
- When you call `parse()`, the model will post a message to the worker. This starts processing, and sends messages back. `models/trace/worker/Types.ts` contains the messages that can be sent to and from the worker.
48
-
49
- The model listens to these messages and will deal with them. If you want to listen out for updates from the model, you can listen to the `ModelUpdateEvent`, which will be emitted periodically with progress stats, and finally once a trace is complete.
99
+ ```ts
100
+ {
101
+ NetworkRequestHandler: ReturnType<typeof NetworkRequestHandler['data']>,
102
+ LayoutShiftHandler: ReturnType<typeof LayoutShiftHandler['data']>,
103
+ // and so on for each enabled Handler
104
+ }
105
+ ```
@@ -196,7 +196,7 @@ export async function finalize(): Promise<void> {
196
196
  Types.Timing.MicroSeconds(request.willSendRequests[request.willSendRequests.length - 1].ts) :
197
197
  Types.Timing.MicroSeconds(finalSendRequest.ts);
198
198
 
199
- // Finsh time and end time
199
+ // Finish time and end time
200
200
  // =======================
201
201
  // The finish time and the end time are subtly different.
202
202
  // - Finish time: records the point at which the network stack stopped receiving the data
@@ -6,18 +6,15 @@ import * as Platform from '../../../core/platform/platform.js';
6
6
  import * as Helpers from '../helpers/helpers.js';
7
7
 
8
8
  import {data as metaHandlerData} from './MetaHandler.js';
9
- import {
10
- buildStackTraceAsCallFramesFromId,
11
- data as samplesData,
12
- getAllHotFunctionsBetweenTimestamps,
13
- } from './SamplesHandler.js';
14
9
 
15
10
  import {KnownEventName, KNOWN_EVENTS, type TraceEventHandlerName, HandlerState} from './types.js';
16
11
  import * as Types from '../types/types.js';
17
12
 
18
13
  const processes = new Map<Types.TraceEvents.ProcessID, RendererProcess>();
19
- const traceEventToNode = new Map<RendererEvent, RendererEventNode>();
20
- const allRendererEvents: RendererEvent[] = [];
14
+ const traceEventToNode = new Map<RendererTraceEvent, RendererEventNode>();
15
+ const allRendererEvents: RendererTraceEvent[] = [];
16
+ let nodeIdCount = 0;
17
+ const makeRendererEventNodeId = (): RendererEventNodeId => (++nodeIdCount) as RendererEventNodeId;
21
18
 
22
19
  let handlerState = HandlerState.UNINITIALIZED;
23
20
 
@@ -38,18 +35,14 @@ const makeEmptyRendererEventTree = (): RendererEventTree => ({
38
35
  maxDepth: 0,
39
36
  });
40
37
 
41
- const makeEmptyRendererEventNode = (eventIndex: number): RendererEventNode => ({
42
- eventIndex,
38
+ const makeEmptyRendererEventNode = (event: RendererTraceEvent, id: RendererEventNodeId): RendererEventNode => ({
39
+ event,
40
+ id,
43
41
  parentId: null,
44
42
  childrenIds: new Set(),
45
43
  depth: 0,
46
44
  });
47
45
 
48
- const makeRendererEventNodeIdGenerator = (): () => RendererEventNodeId => {
49
- let i = 0;
50
- return (): RendererEventNodeId => i++ as RendererEventNodeId;
51
- };
52
-
53
46
  const getOrCreateRendererProcess =
54
47
  (processes: Map<Types.TraceEvents.ProcessID, RendererProcess>, pid: Types.TraceEvents.ProcessID):
55
48
  RendererProcess => {
@@ -64,6 +57,7 @@ export function reset(): void {
64
57
  processes.clear();
65
58
  traceEventToNode.clear();
66
59
  allRendererEvents.length = 0;
60
+ nodeIdCount = -1;
67
61
  handlerState = HandlerState.UNINITIALIZED;
68
62
  }
69
63
 
@@ -292,69 +286,46 @@ export function buildHierarchy(
292
286
  * Complexity: O(n), where n = number of events
293
287
  */
294
288
  export function treify(
295
- events: RendererEvent[], options: {filter: {has: (name: KnownEventName) => boolean}}): RendererEventTree {
289
+ events: RendererTraceEvent[], options: {filter: {has: (name: KnownEventName) => boolean}}): RendererEventTree {
296
290
  const stack = [];
291
+ // Reset the node id counter for every new renderer.
292
+ nodeIdCount = -1;
297
293
  const tree = makeEmptyRendererEventTree();
298
- const makeRendererEventNodeId = makeRendererEventNodeIdGenerator();
299
- let lastScheduleStyleRecalcEvent: RendererEvent|null = null;
300
- let lastInvalidateLayout: RendererEvent|null = null;
301
- let lastForcedStyleRecalc: RendererEvent|null = null;
302
-
303
294
  for (let i = 0; i < events.length; i++) {
304
295
  const event = events[i];
305
296
 
306
- buildHotFunctionsStackTracesForTask(event);
307
-
308
- if (event.name === KnownEventName.ScheduleStyleRecalculation) {
309
- lastScheduleStyleRecalcEvent = event;
310
- }
311
- if (event.name === KnownEventName.InvalidateLayout) {
312
- lastInvalidateLayout = event;
313
- }
314
- if (event.name === KnownEventName.RecalculateStyles) {
315
- lastForcedStyleRecalc = event;
316
- }
317
297
  // If the current event should not be part of the tree, then simply proceed
318
298
  // with the next event.
319
299
  if (!options.filter.has(event.name as KnownEventName)) {
320
300
  continue;
321
301
  }
322
302
 
323
- const duration = Types.TraceEvents.isTraceEventInstant(event) ? 0 : event.dur;
303
+ const duration = event.dur || 0;
304
+ const nodeId = makeRendererEventNodeId();
305
+ const node = makeEmptyRendererEventNode(event, nodeId);
324
306
 
325
307
  // If the parent stack is empty, then the current event is a root. Create a
326
308
  // node for it, mark it as a root, then proceed with the next event.
327
309
  if (stack.length === 0) {
328
- const node = makeEmptyRendererEventNode(i);
329
- const nodeId = makeRendererEventNodeId();
330
310
  tree.nodes.set(nodeId, node);
331
311
  tree.roots.add(nodeId);
332
- event.totalTime = Types.Timing.MicroSeconds(duration);
333
312
  event.selfTime = Types.Timing.MicroSeconds(duration);
334
- stack.push(nodeId);
313
+ stack.push(node);
335
314
  tree.maxDepth = Math.max(tree.maxDepth, stack.length);
336
315
  traceEventToNode.set(event, node);
337
316
  continue;
338
317
  }
339
318
 
340
- const parentNodeId = stack[stack.length - 1];
341
- if (parentNodeId === undefined) {
342
- throw new Error('Impossible: no parent node id found in the stack');
319
+ const parentNode = stack.at(-1);
320
+ if (parentNode === undefined) {
321
+ throw new Error('Impossible: no parent node found in the stack');
343
322
  }
344
323
 
345
- const parentNode = tree.nodes.get(parentNodeId);
346
- if (!parentNode) {
347
- throw new Error('Impossible: no parent node exists for the given id');
348
- }
349
-
350
- const parentEvent = events[parentNode.eventIndex];
351
- if (!parentEvent) {
352
- throw new Error('Impossible: no parent event exists for the given node');
353
- }
324
+ const parentEvent = parentNode.event;
354
325
 
355
326
  const begin = event.ts;
356
327
  const parentBegin = parentEvent.ts;
357
- const parentDuration = Types.TraceEvents.isTraceEventInstant(parentEvent) ? 0 : parentEvent.dur;
328
+ const parentDuration = parentEvent.dur || 0;
358
329
  const end = begin + duration;
359
330
  const parentEnd = parentBegin + parentDuration;
360
331
  // Check the relationship between the parent event at the top of the stack,
@@ -378,6 +349,8 @@ export function treify(
378
349
  if (startsAfterParent) {
379
350
  stack.pop();
380
351
  i--;
352
+ // The last created node has been discarded, so discard this id.
353
+ nodeIdCount--;
381
354
  continue;
382
355
  }
383
356
 
@@ -392,105 +365,21 @@ export function treify(
392
365
  // contained within the parent event. Create a node for the current
393
366
  // event, establish the parent/child relationship, then proceed with the
394
367
  // next event.
395
- const node = makeEmptyRendererEventNode(i);
396
- const nodeId = makeRendererEventNodeId();
397
368
  tree.nodes.set(nodeId, node);
398
369
  node.depth = stack.length;
399
- node.parentId = parentNodeId;
370
+ node.parentId = parentNode.id;
400
371
  parentNode.childrenIds.add(nodeId);
401
372
  event.selfTime = Types.Timing.MicroSeconds(duration);
402
- event.totalTime = Types.Timing.MicroSeconds(duration);
403
373
  if (parentEvent.selfTime !== undefined) {
404
- parentEvent.selfTime = Types.Timing.MicroSeconds(parentEvent.selfTime - event.totalTime);
374
+ parentEvent.selfTime = Types.Timing.MicroSeconds(parentEvent.selfTime - (event.dur || 0));
405
375
  }
406
- stack.push(nodeId);
376
+ stack.push(node);
407
377
  tree.maxDepth = Math.max(tree.maxDepth, stack.length);
408
378
  traceEventToNode.set(event, node);
409
- // For checking if an event is a forced layout, this point in the process
410
- // is the perfect spot.
411
- // An event is a forced layout if it is in a subset of all events (see
412
- // EVENTS_THAT_MAY_BE_FORCED_LAYOUTS constant) AND if its parent event is
413
- // not a RunTask. Rather than navigate the tree and perform this check
414
- // later on, we can do this at this point, where we already have done the
415
- // work to calculate the event and its parent event.
416
- checkIfEventIsForcedLayoutAndStore(
417
- event, lastScheduleStyleRecalcEvent, lastInvalidateLayout, lastForcedStyleRecalc);
418
379
  }
419
380
  return tree;
420
381
  }
421
382
 
422
- function checkIfEventIsForcedLayoutAndStore(
423
- event: RendererEvent, lastScheduleStyleRecalcEvent: RendererEvent|null, lastInvalidateLayout: RendererEvent|null,
424
- lastForcedStyleRecalc: RendererEvent|null): void {
425
- // Forced re relayout
426
- if (FORCED_LAYOUT_EVENT_NAMES.has(event.name as KnownEventName)) {
427
- if (lastInvalidateLayout) {
428
- // By default, the initiator of a forced re layout is the last InvalidateLayout event.
429
- event.initiator = lastInvalidateLayout;
430
- }
431
-
432
- // However if the last InvalidateLayout ended before the last forced styles recalc,
433
- // set the initiator to be the last ScheduleStylesRecalc.
434
- const lastForcedStyleEndTime = lastForcedStyleRecalc && lastForcedStyleRecalc.ts + (lastForcedStyleRecalc.dur || 0);
435
- const hasInitiator = lastScheduleStyleRecalcEvent && lastInvalidateLayout && lastForcedStyleEndTime;
436
- if (hasInitiator && lastForcedStyleEndTime > lastInvalidateLayout.ts) {
437
- event.initiator = lastScheduleStyleRecalcEvent;
438
- }
439
- }
440
-
441
- // Forced styles recalc
442
- if (FORCED_RECALC_STYLE_EVENTS.has(event.name as KnownEventName)) {
443
- lastForcedStyleRecalc = event;
444
- if (lastScheduleStyleRecalcEvent) {
445
- // By default, the initiator of a forced styles recalc is the last ScheduleStylesRecalc event.
446
- event.initiator = lastScheduleStyleRecalcEvent;
447
- }
448
- }
449
- }
450
-
451
- function eventIsLongTask(event: RendererEvent, mustBeOnMainFrame: boolean): boolean {
452
- if (event.name !== KnownEventName.RunTask) {
453
- return false;
454
- }
455
- const eventProcess = processes.get(event.pid);
456
- const isOnMainFrame = Boolean(eventProcess && eventProcess.isOnMainFrame);
457
- if (!isOnMainFrame && mustBeOnMainFrame) {
458
- return false;
459
- }
460
-
461
- const errorTimeThreshold = Helpers.Timing.millisecondsToMicroseconds(Types.Timing.MilliSeconds(50));
462
- const eventDuration = Types.TraceEvents.isTraceEventInstant(event) ? 0 : event.dur;
463
- return eventDuration > errorTimeThreshold;
464
- }
465
-
466
- export function buildHotFunctionsStackTracesForTask(task: RendererEvent): void {
467
- if (!eventIsLongTask(task, true)) {
468
- // Don't spend time computing this data for tasks that aren't long.
469
- return;
470
- }
471
- const {processes} = samplesData();
472
- const thread = processes.get(task.pid)?.threads.get(task.tid);
473
- // This threshold is temporarily set to 0 until so that at the moment
474
- // we want to always show what functions were executed in a long task.
475
- // In the future however, as we provide more sophisticated details
476
- // for long tasks, we should set the threshold to a more valuable value.
477
- const HOT_FUNCTION_MIN_SELF_PERCENTAGE = 0;
478
- const calls = thread?.calls;
479
- const taskStart = task.ts;
480
- const taskEnd = Types.Timing.MicroSeconds(task.ts + (task.dur || 0));
481
- const hotFunctions =
482
- calls && getAllHotFunctionsBetweenTimestamps(calls, taskStart, taskEnd, HOT_FUNCTION_MIN_SELF_PERCENTAGE);
483
- const tree = thread?.tree;
484
- if (!hotFunctions || !tree) {
485
- return;
486
- }
487
- // Store the top 10 hot functions.
488
- const MAX_HOT_FUNCTION_COUNT = 10;
489
- task.hotFunctionsStackTraces =
490
- hotFunctions.slice(0, MAX_HOT_FUNCTION_COUNT)
491
- .map(hotFunction => buildStackTraceAsCallFramesFromId(tree, hotFunction.stackFrame.nodeId).reverse());
492
- }
493
-
494
383
  export const FORCED_LAYOUT_EVENT_NAMES = new Set([
495
384
  KnownEventName.Layout,
496
385
  ]);
@@ -506,8 +395,8 @@ export function deps(): TraceEventHandlerName[] {
506
395
 
507
396
  export interface RendererHandlerData {
508
397
  processes: Map<Types.TraceEvents.ProcessID, RendererProcess>;
509
- traceEventToNode: Map<RendererEvent, RendererEventNode>;
510
- allRendererEvents: RendererEvent[];
398
+ traceEventToNode: Map<RendererTraceEvent, RendererEventNode>;
399
+ allRendererEvents: RendererTraceEvent[];
511
400
  }
512
401
 
513
402
  export interface RendererProcess {
@@ -520,18 +409,18 @@ export interface RendererProcess {
520
409
 
521
410
  export interface RendererThread {
522
411
  name: string|null;
523
- events: RendererEvent[];
412
+ events: RendererTraceEvent[];
524
413
  tree?: RendererEventTree;
525
414
  }
526
415
 
527
416
  interface RendererEventData {
528
417
  selfTime: Types.Timing.MicroSeconds;
529
- totalTime: Types.Timing.MicroSeconds;
530
- initiator: RendererEvent;
418
+ initiator: RendererTraceEvent;
419
+ parent?: RendererTraceEvent;
531
420
  hotFunctionsStackTraces: Types.TraceEvents.TraceEventCallFrame[][];
532
421
  }
533
422
 
534
- export type RendererEvent = Types.TraceEvents.TraceEventRendererData&Partial<RendererEventData>;
423
+ export type RendererTraceEvent = Types.TraceEvents.TraceEventRendererData&Partial<RendererEventData>;
535
424
 
536
425
  export interface RendererEventTree {
537
426
  nodes: Map<RendererEventNodeId, RendererEventNode>;
@@ -540,8 +429,9 @@ export interface RendererEventTree {
540
429
  }
541
430
 
542
431
  export interface RendererEventNode {
543
- eventIndex: number;
432
+ event: RendererTraceEvent;
544
433
  depth: number;
434
+ id: RendererEventNodeId;
545
435
  parentId?: RendererEventNodeId|null;
546
436
  childrenIds: Set<RendererEventNodeId>;
547
437
  }
@@ -20,7 +20,7 @@ Events that represent a `performance.measure` call will come in pairs: a begin e
20
20
 
21
21
  ### Parsing out user timings
22
22
 
23
- For `performance.mark` calls, the handler looks for events with the user timing category and the `Mark` trace event phase ('R'). These events are then filtered to exclude timings appendend by Chrome (1).
23
+ For `performance.mark` calls, the handler looks for events with the user timing category and the `Mark` trace event phase (`I`). However, pre-June 2023, these are emitted with phase `R`. These events are then filtered to exclude timings appendend by Chrome (1).
24
24
 
25
25
  For `performance.measure` calls, the handler looks for begin or end events in the trace. Once we have these, we then pair them up, and create a list of synthetic user timing events. These are events that do not actually exist in the trace, but we create because we need one event to map to each block. The event is made up of information from the begin and/or end event and includes most crucially:
26
26
 
@@ -738,7 +738,7 @@ export interface TraceEventTimeStamp extends TraceEventData {
738
738
 
739
739
  export interface TraceEventPerformanceMark extends TraceEventData {
740
740
  cat: 'blink.user_timing';
741
- ph: Phase.MARK;
741
+ ph: Phase.INSTANT|Phase.MARK;
742
742
  id: string;
743
743
  }
744
744
 
@@ -1057,7 +1057,8 @@ export function isTraceEventPerformanceMeasure(traceEventData: TraceEventData):
1057
1057
 
1058
1058
  export function isTraceEventPerformanceMark(traceEventData: TraceEventData):
1059
1059
  traceEventData is TraceEventPerformanceMark {
1060
- return traceEventData.ph === Phase.MARK && traceEventData.cat === 'blink.user_timing';
1060
+ return (traceEventData.ph === Phase.MARK || traceEventData.ph === Phase.INSTANT) &&
1061
+ traceEventData.cat === 'blink.user_timing';
1061
1062
  }
1062
1063
 
1063
1064
  export function isTraceEventConsoleTime(traceEventData: TraceEventData): traceEventData is TraceEventConsoleTimeBegin|
@@ -50,6 +50,7 @@ import {BackgroundServiceModel} from './BackgroundServiceModel.js';
50
50
  import {BackgroundServiceView} from './BackgroundServiceView.js';
51
51
  import {BounceTrackingMitigationsTreeElement} from './BounceTrackingMitigationsTreeElement.js';
52
52
  import * as ApplicationComponents from './components/components.js';
53
+ import {type PreloadingResultView, type PreloadingView} from './preloading/PreloadingView.js';
53
54
  import {PreloadingTreeElement} from './PreloadingTreeElement.js';
54
55
  import resourcesSidebarStyles from './resourcesSidebar.css.js';
55
56
  import {ServiceWorkerCacheTreeElement} from './ServiceWorkerCacheTreeElement.js';
@@ -264,7 +265,8 @@ export class ApplicationPanelSidebar extends UI.Widget.VBox implements SDK.Targe
264
265
  periodicBackgroundSyncTreeElement: BackgroundServiceTreeElement;
265
266
  pushMessagingTreeElement: BackgroundServiceTreeElement;
266
267
  reportingApiTreeElement: ReportingApiTreeElement;
267
- preloadingTreeElement: PreloadingTreeElement|undefined;
268
+ preloadingTreeElement: PreloadingTreeElement<PreloadingView>|undefined;
269
+ preloadingResultTreeElement: PreloadingTreeElement<PreloadingResultView>|undefined;
268
270
  private readonly resourcesSection: ResourcesSection;
269
271
  private readonly databaseTableViews: Map<DatabaseModelDatabase, {
270
272
  [x: string]: DatabaseTableView,
@@ -400,8 +402,10 @@ export class ApplicationPanelSidebar extends UI.Widget.VBox implements SDK.Targe
400
402
  const preloadingSectionTitle = i18nString(UIStrings.preloading);
401
403
  const preloadingSectionTreeElement = this.addSidebarSection(preloadingSectionTitle);
402
404
 
403
- this.preloadingTreeElement = new PreloadingTreeElement(panel);
405
+ this.preloadingTreeElement = PreloadingTreeElement.newForPreloadingView(panel);
406
+ this.preloadingResultTreeElement = PreloadingTreeElement.newForPreloadingResultView(panel);
404
407
  preloadingSectionTreeElement.appendChild(this.preloadingTreeElement);
408
+ preloadingSectionTreeElement.appendChild(this.preloadingResultTreeElement);
405
409
  }
406
410
 
407
411
  const resourcesSectionTitle = i18nString(UIStrings.frames);
@@ -561,12 +565,11 @@ export class ApplicationPanelSidebar extends UI.Widget.VBox implements SDK.Targe
561
565
  this.periodicBackgroundSyncTreeElement.initialize(backgroundServiceModel);
562
566
  this.pushMessagingTreeElement.initialize(backgroundServiceModel);
563
567
 
564
- // The condition is equivalent to
565
- // `Root.Runtime.experiments.isEnabled(Root.Runtime.ExperimentName.PRELOADING_STATUS_PANEL)`.
566
- if (this.preloadingTreeElement) {
568
+ if (Root.Runtime.experiments.isEnabled(Root.Runtime.ExperimentName.PRELOADING_STATUS_PANEL)) {
567
569
  const preloadingModel = this.target?.model(SDK.PreloadingModel.PreloadingModel);
568
570
  if (preloadingModel) {
569
- this.preloadingTreeElement.initialize(preloadingModel);
571
+ this.preloadingTreeElement?.initialize(preloadingModel);
572
+ this.preloadingResultTreeElement?.initialize(preloadingModel);
570
573
  }
571
574
  }
572
575
  }
@@ -10,24 +10,42 @@ import type * as Platform from '../../core/platform/platform.js';
10
10
  import {ApplicationPanelTreeElement} from './ApplicationPanelTreeElement.js';
11
11
  import {type ResourcesPanel} from './ResourcesPanel.js';
12
12
 
13
- import {PreloadingView} from './preloading/PreloadingView.js';
13
+ import {PreloadingView, PreloadingResultView} from './preloading/PreloadingView.js';
14
14
 
15
15
  const UIStrings = {
16
16
  /**
17
17
  *@description Text in Application Panel Sidebar of the Application panel
18
18
  */
19
19
  prefetchingAndPrerendering: 'Prefetching & Prerendering',
20
+ /**
21
+ *@description Text in Application Panel Sidebar of the Application panel
22
+ */
23
+ thisPage: 'This Page',
20
24
  };
21
25
  const str_ = i18n.i18n.registerUIStrings('panels/application/PreloadingTreeElement.ts', UIStrings);
22
26
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
23
27
 
24
- export class PreloadingTreeElement extends ApplicationPanelTreeElement {
25
- private model?: SDK.PreloadingModel.PreloadingModel;
26
- private view?: PreloadingView;
28
+ type M = SDK.PreloadingModel.PreloadingModel;
29
+
30
+ export class PreloadingTreeElement<V extends PreloadingView|PreloadingResultView> extends ApplicationPanelTreeElement {
31
+ private model?: M;
32
+ private ctorV: {new(model: M): V};
33
+ private view?: V;
27
34
  #selectedInternal: boolean;
28
35
 
29
- constructor(resourcesPanel: ResourcesPanel) {
30
- super(resourcesPanel, i18nString(UIStrings.prefetchingAndPrerendering), false);
36
+ // TODO(https://crbug.com/1410709): Split this view into "SpeculationRules" and "Preload".
37
+ static newForPreloadingView(resourcesPanel: ResourcesPanel): PreloadingTreeElement<PreloadingView> {
38
+ return new PreloadingTreeElement(resourcesPanel, PreloadingView, i18nString(UIStrings.prefetchingAndPrerendering));
39
+ }
40
+
41
+ static newForPreloadingResultView(resourcesPanel: ResourcesPanel): PreloadingTreeElement<PreloadingResultView> {
42
+ return new PreloadingTreeElement(resourcesPanel, PreloadingResultView, i18nString(UIStrings.thisPage));
43
+ }
44
+
45
+ constructor(resourcesPanel: ResourcesPanel, ctorV: {new(model: M): V}, title: string) {
46
+ super(resourcesPanel, title, false);
47
+
48
+ this.ctorV = ctorV;
31
49
 
32
50
  const icon = UI.Icon.Icon.create('arrow-up-down', 'resource-tree-item');
33
51
  this.setLeadingIcons([icon]);
@@ -58,7 +76,7 @@ export class PreloadingTreeElement extends ApplicationPanelTreeElement {
58
76
  }
59
77
 
60
78
  if (!this.view) {
61
- this.view = new PreloadingView(this.model);
79
+ this.view = new this.ctorV(this.model);
62
80
  }
63
81
 
64
82
  this.showView(this.view);