lighthouse 9.5.0-dev.20230124 → 9.5.0-dev.20230126

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 (77) hide show
  1. package/core/audits/accessibility/accesskeys.js +1 -1
  2. package/core/audits/accessibility/aria-allowed-attr.js +1 -1
  3. package/core/audits/accessibility/aria-command-name.js +1 -1
  4. package/core/audits/accessibility/aria-hidden-body.js +1 -1
  5. package/core/audits/accessibility/aria-hidden-focus.js +1 -1
  6. package/core/audits/accessibility/aria-input-field-name.js +1 -1
  7. package/core/audits/accessibility/aria-meter-name.js +1 -1
  8. package/core/audits/accessibility/aria-progressbar-name.js +1 -1
  9. package/core/audits/accessibility/aria-required-attr.js +1 -1
  10. package/core/audits/accessibility/aria-required-children.js +1 -1
  11. package/core/audits/accessibility/aria-required-parent.js +1 -1
  12. package/core/audits/accessibility/aria-roles.js +1 -1
  13. package/core/audits/accessibility/aria-toggle-field-name.js +1 -1
  14. package/core/audits/accessibility/aria-tooltip-name.js +1 -1
  15. package/core/audits/accessibility/aria-treeitem-name.js +1 -1
  16. package/core/audits/accessibility/aria-valid-attr-value.js +1 -1
  17. package/core/audits/accessibility/aria-valid-attr.js +1 -1
  18. package/core/audits/accessibility/button-name.js +1 -1
  19. package/core/audits/accessibility/bypass.js +1 -1
  20. package/core/audits/accessibility/color-contrast.js +1 -1
  21. package/core/audits/accessibility/definition-list.js +1 -1
  22. package/core/audits/accessibility/dlitem.js +1 -1
  23. package/core/audits/accessibility/document-title.js +1 -1
  24. package/core/audits/accessibility/duplicate-id-active.js +1 -1
  25. package/core/audits/accessibility/duplicate-id-aria.js +1 -1
  26. package/core/audits/accessibility/form-field-multiple-labels.js +1 -1
  27. package/core/audits/accessibility/frame-title.js +1 -1
  28. package/core/audits/accessibility/heading-order.js +1 -1
  29. package/core/audits/accessibility/html-has-lang.js +1 -1
  30. package/core/audits/accessibility/html-lang-valid.js +1 -1
  31. package/core/audits/accessibility/image-alt.js +1 -1
  32. package/core/audits/accessibility/input-image-alt.js +1 -1
  33. package/core/audits/accessibility/label.js +1 -1
  34. package/core/audits/accessibility/link-name.js +1 -1
  35. package/core/audits/accessibility/list.js +1 -1
  36. package/core/audits/accessibility/listitem.js +1 -1
  37. package/core/audits/accessibility/meta-refresh.js +1 -1
  38. package/core/audits/accessibility/meta-viewport.js +1 -1
  39. package/core/audits/accessibility/object-alt.js +1 -1
  40. package/core/audits/accessibility/tabindex.js +1 -1
  41. package/core/audits/accessibility/td-headers-attr.js +1 -1
  42. package/core/audits/accessibility/th-has-data-cells.js +1 -1
  43. package/core/audits/accessibility/valid-lang.js +1 -1
  44. package/core/audits/accessibility/video-caption.js +1 -1
  45. package/core/audits/byte-efficiency/legacy-javascript.js +1 -2
  46. package/core/audits/network-requests.js +7 -6
  47. package/core/audits/preload-lcp-image.d.ts +12 -3
  48. package/core/audits/preload-lcp-image.js +32 -13
  49. package/core/computed/entity-classification.js +1 -1
  50. package/core/computed/metrics/responsiveness.js +5 -2
  51. package/core/computed/page-dependency-graph.js +1 -1
  52. package/core/config/constants.d.ts +2 -2
  53. package/core/config/constants.js +2 -2
  54. package/core/gather/gatherers/accessibility.js +3 -1
  55. package/core/gather/gatherers/trace-elements.js +1 -1
  56. package/core/lib/network-request.d.ts +2 -0
  57. package/core/lib/network-request.js +3 -0
  58. package/core/lib/stack-packs.js +4 -0
  59. package/core/lib/tracehouse/trace-processor.d.ts +14 -5
  60. package/core/lib/tracehouse/trace-processor.js +85 -30
  61. package/core/lib/traces/metric-trace-events.js +1 -1
  62. package/dist/report/bundle.esm.js +1 -1
  63. package/dist/report/flow.js +1 -1
  64. package/dist/report/standalone.js +1 -1
  65. package/package.json +4 -5
  66. package/report/assets/styles.css +1 -1
  67. package/report/renderer/components.js +1 -1
  68. package/report/test/generator/report-generator-test.js +2 -2
  69. package/report/test/renderer/performance-category-renderer-test.js +2 -2
  70. package/shared/localization/locales/en-US.json +74 -44
  71. package/shared/localization/locales/en-XL.json +74 -44
  72. package/tsconfig.json +0 -2
  73. package/types/artifacts.d.ts +9 -2
  74. package/core/lib/minify-trace.d.ts +0 -6
  75. package/core/lib/minify-trace.js +0 -172
  76. package/core/util.cjs +0 -845
  77. package/core/util.d.cts +0 -342
