lighthouse 11.7.0-dev.20240409 → 11.7.0-dev.20240411

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 (134) hide show
  1. package/cli/cli-flags.js +3 -3
  2. package/cli/test/smokehouse/core-tests.js +0 -16
  3. package/cli/test/smokehouse/frontends/smokehouse-bin.js +2 -2
  4. package/core/audits/byte-efficiency/render-blocking-resources.js +25 -26
  5. package/core/audits/layout-shifts.js +1 -1
  6. package/core/computed/metrics/lantern-first-contentful-paint.js +3 -2
  7. package/core/computed/metrics/lantern-first-meaningful-paint.js +3 -2
  8. package/core/computed/metrics/lantern-interactive.js +3 -2
  9. package/core/computed/metrics/lantern-largest-contentful-paint.js +3 -2
  10. package/core/computed/metrics/lantern-max-potential-fid.js +3 -2
  11. package/core/computed/metrics/lantern-metric.d.ts +5 -0
  12. package/core/computed/metrics/lantern-metric.js +23 -1
  13. package/core/computed/metrics/lantern-speed-index.js +3 -2
  14. package/core/computed/navigation-insights.d.ts +36 -0
  15. package/core/computed/navigation-insights.js +35 -0
  16. package/core/computed/trace-engine-result.d.ts +16 -3
  17. package/core/computed/trace-engine-result.js +19 -14
  18. package/core/config/default-config.js +6 -51
  19. package/core/config/filters.js +0 -1
  20. package/core/gather/gatherers/root-causes.d.ts +2 -2
  21. package/core/gather/gatherers/root-causes.js +6 -5
  22. package/core/gather/gatherers/trace-elements.d.ts +2 -2
  23. package/core/gather/gatherers/trace-elements.js +2 -2
  24. package/core/lib/lantern/cpu-node.d.ts +7 -1
  25. package/core/lib/lantern/cpu-node.js +12 -2
  26. package/core/lib/lantern/lantern-error.d.ts +8 -0
  27. package/core/lib/lantern/lantern-error.js +9 -0
  28. package/core/lib/lantern/metrics/first-meaningful-paint.js +2 -4
  29. package/core/lib/lantern/metrics/interactive.js +1 -1
  30. package/core/lib/lantern/metrics/largest-contentful-paint.js +3 -3
  31. package/core/lib/lantern/network-node.js +28 -4
  32. package/core/lib/lantern/page-dependency-graph.js +14 -2
  33. package/core/lib/lantern/simulator/simulator.js +1 -1
  34. package/core/lib/lh-error.js +3 -3
  35. package/core/runner.js +0 -1
  36. package/dist/report/bundle.esm.js +11 -95
  37. package/dist/report/flow.js +16 -100
  38. package/dist/report/standalone.js +12 -96
  39. package/flow-report/src/help-dialog.tsx +0 -1
  40. package/flow-report/src/i18n/i18n.d.ts +0 -4
  41. package/flow-report/src/i18n/ui-strings.d.ts +0 -1
  42. package/flow-report/src/i18n/ui-strings.js +0 -2
  43. package/package.json +1 -1
  44. package/readme.md +2 -2
  45. package/report/assets/styles.css +1 -43
  46. package/report/assets/templates.html +0 -95
  47. package/report/renderer/components.d.ts +2 -2
  48. package/report/renderer/components.js +2 -103
  49. package/report/renderer/report-renderer.js +2 -12
  50. package/report/renderer/report-ui-features.js +1 -1
  51. package/report/renderer/report-utils.d.ts +0 -1
  52. package/report/renderer/report-utils.js +0 -3
  53. package/shared/localization/locales/ar-XB.json +0 -174
  54. package/shared/localization/locales/ar.json +0 -174
  55. package/shared/localization/locales/bg.json +0 -174
  56. package/shared/localization/locales/ca.json +0 -174
  57. package/shared/localization/locales/cs.json +0 -174
  58. package/shared/localization/locales/da.json +0 -174
  59. package/shared/localization/locales/de.json +0 -174
  60. package/shared/localization/locales/el.json +0 -174
  61. package/shared/localization/locales/en-GB.json +0 -174
  62. package/shared/localization/locales/en-US.json +0 -177
  63. package/shared/localization/locales/en-XA.json +0 -174
  64. package/shared/localization/locales/en-XL.json +0 -177
  65. package/shared/localization/locales/es-419.json +0 -174
  66. package/shared/localization/locales/es.json +0 -174
  67. package/shared/localization/locales/fi.json +0 -174
  68. package/shared/localization/locales/fil.json +0 -174
  69. package/shared/localization/locales/fr.json +0 -174
  70. package/shared/localization/locales/he.json +0 -174
  71. package/shared/localization/locales/hi.json +0 -174
  72. package/shared/localization/locales/hr.json +0 -174
  73. package/shared/localization/locales/hu.json +0 -174
  74. package/shared/localization/locales/id.json +0 -174
  75. package/shared/localization/locales/it.json +0 -174
  76. package/shared/localization/locales/ja.json +0 -174
  77. package/shared/localization/locales/ko.json +0 -174
  78. package/shared/localization/locales/lt.json +0 -174
  79. package/shared/localization/locales/lv.json +0 -174
  80. package/shared/localization/locales/nl.json +0 -174
  81. package/shared/localization/locales/no.json +0 -174
  82. package/shared/localization/locales/pl.json +0 -174
  83. package/shared/localization/locales/pt-PT.json +0 -174
  84. package/shared/localization/locales/pt.json +0 -174
  85. package/shared/localization/locales/ro.json +0 -174
  86. package/shared/localization/locales/ru.json +0 -174
  87. package/shared/localization/locales/sk.json +0 -174
  88. package/shared/localization/locales/sl.json +0 -174
  89. package/shared/localization/locales/sr-Latn.json +0 -174
  90. package/shared/localization/locales/sr.json +0 -174
  91. package/shared/localization/locales/sv.json +0 -174
  92. package/shared/localization/locales/ta.json +0 -174
  93. package/shared/localization/locales/te.json +0 -174
  94. package/shared/localization/locales/th.json +0 -174
  95. package/shared/localization/locales/tr.json +0 -174
  96. package/shared/localization/locales/uk.json +0 -174
  97. package/shared/localization/locales/vi.json +0 -174
  98. package/shared/localization/locales/zh-HK.json +0 -174
  99. package/shared/localization/locales/zh-TW.json +0 -174
  100. package/shared/localization/locales/zh.json +0 -174
  101. package/tsconfig.json +0 -1
  102. package/types/artifacts.d.ts +15 -90
  103. package/types/audit.d.ts +0 -10
  104. package/types/internal/smokehouse.d.ts +1 -1
  105. package/types/lhr/lhr.d.ts +0 -2
  106. package/core/audits/content-width.d.ts +0 -17
  107. package/core/audits/content-width.js +0 -76
  108. package/core/audits/installable-manifest.d.ts +0 -111
  109. package/core/audits/installable-manifest.js +0 -248
  110. package/core/audits/manual/pwa-cross-browser.d.ts +0 -12
  111. package/core/audits/manual/pwa-cross-browser.js +0 -40
  112. package/core/audits/manual/pwa-each-page-has-url.d.ts +0 -12
  113. package/core/audits/manual/pwa-each-page-has-url.js +0 -38
  114. package/core/audits/manual/pwa-page-transitions.d.ts +0 -12
  115. package/core/audits/manual/pwa-page-transitions.js +0 -38
  116. package/core/audits/maskable-icon.d.ts +0 -25
  117. package/core/audits/maskable-icon.js +0 -70
  118. package/core/audits/multi-check-audit.d.ts +0 -28
  119. package/core/audits/multi-check-audit.js +0 -82
  120. package/core/audits/splash-screen.d.ts +0 -37
  121. package/core/audits/splash-screen.js +0 -98
  122. package/core/audits/themed-omnibox.d.ts +0 -42
  123. package/core/audits/themed-omnibox.js +0 -102
  124. package/core/computed/manifest-values.d.ts +0 -24
  125. package/core/computed/manifest-values.js +0 -136
  126. package/core/gather/gatherers/dobetterweb/tags-blocking-first-paint.d.ts +0 -47
  127. package/core/gather/gatherers/dobetterweb/tags-blocking-first-paint.js +0 -233
  128. package/core/gather/gatherers/installability-errors.d.ts +0 -17
  129. package/core/gather/gatherers/installability-errors.js +0 -56
  130. package/core/gather/gatherers/web-app-manifest.d.ts +0 -32
  131. package/core/gather/gatherers/web-app-manifest.js +0 -106
  132. package/report/renderer/pwa-category-renderer.d.ts +0 -55
  133. package/report/renderer/pwa-category-renderer.js +0 -184
  134. package/third-party/chromium-synchronization/installability-errors-test.js +0 -79