@@ -11,6 +11,7 @@ import {MainResource} from '../computed/main-resource.js';
11
11
  import {LanternLargestContentfulPaint} from '../computed/metrics/lantern-largest-contentful-paint.js';
12
12
  import {LoadSimulator} from '../computed/load-simulator.js';
13
13
  import {ByteEfficiencyAudit} from './byte-efficiency/byte-efficiency-audit.js';
14
+ import {ProcessedNavigation} from '../computed/processed-navigation.js';
14
15
 
15
16
  const UIStrings = {
16
17
  /** Title of a lighthouse audit that tells a user to preload an image in order to improve their LCP time. */
@@ -36,8 +37,7 @@ class PreloadLCPImageAudit extends Audit {
36
37
  title: str_(UIStrings.title),
37
38
  description: str_(UIStrings.description),
38
39
  supportedModes: ['navigation'],
39
- requiredArtifacts: ['traces', 'devtoolsLogs', 'GatherContext', 'URL', 'TraceElements',
40
- 'ImageElements'],
40
+ requiredArtifacts: ['traces', 'devtoolsLogs', 'GatherContext', 'URL', 'TraceElements'],
41
41
  scoreDisplayMode: Audit.SCORING_MODES.NUMERIC,
42
42
  };
43
43
  }
@@ -87,17 +87,11 @@ class PreloadLCPImageAudit extends Audit {
87
87
  /**
88
88
  * @param {LH.Artifacts.NetworkRequest} mainResource
89
89
  * @param {LH.Gatherer.Simulation.GraphNode} graph
90
- * @param {LH.Artifacts.TraceElement} lcpElement
91
- * @param {Array<LH.Artifacts.ImageElement>} imageElements
90
+ * @param {string | undefined} lcpUrl
92
91
  * @return {{lcpNodeToPreload?: LH.Gatherer.Simulation.GraphNetworkNode, initiatorPath?: InitiatorPath}}
93
92
  */
94
- static getLCPNodeToPreload(mainResource, graph, lcpElement, imageElements) {
95
- const lcpImageElement = imageElements.find(elem => {
96
- return elem.node.devtoolsNodePath === lcpElement.node.devtoolsNodePath;
97
- });
98
-
99
- if (!lcpImageElement) return {};
100
- const lcpUrl = lcpImageElement.src;
93
+ static getLCPNodeToPreload(mainResource, graph, lcpUrl) {
94
+ if (!lcpUrl) return {};
101
95
  const {lcpNode, path} = PreloadLCPImageAudit.findLCPNode(graph, lcpUrl);
102
96
  if (!lcpNode || !path) return {};
103
97
 
@@ -116,6 +110,29 @@ class PreloadLCPImageAudit extends Audit {
116
110
  };
117
111
  }
118
112
 
113
+ /**
114
+ * Match the LCP event with the paint event to get the URL of the image actually painted.
115
+ * This could differ from the `ImageElement` associated with the nodeId if e.g. the LCP
116
+ * was a pseudo-element associated with a node containing a smaller background-image.
117
+ * @param {LH.Trace} trace
118
+ * @param {LH.Artifacts.ProcessedNavigation} processedNavigation
119
+ * @return {string | undefined}
120
+ */
121
+ static getLcpUrl(trace, processedNavigation) {
122
+ const lcpEvent = processedNavigation.largestContentfulPaintAllFramesEvt;
123
+ if (!lcpEvent) return;
124
+
125
+ const lcpImagePaintEvent = trace.traceEvents.filter(e => {
126
+ return e.name === 'LargestImagePaint::Candidate' &&
127
+ e.args.frame === lcpEvent.args.frame &&
128
+ e.args.data?.DOMNodeId === lcpEvent.args.data?.nodeId &&
129
+ e.args.data?.size === lcpEvent.args.data?.size;
130
+ // Get last candidate, in case there was more than one.
131
+ }).sort((a, b) => b.ts - a.ts)[0];
132
+
133
+ return lcpImagePaintEvent?.args.data?.imageUrl;
134
+ }
135
+
119
136
  /**
120
137
  * Computes the estimated effect of preloading the LCP image.
121
138
  * @param {LH.Artifacts.TraceElement} lcpElement
@@ -223,15 +240,17 @@ class PreloadLCPImageAudit extends Audit {
223
240
  return {score: null, notApplicable: true};
224
241
  }
225
242
 
226
- const [mainResource, lanternLCP, simulator] = await Promise.all([
243
+ const [processedNavigation, mainResource, lanternLCP, simulator] = await Promise.all([
244
+ ProcessedNavigation.request(trace, context),
227
245
  MainResource.request({devtoolsLog, URL}, context),
228
246
  LanternLargestContentfulPaint.request(metricData, context),
229
247
  LoadSimulator.request({devtoolsLog, settings: context.settings}, context),
230
248
  ]);
231
249
 
250
+ const lcpUrl = PreloadLCPImageAudit.getLcpUrl(trace, processedNavigation);
232
251
  const graph = lanternLCP.pessimisticGraph;
233
252
  // eslint-disable-next-line max-len
234
- const {lcpNodeToPreload, initiatorPath} = PreloadLCPImageAudit.getLCPNodeToPreload(mainResource, graph, lcpElement, artifacts.ImageElements);
253
+ const {lcpNodeToPreload, initiatorPath} = PreloadLCPImageAudit.getLCPNodeToPreload(mainResource, graph, lcpUrl);
235
254
 
236
255
  const {results, wastedMs} =
237
256
  PreloadLCPImageAudit.computeWasteWithGraph(lcpElement, lcpNodeToPreload, graph, simulator);
@@ -6,7 +6,7 @@
6
6
 
7
7
  import {makeComputedArtifact} from './computed-artifact.js';
8
8
  import {NetworkRecords} from './network-records.js';
9
- import {Util} from '../util.cjs';
9
+ import {Util} from '../../shared/util.js';
10
10
  import UrlUtils from '../lib/url-utils.js';
11
11
  import thirdPartyWeb from '../lib/third-party-web.js';
12
12
 
@@ -87,8 +87,11 @@ class Responsiveness {
87
87
  return evt.name === 'EventTiming' && evt.ph !== 'e';
88
88
  });
89
89
 
90
- if (candidates.length && !candidates.some(candidate => candidate.args.data?.frame)) {
91
- // Full EventTiming data added in https://crrev.com/c/3632661
90
+ // If trace is from < m103, the timestamps cannot be trusted, so we craft a fallback
91
+ // <m103 traces (bad) had a args.frame
92
+ // m103+ traces (good) have a args.data.frame (https://crrev.com/c/3632661)
93
+ // TODO(compat): remove FallbackTiming handling when we don't care about <m103
94
+ if (candidates.length && candidates.every(candidate => !candidate.args.data?.frame)) {
92
95
  return {
93
96
  name: 'FallbackTiming',
94
97
  duration: responsivenessEvent.args.data.maxDuration,
@@ -468,7 +468,7 @@ class PageDependencyGraph {
468
468
  * @return {URLArtifact}
469
469
  */
470
470
  static getDocumentUrls(devtoolsLog, networkRecords, processedTrace) {
471
- const mainFrameId = processedTrace.mainFrameIds.frameId;
471
+ const mainFrameId = processedTrace.mainFrameInfo.frameId;
472
472
 
473
473
  /** @type {string|undefined} */
474
474
  let requestedUrl;
@@ -80,7 +80,7 @@ declare const MOTOG4_EMULATION_METRICS: Required<LH.SharedFlagsSettings['screenE
80
80
  * @type {Required<LH.SharedFlagsSettings['screenEmulation']>}
81
81
  */
82
82
  declare const DESKTOP_EMULATION_METRICS: Required<LH.SharedFlagsSettings['screenEmulation']>;
83
- declare const MOTOG4_USERAGENT: "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4695.0 Mobile Safari/537.36 Chrome-Lighthouse";
84
- declare const DESKTOP_USERAGENT: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4695.0 Safari/537.36 Chrome-Lighthouse";
83
+ declare const MOTOG4_USERAGENT: "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Mobile Safari/537.36";
84
+ declare const DESKTOP_USERAGENT: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36";
85
85
  export {};
86
86
  //# sourceMappingURL=constants.d.ts.map
@@ -81,8 +81,8 @@ const screenEmulationMetrics = {
81
81
  };
82
82
 
83
83
 
84
- const MOTOG4_USERAGENT = 'Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4695.0 Mobile Safari/537.36 Chrome-Lighthouse'; // eslint-disable-line max-len
85
- const DESKTOP_USERAGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4695.0 Safari/537.36 Chrome-Lighthouse'; // eslint-disable-line max-len
84
+ const MOTOG4_USERAGENT = 'Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Mobile Safari/537.36'; // eslint-disable-line max-len
85
+ const DESKTOP_USERAGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36'; // eslint-disable-line max-len
86
86
 
87
87
  const userAgents = {
88
88
  mobile: MOTOG4_USERAGENT,
@@ -61,10 +61,12 @@ async function runA11yChecks() {
61
61
  'frame-focusable-content': {enabled: false},
62
62
  'aria-roledescription': {enabled: false},
63
63
  'scrollable-region-focusable': {enabled: false},
64
- // TODO(paulirish): create audits and enable these 3.
64
+ // TODO(paulirish): create audits and enable these 5.
65
65
  'input-button-name': {enabled: false},
66
66
  'role-img-alt': {enabled: false},
67
67
  'select-name': {enabled: false},
68
+ 'link-in-text-block': {enabled: false},
69
+ 'frame-title-unique': {enabled: false},
68
70
  },
69
71
  });
70
72
 
@@ -231,7 +231,7 @@ class TraceElements extends FRGatherer {
231
231
  }
232
232
 
233
233
  // These should exist, but trace types are loose.
234
- const lcpData = processedNavigation.largestContentfulPaintEvt?.args?.data;
234
+ const lcpData = processedNavigation.largestContentfulPaintAllFramesEvt?.args?.data;
235
235
  if (lcpData?.nodeId === undefined || !lcpData.type) return;
236
236
 
237
237
  return {
@@ -86,6 +86,8 @@ export class NetworkRequest {
86
86
  isValid: boolean;
87
87
  parsedURL: ParsedURL;
88
88
  documentURL: string;
89
+ /** When the renderer process initially discovers a network request, in milliseconds. */
90
+ rendererStartTime: number;
89
91
  /**
90
92
  * When the network service is about to handle a request, ie. just before going to the
91
93
  * HTTP cache or going to the network for DNS/connection setup, in milliseconds.
@@ -125,6 +125,8 @@ class NetworkRequest {
125
125
  this.parsedURL = /** @type {ParsedURL} */ ({scheme: ''});
126
126
  this.documentURL = '';
127
127
 
128
+ /** When the renderer process initially discovers a network request, in milliseconds. */
129
+ this.rendererStartTime = -1;
128
130
  /**
129
131
  * When the network service is about to handle a request, ie. just before going to the
130
132
  * HTTP cache or going to the network for DNS/connection setup, in milliseconds.
@@ -222,6 +224,7 @@ class NetworkRequest {
222
224
  };
223
225
  this.isSecure = UrlUtils.isSecureScheme(this.parsedURL.scheme);
224
226
 
227
+ this.rendererStartTime = data.timestamp * 1000;
225
228
  // Expected to be overriden with better value in `_recomputeTimesWithResourceTiming`.
226
229
  this.startTime = data.timestamp * 1000;
227
230
 
@@ -19,6 +19,10 @@ const stackPacksToInclude = [
19
19
  packId: 'wordpress',
20
20
  requiredStacks: ['js:wordpress'],
21
21
  },
22
+ {
23
+ packId: 'wp-rocket',
24
+ requiredStacks: ['js:wp-rocket'],
25
+ },
22
26
  {
23
27
  packId: 'ezoic',
24
28
  requiredStacks: ['js:ezoic'],
@@ -148,13 +148,22 @@ export class TraceProcessor {
148
148
  static getMainThreadTopLevelEvents(trace: LH.Artifacts.ProcessedTrace, startTime?: number | undefined, endTime?: number | undefined): Array<ToplevelEvent>;
149
149
  /**
150
150
  * @param {LH.TraceEvent[]} events
151
- * @return {{pid: number, tid: number, frameId: string}}
151
+ * @return {{startingPid: number, frameId: string}}
152
152
  */
153
153
  static findMainFrameIds(events: LH.TraceEvent[]): {
154
- pid: number;
155
- tid: number;
154
+ startingPid: number;
156
155
  frameId: string;
157
156
  };
157
+ /**
158
+ * If there were any cross-origin navigations, there'll be more than one pid returned
159
+ * @param {{startingPid: number, frameId: string}} mainFrameInfo
160
+ * @param {LH.TraceEvent[]} keyEvents
161
+ * @return {Map<number, number>} Map where keys are process IDs and their values are thread IDs
162
+ */
163
+ static findMainFramePidTids(mainFrameInfo: {
164
+ startingPid: number;
165
+ frameId: string;
166
+ }, keyEvents: LH.TraceEvent[]): Map<number, number>;
158
167
  /**
159
168
  * @param {LH.TraceEvent} evt
160
169
  * @return {boolean}
@@ -239,14 +248,14 @@ export class TraceProcessor {
239
248
  * Can also be skewed by several hundred milliseconds or even seconds when the browser takes a long
240
249
  * time to unload `about:blank`.
241
250
  *
242
- * @param {{keyEvents: Array<LH.TraceEvent>, frameEvents: Array<LH.TraceEvent>, mainFrameIds: {frameId: string}}} traceEventSubsets
251
+ * @param {{keyEvents: Array<LH.TraceEvent>, frameEvents: Array<LH.TraceEvent>, mainFrameInfo: {frameId: string}}} traceEventSubsets
243
252
  * @param {TimeOriginDeterminationMethod} method
244
253
  * @return {LH.TraceEvent}
245
254
  */
246
255
  static computeTimeOrigin(traceEventSubsets: {
247
256
  keyEvents: Array<LH.TraceEvent>;
248
257
  frameEvents: Array<LH.TraceEvent>;
249
- mainFrameIds: {
258
+ mainFrameInfo: {
250
259
  frameId: string;
251
260
  };
252
261
  }, method: TimeOriginDeterminationMethod): LH.TraceEvent;
@@ -423,7 +423,7 @@ class TraceProcessor {
423
423
 
424
424
  /**
425
425
  * @param {LH.TraceEvent[]} events
426
- * @return {{pid: number, tid: number, frameId: string}}
426
+ * @return {{startingPid: number, frameId: string}}
427
427
  */
428
428
  static findMainFrameIds(events) {
429
429
  // Prefer the newer TracingStartedInBrowser event first, if it exists
@@ -433,14 +433,9 @@ class TraceProcessor {
433
433
  const frameId = mainFrame?.frame;
434
434
  const pid = mainFrame?.processId;
435
435
 
436
- const threadNameEvt = events.find(e => e.pid === pid && e.ph === 'M' &&
437
- e.cat === '__metadata' && e.name === 'thread_name' && e.args.name === 'CrRendererMain');
438
- const tid = threadNameEvt?.tid;
439
-
440
- if (pid && tid && frameId) {
436
+ if (pid && frameId) {
441
437
  return {
442
- pid,
443
- tid,
438
+ startingPid: pid,
444
439
  frameId,
445
440
  };
446
441
  }
@@ -454,8 +449,7 @@ class TraceProcessor {
454
449
  const frameId = startedInPageEvt.args.data.page;
455
450
  if (frameId) {
456
451
  return {
457
- pid: startedInPageEvt.pid,
458
- tid: startedInPageEvt.tid,
452
+ startingPid: startedInPageEvt.pid,
459
453
  frameId,
460
454
  };
461
455
  }
@@ -478,8 +472,7 @@ class TraceProcessor {
478
472
  const frameId = navStartEvt.args.frame;
479
473
  if (frameId) {
480
474
  return {
481
- pid: navStartEvt.pid,
482
- tid: navStartEvt.tid,
475
+ startingPid: navStartEvt.pid,
483
476
  frameId,
484
477
  };
485
478
  }
@@ -488,6 +481,47 @@ class TraceProcessor {
488
481
  throw this.createNoTracingStartedError();
489
482
  }
490
483
 
484
+ /**
485
+ * If there were any cross-origin navigations, there'll be more than one pid returned
486
+ * @param {{startingPid: number, frameId: string}} mainFrameInfo
487
+ * @param {LH.TraceEvent[]} keyEvents
488
+ * @return {Map<number, number>} Map where keys are process IDs and their values are thread IDs
489
+ */
490
+ static findMainFramePidTids(mainFrameInfo, keyEvents) {
491
+ const frameCommittedEvts = keyEvents.filter(evt =>
492
+ evt.name === 'FrameCommittedInBrowser' &&
493
+ evt.args?.data?.frame === mainFrameInfo.frameId
494
+ );
495
+
496
+ // "Modern" traces with a navigation have a FrameCommittedInBrowser event
497
+ const mainFramePids = frameCommittedEvts.length
498
+ ? frameCommittedEvts.map(e => e?.args?.data?.processId)
499
+ // …But old traces and some timespan traces may not. In these situations, we'll assume the
500
+ // primary process ID remains constant (as there were no navigations).
501
+ : [mainFrameInfo.startingPid];
502
+
503
+ const pidToTid = new Map();
504
+
505
+ mainFramePids.forEach(pid => {
506
+ // While renderer tids are generally predictable, we'll doublecheck it
507
+ const threadNameEvt = keyEvents.find(e =>
508
+ e.cat === '__metadata' &&
509
+ e.pid === pid &&
510
+ e.ph === 'M' &&
511
+ e.name === 'thread_name' &&
512
+ e.args.name === 'CrRendererMain'
513
+ );
514
+ const tid = threadNameEvt?.tid;
515
+
516
+ if (!tid) {
517
+ throw new Error('Unable to determine tid for renderer process');
518
+ }
519
+
520
+ pidToTid.set(pid, tid);
521
+ });
522
+ return pidToTid;
523
+ }
524
+
491
525
  /**
492
526
  * @param {LH.TraceEvent} evt
493
527
  * @return {boolean}
@@ -583,6 +617,9 @@ class TraceProcessor {
583
617
  while (parentFrames.has(cur)) {
584
618
  cur = /** @type {string} */ (parentFrames.get(cur));
585
619
  }
620
+ if (cur === undefined) {
621
+ throw new Error('Unexpected undefined frameId');
622
+ }
586
623
  frameIdToRootFrameId.set(frame.id, cur);
587
624
  }
588
625
 
@@ -609,7 +646,15 @@ class TraceProcessor {
609
646
  });
610
647
 
611
648
  // Find the inspected frame
612
- const mainFrameIds = this.findMainFrameIds(keyEvents);
649
+ const mainFrameInfo = this.findMainFrameIds(keyEvents);
650
+ const rendererPidToTid = this.findMainFramePidTids(mainFrameInfo, keyEvents);
651
+
652
+ // Subset all trace events to just our tab's process (incl threads other than main)
653
+ // stable-sort events to keep them correctly nested.
654
+ const processEvents = TraceProcessor
655
+ .filteredTraceSort(trace.traceEvents, e => rendererPidToTid.has(e.pid));
656
+
657
+ // TODO(paulirish): filter down frames (and subsequent actions) to the primary process tree & frame tree
613
658
 
614
659
  /** @type {Map<string, {id: string, url: string, parent?: string}>} */
615
660
  const framesById = new Map();
@@ -645,18 +690,32 @@ class TraceProcessor {
645
690
  parent: evt.args.data.parent,
646
691
  });
647
692
  });
693
+
648
694
  const frames = [...framesById.values()];
649
695
  const frameIdToRootFrameId = this.resolveRootFrames(frames);
650
696
 
697
+ const inspectedTreeFrameIds = [...frameIdToRootFrameId.entries()]
698
+ .filter(([, rootFrameId]) => rootFrameId === mainFrameInfo.frameId)
699
+ .map(([child]) => child);
700
+
651
701
  // Filter to just events matching the main frame ID, just to make sure.
652
- const frameEvents = keyEvents.filter(e => e.args.frame === mainFrameIds.frameId);
702
+ /** @param {LH.TraceEvent} e */
703
+ function associatedToMainFrame(e) {
704
+ const frameId = e.args?.data?.frame || e.args.frame;
705
+ return frameId === mainFrameInfo.frameId;
706
+ }
707
+
708
+ /** @param {LH.TraceEvent} e */
709
+ function associatedToAllFrames(e) {
710
+ const frameId = e.args?.data?.frame || e.args.frame;
711
+ return frameId ? inspectedTreeFrameIds.includes(frameId) : false;
712
+ }
713
+ const frameEvents = keyEvents.filter(e => associatedToMainFrame(e));
653
714
 
654
715
  // Filter to just events matching the main frame ID or any child frame IDs.
655
716
  let frameTreeEvents = [];
656
- if (frameIdToRootFrameId.has(mainFrameIds.frameId)) {
657
- frameTreeEvents = keyEvents.filter(e => {
658
- return e.args.frame && frameIdToRootFrameId.get(e.args.frame) === mainFrameIds.frameId;
659
- });
717
+ if (frameIdToRootFrameId.has(mainFrameInfo.frameId)) {
718
+ frameTreeEvents = keyEvents.filter(e => associatedToAllFrames(e));
660
719
  } else {
661
720
  // In practice, there should always be TracingStartedInBrowser/FrameCommittedInBrowser events to
662
721
  // define the frame tree. Unfortunately, many test traces do not that frame info due to minification.
@@ -665,23 +724,17 @@ class TraceProcessor {
665
724
  'TraceProcessor',
666
725
  'frameTreeEvents may be incomplete, make sure the trace has frame events'
667
726
  );
668
- frameIdToRootFrameId.set(mainFrameIds.frameId, mainFrameIds.frameId);
727
+ frameIdToRootFrameId.set(mainFrameInfo.frameId, mainFrameInfo.frameId);
669
728
  frameTreeEvents = frameEvents;
670
729
  }
671
730
 
672
731
  // Compute our time origin to use for all relative timings.
673
732
  const timeOriginEvt = this.computeTimeOrigin(
674
- {keyEvents, frameEvents, mainFrameIds},
733
+ {keyEvents, frameEvents, mainFrameInfo: mainFrameInfo},
675
734
  timeOriginDeterminationMethod
676
735
  );
677
736
 
678
- // Subset all trace events to just our tab's process (incl threads other than main)
679
- // stable-sort events to keep them correctly nested.
680
- const processEvents = TraceProcessor
681
- .filteredTraceSort(trace.traceEvents, e => e.pid === mainFrameIds.pid);
682
-
683
- const mainThreadEvents = processEvents
684
- .filter(e => e.tid === mainFrameIds.tid);
737
+ const mainThreadEvents = processEvents.filter(e => e.tid === rendererPidToTid.get(e.pid));
685
738
 
686
739
  // Ensure our traceEnd reflects all page activity.
687
740
  const traceEnd = this.computeTraceEnd(trace.traceEvents, timeOriginEvt);
@@ -694,7 +747,7 @@ class TraceProcessor {
694
747
  frameEvents,
695
748
  frameTreeEvents,
696
749
  processEvents,
697
- mainFrameIds,
750
+ mainFrameInfo,
698
751
  timeOriginEvt,
699
752
  timings: {
700
753
  timeOrigin: 0,
@@ -704,6 +757,8 @@ class TraceProcessor {
704
757
  timeOrigin: timeOriginEvt.ts,
705
758
  traceEnd: traceEnd.timestamp,
706
759
  },
760
+ _keyEvents: keyEvents,
761
+ _rendererPidToTid: rendererPidToTid,
707
762
  };
708
763
  }
709
764
 
@@ -807,7 +862,7 @@ class TraceProcessor {
807
862
  * Can also be skewed by several hundred milliseconds or even seconds when the browser takes a long
808
863
  * time to unload `about:blank`.
809
864
  *
810
- * @param {{keyEvents: Array<LH.TraceEvent>, frameEvents: Array<LH.TraceEvent>, mainFrameIds: {frameId: string}}} traceEventSubsets
865
+ * @param {{keyEvents: Array<LH.TraceEvent>, frameEvents: Array<LH.TraceEvent>, mainFrameInfo: {frameId: string}}} traceEventSubsets
811
866
  * @param {TimeOriginDeterminationMethod} method
812
867
  * @return {LH.TraceEvent}
813
868
  */
@@ -833,7 +888,7 @@ class TraceProcessor {
833
888
  const fetchStart = traceEventSubsets.keyEvents.find(event => {
834
889
  if (event.name !== 'ResourceSendRequest') return false;
835
890
  const data = event.args.data || {};
836
- return data.frame === traceEventSubsets.mainFrameIds.frameId;
891
+ return data.frame === traceEventSubsets.mainFrameInfo.frameId;
837
892
  });
838
893
  if (!fetchStart) throw this.createNoResourceSendRequestError();
839
894
  return fetchStart;
@@ -128,7 +128,7 @@ class MetricTraceEvents {
128
128
  if (!timeOriginMetric) return {errorMessage: 'timeorigin Metric not found in definitions'};
129
129
  try {
130
130
  const frameIds = TraceProcessor.findMainFrameIds(this._traceEvents);
131
- return {pid: frameIds.pid, tid: frameIds.tid, ts: timeOriginMetric.ts};
131
+ return {pid: frameIds.startingPid, tid: 1, ts: timeOriginMetric.ts};
132
132
  } catch (err) {
133
133
  return {errorMessage: err.message};
134
134
  }