@@ -90,15 +90,6 @@ const UIStrings = {
90
90
  seoCrawlingGroupTitle: 'Crawling and Indexing',
91
91
  /** Description of the navigation section within the Search Engine Optimization (SEO) category. Within this section are audits with descriptive titles that highlight ways to make a website accessible to search engine crawlers. */
92
92
  seoCrawlingGroupDescription: 'To appear in search results, crawlers need access to your app.',
93
- /** Title of the Progressive Web Application (PWA) category of audits. This is displayed at the top of a list of audits focused on topics related to whether or not a site is a progressive web app, e.g. responds offline, uses a service worker, is on https, etc. Also used as a label of a score gauge. */
94
- pwaCategoryTitle: 'PWA',
95
- /** Description of the Progressive Web Application (PWA) category. This is displayed at the top of a list of audits focused on topics related to whether or not a site is a progressive web app, e.g. responds offline, uses a service worker, is on https, etc. No character length limits. The last sentence starting with 'Learn' becomes link text to additional documentation. */
96
- pwaCategoryDescription: 'These checks validate the aspects of a Progressive Web App. ' +
97
- '[Learn what makes a good Progressive Web App](https://web.dev/articles/pwa-checklist).',
98
- /** Description of the Progressive Web Application (PWA) manual checks category, containing a list of additional validators must be run by hand in order to check all PWA best practices. This is displayed at the top of a list of manually run audits focused on topics related to whether or not a site is a progressive web app, e.g. responds offline, uses a service worker, is on https, etc.. No character length limits. */
99
- pwaCategoryManualDescription: 'These checks are required by the baseline ' +
100
- '[PWA Checklist](https://web.dev/articles/pwa-checklist) but are ' +
101
- 'not automatically checked by Lighthouse. They do not affect your score but it\'s important that you verify them manually.',
102
93
  /** Title of the Best Practices category of audits. This is displayed at the top of a list of audits focused on topics related to following web development best practices and accepted guidelines. Also used as a label of a score gauge; try to limit to 20 characters. */
103
94
  bestPracticesCategoryTitle: 'Best Practices',
104
95
  /** Title of the Trust & Safety group of audits. This is displayed at the top of a list of audits focused on maintaining user trust and protecting security in web development. */
@@ -109,10 +100,6 @@ const UIStrings = {
109
100
  bestPracticesBrowserCompatGroupTitle: 'Browser Compatibility',
110
101
  /** Title of the General group of the Best Practices category. Within this section are the audits that don't belong to a specific group but are of general interest. */
111
102
  bestPracticesGeneralGroupTitle: 'General',
112
- /** Title of the Installable section of the web app category. Within this section are audits that check if Chrome supports installing the web site as an app on their device. */
113
- pwaInstallableGroupTitle: 'Installable',
114
- /** Title of the "PWA Optimized" section of the web app category. Within this section are audits that check if the developer has taken advantage of features to make their web page more enjoyable and engaging for the user. */
115
- pwaOptimizedGroupTitle: 'PWA Optimized',
116
103
  };
117
104
 
118
105
  const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
@@ -137,7 +124,6 @@ const defaultConfig = {
137
124
  {id: 'Inputs', gatherer: 'inputs'},
138
125
  {id: 'IFrameElements', gatherer: 'iframe-elements'},
139
126
  {id: 'ImageElements', gatherer: 'image-elements'},
140
- {id: 'InstallabilityErrors', gatherer: 'installability-errors'},
141
127
  {id: 'InspectorIssues', gatherer: 'inspector-issues'},
142
128
  {id: 'JsUsage', gatherer: 'js-usage'},
143
129
  {id: 'LinkElements', gatherer: 'link-elements'},
@@ -151,10 +137,8 @@ const defaultConfig = {
151
137
  {id: 'Scripts', gatherer: 'scripts'},
152
138
  {id: 'SourceMaps', gatherer: 'source-maps'},
153
139
  {id: 'Stacks', gatherer: 'stacks'},
154
- {id: 'TagsBlockingFirstPaint', gatherer: 'dobetterweb/tags-blocking-first-paint'},
155
140
  {id: 'TraceElements', gatherer: 'trace-elements'},
156
141
  {id: 'ViewportDimensions', gatherer: 'viewport-dimensions'},
157
- {id: 'WebAppManifest', gatherer: 'web-app-manifest'},
158
142
 
159
143
  // Artifact copies are renamed for compatibility with legacy artifacts.
160
144
  {id: 'devtoolsLogs', gatherer: 'devtools-log-compat'},
@@ -186,11 +170,6 @@ const defaultConfig = {
186
170
  'user-timings',
187
171
  'critical-request-chains',
188
172
  'redirects',
189
- 'installable-manifest',
190
- 'splash-screen',
191
- 'themed-omnibox',
192
- 'maskable-icon',
193
- 'content-width',
194
173
  'image-aspect-ratio',
195
174
  'image-size-responsive',
196
175
  'deprecations',
@@ -220,9 +199,6 @@ const defaultConfig = {
220
199
  'prioritize-lcp-image',
221
200
  'csp-xss',
222
201
  'script-treemap-data',
223
- 'manual/pwa-cross-browser',
224
- 'manual/pwa-page-transitions',
225
- 'manual/pwa-each-page-has-url',
226
202
  'accessibility/accesskeys',
227
203
  'accessibility/aria-allowed-attr',
228
204
  'accessibility/aria-allowed-role',
@@ -345,12 +321,6 @@ const defaultConfig = {
345
321
  title: str_(UIStrings.diagnosticsGroupTitle),
346
322
  description: str_(UIStrings.diagnosticsGroupDescription),
347
323
  },
348
- 'pwa-installable': {
349
- title: str_(UIStrings.pwaInstallableGroupTitle),
350
- },
351
- 'pwa-optimized': {
352
- title: str_(UIStrings.pwaOptimizedGroupTitle),
353
- },
354
324
  'a11y-best-practices': {
355
325
  title: str_(UIStrings.a11yBestPracticesGroupTitle),
356
326
  description: str_(UIStrings.a11yBestPracticesGroupDescription),
@@ -603,12 +573,17 @@ const defaultConfig = {
603
573
  manualDescription: str_(UIStrings.seoCategoryManualDescription),
604
574
  supportedModes: ['navigation', 'snapshot'],
605
575
  auditRefs: [
576
+ // Should be at least 31% of the score, such that this audit failing
577
+ // results in the SEO category failing.
578
+ // Solve for w:
579
+ // w / (w + T) >= 0.31
580
+ // where T is the sum of all the other weights.
581
+ {id: 'is-crawlable', weight: 93 / 23, group: 'seo-crawl'},
606
582
  {id: 'document-title', weight: 1, group: 'seo-content'},
607
583
  {id: 'meta-description', weight: 1, group: 'seo-content'},
608
584
  {id: 'http-status-code', weight: 1, group: 'seo-crawl'},
609
585
  {id: 'link-text', weight: 1, group: 'seo-content'},
610
586
  {id: 'crawlable-anchors', weight: 1, group: 'seo-crawl'},
611
- {id: 'is-crawlable', weight: 1, group: 'seo-crawl'},
612
587
  {id: 'robots-txt', weight: 1, group: 'seo-crawl'},
613
588
  {id: 'image-alt', weight: 1, group: 'seo-content'},
614
589
  {id: 'hreflang', weight: 1, group: 'seo-content'},
@@ -617,26 +592,6 @@ const defaultConfig = {
617
592
  {id: 'structured-data', weight: 0},
618
593
  ],
619
594
  },
620
- 'pwa': {
621
- title: str_(UIStrings.pwaCategoryTitle),
622
- description: str_(UIStrings.pwaCategoryDescription),
623
- manualDescription: str_(UIStrings.pwaCategoryManualDescription),
624
- supportedModes: ['navigation'],
625
- auditRefs: [
626
- // Installable
627
- {id: 'installable-manifest', weight: 2, group: 'pwa-installable'},
628
- // PWA Optimized
629
- {id: 'splash-screen', weight: 1, group: 'pwa-optimized'},
630
- {id: 'themed-omnibox', weight: 1, group: 'pwa-optimized'},
631
- {id: 'content-width', weight: 1, group: 'pwa-optimized'},
632
- {id: 'viewport', weight: 2, group: 'pwa-optimized'},
633
- {id: 'maskable-icon', weight: 1, group: 'pwa-optimized'},
634
- // Manual audits
635
- {id: 'pwa-cross-browser', weight: 0},
636
- {id: 'pwa-page-transitions', weight: 0},
637
- {id: 'pwa-each-page-has-url', weight: 0},
638
- ],
639
- },
640
595
  },
641
596
  };
642
597
 
@@ -13,7 +13,6 @@ const baseArtifactKeySource = {
13
13
  fetchTime: '',
14
14
  LighthouseRunWarnings: '',
15
15
  BenchmarkIndex: '',
16
- BenchmarkIndexes: '',
17
16
  settings: '',
18
17
  Timing: '',
19
18
  URL: '',
@@ -3,10 +3,10 @@ declare class RootCauses extends BaseGatherer {
3
3
  static symbol: symbol;
4
4
  /**
5
5
  * @param {LH.Gatherer.Driver} driver
6
- * @param {LH.Artifacts.TraceEngineResult} traceEngineResult
6
+ * @param {LH.Artifacts.TraceEngineResult['data']} traceParsedData
7
7
  * @return {Promise<LH.Artifacts.TraceEngineRootCauses>}
8
8
  */
9
- static runRootCauseAnalysis(driver: LH.Gatherer.Driver, traceEngineResult: LH.Artifacts.TraceEngineResult): Promise<LH.Artifacts.TraceEngineRootCauses>;
9
+ static runRootCauseAnalysis(driver: LH.Gatherer.Driver, traceParsedData: LH.Artifacts.TraceEngineResult['data']): Promise<LH.Artifacts.TraceEngineRootCauses>;
10
10
  /** @type {LH.Gatherer.GathererMeta<'Trace'>} */
11
11
  meta: LH.Gatherer.GathererMeta<'Trace'>;
12
12
  /**
@@ -21,10 +21,10 @@ class RootCauses extends BaseGatherer {
21
21
 
22
22
  /**
23
23
  * @param {LH.Gatherer.Driver} driver
24
- * @param {LH.Artifacts.TraceEngineResult} traceEngineResult
24
+ * @param {LH.Artifacts.TraceEngineResult['data']} traceParsedData
25
25
  * @return {Promise<LH.Artifacts.TraceEngineRootCauses>}
26
26
  */
27
- static async runRootCauseAnalysis(driver, traceEngineResult) {
27
+ static async runRootCauseAnalysis(driver, traceParsedData) {
28
28
  await driver.defaultSession.sendCommand('DOM.enable');
29
29
  await driver.defaultSession.sendCommand('CSS.enable');
30
30
 
@@ -112,9 +112,10 @@ class RootCauses extends BaseGatherer {
112
112
  layoutShifts: {},
113
113
  };
114
114
  const rootCausesEngine = new TraceEngine.RootCauses(protocolInterface);
115
- const layoutShiftEvents = traceEngineResult.LayoutShifts.clusters.flatMap(c => c.events);
115
+ const layoutShiftEvents = traceParsedData.LayoutShifts.clusters.flatMap(c => c.events);
116
116
  for (const event of layoutShiftEvents) {
117
- const r = await rootCausesEngine.layoutShifts.rootCausesForEvent(traceEngineResult, event);
117
+ // @ts-expect-error The data we do get in the trace processor is still enough here
118
+ const r = await rootCausesEngine.layoutShifts.rootCausesForEvent(traceParsedData, event);
118
119
  if (!r) continue;
119
120
 
120
121
  for (const cause of r.fontChanges) {
@@ -137,7 +138,7 @@ class RootCauses extends BaseGatherer {
137
138
  async getArtifact(context) {
138
139
  const trace = context.dependencies.Trace;
139
140
  const traceEngineResult = await TraceEngineResult.request({trace}, context);
140
- return RootCauses.runRootCauseAnalysis(context.driver, traceEngineResult);
141
+ return RootCauses.runRootCauseAnalysis(context.driver, traceEngineResult.data);
141
142
  }
142
143
  }
143
144
 
@@ -25,12 +25,12 @@ declare class TraceElements extends BaseGatherer {
25
25
  * that may have caused the shift.
26
26
  *
27
27
  * @param {LH.Trace} trace
28
- * @param {LH.Artifacts.TraceEngineResult} traceEngineResult
28
+ * @param {LH.Artifacts.TraceEngineResult['data']} traceEngineResult
29
29
  * @param {LH.Artifacts.TraceEngineRootCauses} rootCauses
30
30
  * @param {LH.Gatherer.Context} context
31
31
  * @return {Promise<Array<{nodeId: number}>>}
32
32
  */
33
- static getTopLayoutShifts(trace: LH.Trace, traceEngineResult: LH.Artifacts.TraceEngineResult, rootCauses: LH.Artifacts.TraceEngineRootCauses, context: LH.Gatherer.Context): Promise<Array<{
33
+ static getTopLayoutShifts(trace: LH.Trace, traceEngineResult: LH.Artifacts.TraceEngineResult['data'], rootCauses: LH.Artifacts.TraceEngineRootCauses, context: LH.Gatherer.Context): Promise<Array<{
34
34
  nodeId: number;
35
35
  }>>;
36
36
  /**
@@ -122,7 +122,7 @@ class TraceElements extends BaseGatherer {
122
122
  * that may have caused the shift.
123
123
  *
124
124
  * @param {LH.Trace} trace
125
- * @param {LH.Artifacts.TraceEngineResult} traceEngineResult
125
+ * @param {LH.Artifacts.TraceEngineResult['data']} traceEngineResult
126
126
  * @param {LH.Artifacts.TraceEngineRootCauses} rootCauses
127
127
  * @param {LH.Gatherer.Context} context
128
128
  * @return {Promise<Array<{nodeId: number}>>}
@@ -322,7 +322,7 @@ class TraceElements extends BaseGatherer {
322
322
 
323
323
  const lcpNodeData = await TraceElements.getLcpElement(trace, context);
324
324
  const shiftsData = await TraceElements.getTopLayoutShifts(
325
- trace, traceEngineResult, rootCauses, context);
325
+ trace, traceEngineResult.data, rootCauses, context);
326
326
  const animatedElementData = await this.getAnimatedElements(mainThreadEvents);
327
327
  const responsivenessElementData = await TraceElements.getResponsivenessElement(trace, context);
328
328
 
@@ -6,11 +6,17 @@ export class CPUNode<T = any> extends BaseNode<T> {
6
6
  /**
7
7
  * @param {LH.TraceEvent} parentEvent
8
8
  * @param {LH.TraceEvent[]=} childEvents
9
+ * @param {number=} correctedEndTs
9
10
  */
10
- constructor(parentEvent: LH.TraceEvent, childEvents?: LH.TraceEvent[] | undefined);
11
+ constructor(parentEvent: LH.TraceEvent, childEvents?: LH.TraceEvent[] | undefined, correctedEndTs?: number | undefined);
11
12
  _event: LH.TraceEvent;
12
13
  _childEvents: LH.TraceEvent[];
14
+ _correctedEndTs: number | undefined;
13
15
  get type(): "cpu";
16
+ /**
17
+ * @return {number}
18
+ */
19
+ get duration(): number;
14
20
  /**
15
21
  * @return {LH.TraceEvent}
16
22
  */
@@ -15,13 +15,15 @@ class CPUNode extends BaseNode {
15
15
  /**
16
16
  * @param {LH.TraceEvent} parentEvent
17
17
  * @param {LH.TraceEvent[]=} childEvents
18
+ * @param {number=} correctedEndTs
18
19
  */
19
- constructor(parentEvent, childEvents = []) {
20
+ constructor(parentEvent, childEvents = [], correctedEndTs) {
20
21
  const nodeId = `${parentEvent.tid}.${parentEvent.ts}`;
21
22
  super(nodeId);
22
23
 
23
24
  this._event = parentEvent;
24
25
  this._childEvents = childEvents;
26
+ this._correctedEndTs = correctedEndTs;
25
27
  }
26
28
 
27
29
  get type() {
@@ -39,9 +41,17 @@ class CPUNode extends BaseNode {
39
41
  * @return {number}
40
42
  */
41
43
  get endTime() {
44
+ if (this._correctedEndTs) return this._correctedEndTs;
42
45
  return this._event.ts + this._event.dur;
43
46
  }
44
47
 
48
+ /**
49
+ * @return {number}
50
+ */
51
+ get duration() {
52
+ return this.endTime - this.startTime;
53
+ }
54
+
45
55
  /**
46
56
  * @return {LH.TraceEvent}
47
57
  */
@@ -83,7 +93,7 @@ class CPUNode extends BaseNode {
83
93
  * @return {CPUNode}
84
94
  */
85
95
  cloneWithoutRelationships() {
86
- return new CPUNode(this._event, this._childEvents);
96
+ return new CPUNode(this._event, this._childEvents, this._correctedEndTs);
87
97
  }
88
98
  }
89
99
 
@@ -0,0 +1,8 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2024 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ export class LanternError extends Error {
7
+ }
8
+ //# sourceMappingURL=lantern-error.d.ts.map
@@ -0,0 +1,9 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2024 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ class LanternError extends Error {}
8
+
9
+ export {LanternError};
@@ -6,8 +6,6 @@
6
6
 
7
7
  import * as Lantern from '../types/lantern.js';
8
8
  import {Metric} from '../metric.js';
9
- // TODO(15841): don't use LighthouseError
10
- import {LighthouseError} from '../../lh-error.js';
11
9
  import {FirstContentfulPaint} from './first-contentful-paint.js';
12
10
 
13
11
  /** @typedef {import('../base-node.js').Node} Node */
@@ -32,7 +30,7 @@ class FirstMeaningfulPaint extends Metric {
32
30
  static getOptimisticGraph(dependencyGraph, processedNavigation) {
33
31
  const fmp = processedNavigation.timestamps.firstMeaningfulPaint;
34
32
  if (!fmp) {
35
- throw new LighthouseError(LighthouseError.errors.NO_FMP);
33
+ throw new Error('NO_FMP');
36
34
  }
37
35
  return FirstContentfulPaint.getFirstPaintBasedGraph(dependencyGraph, {
38
36
  cutoffTimestamp: fmp,
@@ -51,7 +49,7 @@ class FirstMeaningfulPaint extends Metric {
51
49
  static getPessimisticGraph(dependencyGraph, processedNavigation) {
52
50
  const fmp = processedNavigation.timestamps.firstMeaningfulPaint;
53
51
  if (!fmp) {
54
- throw new LighthouseError(LighthouseError.errors.NO_FMP);
52
+ throw new Error('NO_FMP');
55
53
  }
56
54
 
57
55
  return FirstContentfulPaint.getFirstPaintBasedGraph(dependencyGraph, {
@@ -37,7 +37,7 @@ class Interactive extends Metric {
37
37
  return dependencyGraph.cloneWithRelationships(node => {
38
38
  // Include everything that might be a long task
39
39
  if (node.type === BaseNode.TYPES.CPU) {
40
- return node.event.dur > minimumCpuTaskDuration;
40
+ return node.duration > minimumCpuTaskDuration;
41
41
  }
42
42
 
43
43
  // Include all scripts and high priority requests, exclude all images
@@ -6,8 +6,8 @@
6
6
 
7
7
  import * as Lantern from '../types/lantern.js';
8
8
  import {Metric} from '../metric.js';
9
- import {LighthouseError} from '../../../lib/lh-error.js';
10
9
  import {FirstContentfulPaint} from './first-contentful-paint.js';
10
+ import {LanternError} from '../lantern-error.js';
11
11
 
12
12
  /** @typedef {import('../base-node.js').Node} Node */
13
13
 
@@ -45,7 +45,7 @@ class LargestContentfulPaint extends Metric {
45
45
  static getOptimisticGraph(dependencyGraph, processedNavigation) {
46
46
  const lcp = processedNavigation.timestamps.largestContentfulPaint;
47
47
  if (!lcp) {
48
- throw new LighthouseError(LighthouseError.errors.NO_LCP);
48
+ throw new LanternError('NO_LCP');
49
49
  }
50
50
 
51
51
  return FirstContentfulPaint.getFirstPaintBasedGraph(dependencyGraph, {
@@ -62,7 +62,7 @@ class LargestContentfulPaint extends Metric {
62
62
  static getPessimisticGraph(dependencyGraph, processedNavigation) {
63
63
  const lcp = processedNavigation.timestamps.largestContentfulPaint;
64
64
  if (!lcp) {
65
- throw new LighthouseError(LighthouseError.errors.NO_LCP);
65
+ throw new LanternError('NO_LCP');
66
66
  }
67
67
 
68
68
  return FirstContentfulPaint.getFirstPaintBasedGraph(dependencyGraph, {
@@ -7,8 +7,32 @@
7
7
  import * as Lantern from './types/lantern.js';
8
8
  import {NetworkRequestTypes} from './lantern.js';
9
9
  import {BaseNode} from './base-node.js';
10
- // TODO(15841): bring impl of isNonNetworkRequest inside lantern and remove this.
11
- import UrlUtils from '../url-utils.js';
10
+
11
+ const NON_NETWORK_SCHEMES = [
12
+ 'blob', // @see https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL
13
+ 'data', // @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs
14
+ 'intent', // @see https://developer.chrome.com/docs/multidevice/android/intents/
15
+ 'file', // @see https://en.wikipedia.org/wiki/File_URI_scheme
16
+ 'filesystem', // @see https://developer.mozilla.org/en-US/docs/Web/API/FileSystem
17
+ 'chrome-extension',
18
+ ];
19
+
20
+ /**
21
+ * Use `NetworkRequest.isNonNetworkRequest(req)` if working with a request.
22
+ * Note: the `protocol` field from CDP can be 'h2', 'http', (not 'https'!) or it'll be url's scheme.
23
+ * https://source.chromium.org/chromium/chromium/src/+/main:content/browser/devtools/protocol/network_handler.cc;l=598-611;drc=56d4a9a9deb30be73adcee8737c73bcb2a5ab64f
24
+ * However, a `new URL(href).protocol` has a colon suffix.
25
+ * https://url.spec.whatwg.org/#dom-url-protocol
26
+ * A URL's `scheme` is specced as the `protocol` sans-colon, but isn't exposed on a URL object.
27
+ * This method can take all 3 of these string types as a parameter.
28
+ * @param {string} protocol Either a networkRequest's `protocol` per CDP or a `new URL(href).protocol`
29
+ * @return {boolean}
30
+ */
31
+ function isNonNetworkProtocol(protocol) {
32
+ // Strip off any colon
33
+ const urlScheme = protocol.includes(':') ? protocol.slice(0, protocol.indexOf(':')) : protocol;
34
+ return NON_NETWORK_SCHEMES.includes(urlScheme);
35
+ }
12
36
 
13
37
  /**
14
38
  * @template [T=any]
@@ -75,9 +99,9 @@ class NetworkNode extends BaseNode {
75
99
  */
76
100
  get isNonNetworkProtocol() {
77
101
  // The 'protocol' field in devtools a string more like a `scheme`
78
- return UrlUtils.isNonNetworkProtocol(this.request.protocol) ||
102
+ return isNonNetworkProtocol(this.request.protocol) ||
79
103
  // But `protocol` can fail to be populated if the request fails, so fallback to scheme.
80
- UrlUtils.isNonNetworkProtocol(this.request.parsedURL.scheme);
104
+ isNonNetworkProtocol(this.request.parsedURL.scheme);
81
105
  }
82
106
 
83
107
  /**
@@ -128,6 +128,9 @@ class PageDependencyGraph {
128
128
  continue;
129
129
  }
130
130
 
131
+ /** @type {number|undefined} */
132
+ let correctedEndTs = undefined;
133
+
131
134
  // Capture all events that occurred within the task
132
135
  /** @type {Array<LH.TraceEvent>} */
133
136
  const children = [];
@@ -136,10 +139,19 @@ class PageDependencyGraph {
136
139
  i < mainThreadEvents.length && mainThreadEvents[i].ts < endTime;
137
140
  i++
138
141
  ) {
142
+ // Temporary fix for a Chrome bug where some RunTask events can be overlapping.
143
+ // We correct that here be ensuring each RunTask ends at least 1 microsecond before the next
144
+ // https://github.com/GoogleChrome/lighthouse/issues/15896
145
+ // https://issues.chromium.org/issues/329678173
146
+ if (TraceProcessor.isScheduleableTask(mainThreadEvents[i]) && mainThreadEvents[i].dur) {
147
+ correctedEndTs = mainThreadEvents[i].ts - 1;
148
+ break;
149
+ }
150
+
139
151
  children.push(mainThreadEvents[i]);
140
152
  }
141
153
 
142
- nodes.push(new CPUNode(evt, children));
154
+ nodes.push(new CPUNode(evt, children, correctedEndTs));
143
155
  }
144
156
 
145
157
  return nodes;
@@ -359,7 +371,7 @@ class PageDependencyGraph {
359
371
  isFirst = foundFirstParse = true;
360
372
  }
361
373
 
362
- if (isFirst || node.event.dur >= minimumEvtDur) {
374
+ if (isFirst || node.duration >= minimumEvtDur) {
363
375
  // Don't prune this node. The task is long / important so it will impact simulation.
364
376
  continue;
365
377
  }
@@ -276,7 +276,7 @@ class Simulator {
276
276
  ? this._layoutTaskMultiplier
277
277
  : this._cpuSlowdownMultiplier;
278
278
  const totalDuration = Math.min(
279
- Math.round(cpuNode.event.dur / 1000 * multiplier),
279
+ Math.round(cpuNode.duration / 1000 * multiplier),
280
280
  DEFAULT_MAXIMUM_CPU_TASK_DURATION
281
281
  );
282
282
  const estimatedTimeElapsed = totalDuration - timingData.timeElapsed;
@@ -76,13 +76,13 @@ const UIStrings = {
76
76
  criTimeout: 'Timeout waiting for initial Debugger Protocol connection.',
77
77
  /**
78
78
  * @description Error message explaining that a resource that was required for testing was never collected. "artifactName" will be replaced with the name of the resource that wasn't collected.
79
- * @example {WebAppManifest} artifactName
79
+ * @example {MainDocumentContent} artifactName
80
80
  * */
81
81
  missingRequiredArtifact: 'Required {artifactName} gatherer did not run.',
82
82
  /**
83
83
  * @description Error message explaining that there was an error while trying to collect a resource that was required for testing. "artifactName" will be replaced with the name of the resource that wasn't collected; "errorMessage" will be replaced with a string description of the error that occurred.
84
- * @example {WebAppManifest} artifactName
85
- * @example {Manifest invalid} errorMessage
84
+ * @example {MainDocumentContent} artifactName
85
+ * @example {Could not find main document} errorMessage
86
86
  * */
87
87
  erroredRequiredArtifact: 'Required {artifactName} gatherer encountered an error: {errorMessage}',
88
88
 
package/core/runner.js CHANGED
@@ -106,7 +106,6 @@ class Runner {
106
106
  networkUserAgent: artifacts.NetworkUserAgent,
107
107
  hostUserAgent: artifacts.HostUserAgent,
108
108
  benchmarkIndex: artifacts.BenchmarkIndex,
109
- benchmarkIndexes: artifacts.BenchmarkIndexes,
110
109
  credits,
111
110
  },
112
111
  audits: auditResultsById,