lighthouse 11.7.0 → 12.0.0-dev.20240422

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 (334) hide show
  1. package/CONTRIBUTING.md +2 -2
  2. package/cli/bin.js +0 -7
  3. package/cli/cli-flags.d.ts +1 -5
  4. package/cli/cli-flags.js +4 -8
  5. package/cli/test/smokehouse/__snapshots__/report-assert-test.js.snap +14 -14
  6. package/cli/test/smokehouse/config/exclusions.js +3 -0
  7. package/cli/test/smokehouse/core-tests.js +6 -20
  8. package/cli/test/smokehouse/frontends/smokehouse-bin.js +2 -2
  9. package/core/audits/accessibility/accesskeys.js +1 -1
  10. package/core/audits/accessibility/aria-allowed-attr.js +1 -1
  11. package/core/audits/accessibility/aria-allowed-role.js +1 -1
  12. package/core/audits/accessibility/aria-command-name.js +1 -1
  13. package/core/audits/accessibility/aria-conditional-attr.d.ts +10 -0
  14. package/core/audits/accessibility/aria-conditional-attr.js +43 -0
  15. package/core/audits/accessibility/{duplicate-id-active.d.ts → aria-deprecated-role.d.ts} +3 -3
  16. package/core/audits/accessibility/aria-deprecated-role.js +43 -0
  17. package/core/audits/accessibility/aria-dialog-name.js +1 -1
  18. package/core/audits/accessibility/aria-hidden-body.js +1 -1
  19. package/core/audits/accessibility/aria-hidden-focus.js +1 -1
  20. package/core/audits/accessibility/aria-input-field-name.js +1 -1
  21. package/core/audits/accessibility/aria-meter-name.js +1 -1
  22. package/core/audits/accessibility/aria-progressbar-name.js +1 -1
  23. package/core/audits/accessibility/aria-prohibited-attr.d.ts +10 -0
  24. package/core/audits/accessibility/aria-prohibited-attr.js +44 -0
  25. package/core/audits/accessibility/aria-required-attr.js +1 -1
  26. package/core/audits/accessibility/aria-required-children.js +1 -1
  27. package/core/audits/accessibility/aria-required-parent.js +1 -1
  28. package/core/audits/accessibility/aria-roles.js +1 -1
  29. package/core/audits/accessibility/aria-text.js +1 -1
  30. package/core/audits/accessibility/aria-toggle-field-name.js +1 -1
  31. package/core/audits/accessibility/aria-tooltip-name.js +1 -1
  32. package/core/audits/accessibility/aria-treeitem-name.js +1 -1
  33. package/core/audits/accessibility/aria-valid-attr-value.js +1 -1
  34. package/core/audits/accessibility/aria-valid-attr.js +1 -1
  35. package/core/audits/accessibility/button-name.js +1 -1
  36. package/core/audits/accessibility/bypass.js +1 -1
  37. package/core/audits/accessibility/color-contrast.js +1 -1
  38. package/core/audits/accessibility/definition-list.js +1 -1
  39. package/core/audits/accessibility/dlitem.js +1 -1
  40. package/core/audits/accessibility/document-title.js +1 -1
  41. package/core/audits/accessibility/duplicate-id-aria.js +1 -1
  42. package/core/audits/accessibility/empty-heading.js +1 -1
  43. package/core/audits/accessibility/form-field-multiple-labels.js +1 -1
  44. package/core/audits/accessibility/frame-title.js +1 -1
  45. package/core/audits/accessibility/heading-order.js +1 -1
  46. package/core/audits/accessibility/html-has-lang.js +1 -1
  47. package/core/audits/accessibility/html-lang-valid.js +1 -1
  48. package/core/audits/accessibility/html-xml-lang-mismatch.js +1 -1
  49. package/core/audits/accessibility/identical-links-same-purpose.js +1 -1
  50. package/core/audits/accessibility/image-alt.js +1 -1
  51. package/core/audits/accessibility/image-redundant-alt.js +1 -1
  52. package/core/audits/accessibility/input-button-name.js +1 -1
  53. package/core/audits/accessibility/input-image-alt.js +1 -1
  54. package/core/audits/accessibility/label-content-name-mismatch.js +1 -1
  55. package/core/audits/accessibility/label.js +1 -1
  56. package/core/audits/accessibility/landmark-one-main.js +1 -1
  57. package/core/audits/accessibility/link-in-text-block.js +1 -1
  58. package/core/audits/accessibility/link-name.js +1 -1
  59. package/core/audits/accessibility/list.js +1 -1
  60. package/core/audits/accessibility/listitem.js +1 -1
  61. package/core/audits/accessibility/meta-refresh.js +1 -1
  62. package/core/audits/accessibility/meta-viewport.js +1 -1
  63. package/core/audits/accessibility/object-alt.js +1 -1
  64. package/core/audits/accessibility/select-name.js +1 -1
  65. package/core/audits/accessibility/skip-link.js +1 -1
  66. package/core/audits/accessibility/tabindex.js +1 -1
  67. package/core/audits/accessibility/table-duplicate-name.js +1 -1
  68. package/core/audits/accessibility/table-fake-caption.js +1 -1
  69. package/core/audits/accessibility/target-size.js +1 -2
  70. package/core/audits/accessibility/td-has-header.js +1 -1
  71. package/core/audits/accessibility/td-headers-attr.js +1 -1
  72. package/core/audits/accessibility/th-has-data-cells.js +1 -1
  73. package/core/audits/accessibility/valid-lang.js +1 -1
  74. package/core/audits/accessibility/video-caption.js +1 -1
  75. package/core/audits/byte-efficiency/byte-efficiency-audit.d.ts +0 -16
  76. package/core/audits/byte-efficiency/byte-efficiency-audit.js +1 -38
  77. package/core/audits/byte-efficiency/offscreen-images.d.ts +0 -11
  78. package/core/audits/byte-efficiency/offscreen-images.js +0 -15
  79. package/core/audits/byte-efficiency/render-blocking-resources.js +27 -26
  80. package/core/audits/byte-efficiency/unminified-css.js +2 -2
  81. package/core/audits/byte-efficiency/unused-css-rules.js +3 -1
  82. package/core/audits/dobetterweb/uses-http2.d.ts +0 -13
  83. package/core/audits/dobetterweb/uses-http2.js +4 -38
  84. package/core/audits/font-display.js +2 -2
  85. package/core/audits/layout-shifts.js +2 -2
  86. package/core/audits/preload-fonts.d.ts +1 -5
  87. package/core/audits/preload-fonts.js +1 -10
  88. package/core/audits/prioritize-lcp-image.js +2 -2
  89. package/core/audits/redirects-http.d.ts +19 -0
  90. package/core/audits/redirects-http.js +75 -0
  91. package/core/audits/resource-summary.d.ts +2 -0
  92. package/core/audits/resource-summary.js +5 -4
  93. package/core/audits/seo/is-crawlable.d.ts +2 -1
  94. package/core/audits/seo/link-text.js +12 -0
  95. package/core/audits/uses-rel-preload.d.ts +1 -5
  96. package/core/audits/uses-rel-preload.js +5 -15
  97. package/core/audits/work-during-interaction.d.ts +2 -2
  98. package/core/audits/work-during-interaction.js +2 -2
  99. package/core/computed/js-bundles.d.ts +1 -1
  100. package/core/computed/metrics/cumulative-layout-shift.js +2 -2
  101. package/core/computed/metrics/lantern-first-contentful-paint.d.ts +13 -82
  102. package/core/computed/metrics/lantern-first-contentful-paint.js +16 -178
  103. package/core/computed/metrics/lantern-first-meaningful-paint.d.ts +13 -12
  104. package/core/computed/metrics/lantern-first-meaningful-paint.js +11 -49
  105. package/core/computed/metrics/lantern-interactive.d.ts +13 -21
  106. package/core/computed/metrics/lantern-interactive.js +12 -84
  107. package/core/computed/metrics/lantern-largest-contentful-paint.d.ts +13 -25
  108. package/core/computed/metrics/lantern-largest-contentful-paint.js +13 -80
  109. package/core/computed/metrics/lantern-max-potential-fid.d.ts +13 -24
  110. package/core/computed/metrics/lantern-max-potential-fid.js +12 -64
  111. package/core/computed/metrics/lantern-metric.d.ts +14 -18
  112. package/core/computed/metrics/lantern-metric.js +34 -30
  113. package/core/computed/metrics/lantern-speed-index.d.ts +13 -32
  114. package/core/computed/metrics/lantern-speed-index.js +13 -116
  115. package/core/computed/metrics/lantern-total-blocking-time.d.ts +13 -25
  116. package/core/computed/metrics/lantern-total-blocking-time.js +10 -94
  117. package/core/computed/metrics/metric.js +1 -3
  118. package/core/computed/module-duplication.d.ts +1 -1
  119. package/core/computed/navigation-insights.d.ts +36 -0
  120. package/core/computed/navigation-insights.js +35 -0
  121. package/core/computed/page-dependency-graph.js +3 -7
  122. package/core/computed/resource-summary.d.ts +10 -12
  123. package/core/computed/resource-summary.js +12 -19
  124. package/core/computed/trace-engine-result.d.ts +16 -3
  125. package/core/computed/trace-engine-result.js +19 -14
  126. package/core/computed/unused-css.d.ts +3 -1
  127. package/core/computed/unused-css.js +6 -5
  128. package/core/config/config-helpers.js +0 -4
  129. package/core/config/constants.js +0 -1
  130. package/core/config/default-config.js +63 -142
  131. package/core/config/experimental-config.js +11 -0
  132. package/core/config/filters.js +0 -1
  133. package/core/gather/driver/navigation.js +4 -1
  134. package/core/gather/driver/prepare.js +1 -1
  135. package/core/gather/driver/target-manager.js +4 -3
  136. package/core/gather/driver/wait-for-condition.js +2 -3
  137. package/core/gather/driver.d.ts +12 -0
  138. package/core/gather/driver.js +27 -0
  139. package/core/gather/gatherers/accessibility.js +4 -6
  140. package/core/gather/gatherers/css-usage.d.ts +0 -21
  141. package/core/gather/gatherers/css-usage.js +9 -119
  142. package/core/gather/gatherers/root-causes.d.ts +2 -2
  143. package/core/gather/gatherers/root-causes.js +6 -5
  144. package/core/gather/gatherers/stylesheets.d.ts +26 -0
  145. package/core/gather/gatherers/stylesheets.js +138 -0
  146. package/core/gather/gatherers/trace-elements.d.ts +4 -13
  147. package/core/gather/gatherers/trace-elements.js +42 -31
  148. package/core/gather/navigation-runner.js +3 -1
  149. package/core/gather/session.d.ts +8 -0
  150. package/core/gather/session.js +14 -1
  151. package/core/lib/emulation.js +2 -2
  152. package/core/lib/i18n/i18n.d.ts +0 -1
  153. package/core/lib/i18n/i18n.js +0 -2
  154. package/core/lib/lantern/cpu-node.d.ts +7 -1
  155. package/core/lib/lantern/cpu-node.js +12 -2
  156. package/core/lib/lantern/lantern-error.d.ts +8 -0
  157. package/core/lib/lantern/lantern-error.js +9 -0
  158. package/core/lib/lantern/metric.js +2 -6
  159. package/core/lib/lantern/metrics/first-contentful-paint.d.ts +79 -0
  160. package/core/lib/lantern/metrics/first-contentful-paint.js +200 -0
  161. package/core/lib/lantern/metrics/first-meaningful-paint.d.ts +6 -0
  162. package/core/lib/lantern/metrics/first-meaningful-paint.js +64 -0
  163. package/core/lib/lantern/metrics/interactive.d.ts +26 -0
  164. package/core/lib/lantern/metrics/interactive.js +112 -0
  165. package/core/lib/lantern/metrics/largest-contentful-paint.d.ts +19 -0
  166. package/core/lib/lantern/metrics/largest-contentful-paint.js +108 -0
  167. package/core/lib/lantern/metrics/max-potential-fid.d.ts +30 -0
  168. package/core/lib/lantern/metrics/max-potential-fid.js +92 -0
  169. package/core/lib/lantern/metrics/speed-index.d.ts +38 -0
  170. package/core/lib/lantern/metrics/speed-index.js +144 -0
  171. package/core/lib/lantern/metrics/total-blocking-time.d.ts +31 -0
  172. package/core/lib/lantern/metrics/total-blocking-time.js +128 -0
  173. package/core/lib/lantern/network-node.js +28 -4
  174. package/core/lib/lantern/page-dependency-graph.d.ts +4 -4
  175. package/core/lib/lantern/page-dependency-graph.js +19 -7
  176. package/core/lib/lantern/simulator/connection-pool.d.ts +2 -12
  177. package/core/lib/lantern/simulator/connection-pool.js +3 -22
  178. package/core/lib/lantern/simulator/network-analyzer.js +1 -12
  179. package/core/lib/lantern/simulator/simulator.d.ts +2 -4
  180. package/core/lib/lantern/simulator/simulator.js +7 -14
  181. package/core/lib/lantern/types/lantern.d.ts +5 -3
  182. package/core/lib/lantern-trace-saver.js +0 -1
  183. package/core/lib/lh-error.d.ts +9 -0
  184. package/core/lib/lh-error.js +13 -3
  185. package/core/lib/lighthouse-compatibility.js +53 -10
  186. package/core/lib/navigation-error.js +3 -0
  187. package/core/lib/network-request.d.ts +1 -0
  188. package/core/lib/network-request.js +29 -1
  189. package/core/lib/proto-preprocessor.js +24 -8
  190. package/core/lib/tracehouse/trace-processor.js +15 -2
  191. package/core/runner.js +18 -25
  192. package/dist/report/bundle.esm.js +11 -112
  193. package/dist/report/flow.js +16 -117
  194. package/dist/report/standalone.js +12 -113
  195. package/flow-report/src/help-dialog.tsx +0 -1
  196. package/flow-report/src/i18n/i18n.d.ts +0 -4
  197. package/flow-report/src/i18n/ui-strings.d.ts +0 -1
  198. package/flow-report/src/i18n/ui-strings.js +0 -2
  199. package/flow-report/src/summary/category.tsx +10 -12
  200. package/package.json +7 -7
  201. package/readme.md +4 -4
  202. package/report/assets/styles.css +1 -60
  203. package/report/assets/templates.html +0 -95
  204. package/report/renderer/category-renderer.js +1 -1
  205. package/report/renderer/components.d.ts +2 -2
  206. package/report/renderer/components.js +2 -103
  207. package/report/renderer/dom.d.ts +1 -1
  208. package/report/renderer/dom.js +1 -1
  209. package/report/renderer/performance-category-renderer.d.ts +0 -7
  210. package/report/renderer/performance-category-renderer.js +1 -32
  211. package/report/renderer/report-renderer.js +3 -13
  212. package/report/renderer/report-ui-features.js +1 -1
  213. package/report/renderer/report-utils.d.ts +0 -1
  214. package/report/renderer/report-utils.js +13 -13
  215. package/shared/localization/locales/ar-XB.json +62 -329
  216. package/shared/localization/locales/ar.json +62 -329
  217. package/shared/localization/locales/bg.json +62 -329
  218. package/shared/localization/locales/ca.json +62 -329
  219. package/shared/localization/locales/cs.json +62 -329
  220. package/shared/localization/locales/da.json +62 -329
  221. package/shared/localization/locales/de.json +62 -329
  222. package/shared/localization/locales/el.json +62 -329
  223. package/shared/localization/locales/en-GB.json +62 -329
  224. package/shared/localization/locales/en-US.json +101 -332
  225. package/shared/localization/locales/en-XA.json +62 -329
  226. package/shared/localization/locales/en-XL.json +101 -332
  227. package/shared/localization/locales/es-419.json +62 -329
  228. package/shared/localization/locales/es.json +62 -329
  229. package/shared/localization/locales/fi.json +62 -329
  230. package/shared/localization/locales/fil.json +61 -328
  231. package/shared/localization/locales/fr.json +62 -329
  232. package/shared/localization/locales/he.json +62 -329
  233. package/shared/localization/locales/hi.json +62 -329
  234. package/shared/localization/locales/hr.json +63 -330
  235. package/shared/localization/locales/hu.json +62 -329
  236. package/shared/localization/locales/id.json +62 -329
  237. package/shared/localization/locales/it.json +62 -329
  238. package/shared/localization/locales/ja.json +62 -329
  239. package/shared/localization/locales/ko.json +62 -329
  240. package/shared/localization/locales/lt.json +62 -329
  241. package/shared/localization/locales/lv.json +62 -329
  242. package/shared/localization/locales/nl.json +62 -329
  243. package/shared/localization/locales/no.json +62 -329
  244. package/shared/localization/locales/pl.json +61 -328
  245. package/shared/localization/locales/pt-PT.json +62 -329
  246. package/shared/localization/locales/pt.json +64 -331
  247. package/shared/localization/locales/ro.json +62 -329
  248. package/shared/localization/locales/ru.json +62 -329
  249. package/shared/localization/locales/sk.json +62 -329
  250. package/shared/localization/locales/sl.json +62 -329
  251. package/shared/localization/locales/sr-Latn.json +62 -329
  252. package/shared/localization/locales/sr.json +62 -329
  253. package/shared/localization/locales/sv.json +62 -329
  254. package/shared/localization/locales/ta.json +61 -328
  255. package/shared/localization/locales/te.json +62 -329
  256. package/shared/localization/locales/th.json +62 -329
  257. package/shared/localization/locales/tr.json +62 -329
  258. package/shared/localization/locales/uk.json +62 -329
  259. package/shared/localization/locales/vi.json +61 -328
  260. package/shared/localization/locales/zh-HK.json +62 -329
  261. package/shared/localization/locales/zh-TW.json +61 -328
  262. package/shared/localization/locales/zh.json +62 -329
  263. package/tsconfig.json +1 -3
  264. package/types/artifacts.d.ts +21 -110
  265. package/types/audit.d.ts +0 -10
  266. package/types/config.d.ts +0 -1
  267. package/types/externs.d.ts +0 -2
  268. package/types/gatherer.d.ts +3 -0
  269. package/types/internal/smokehouse.d.ts +1 -1
  270. package/types/lh.d.ts +0 -2
  271. package/types/lhr/audit-details.d.ts +9 -4
  272. package/types/lhr/lhr.d.ts +0 -4
  273. package/types/lhr/settings.d.ts +0 -4
  274. package/core/audits/accessibility/duplicate-id-active.js +0 -42
  275. package/core/audits/content-width.d.ts +0 -17
  276. package/core/audits/content-width.js +0 -76
  277. package/core/audits/installable-manifest.d.ts +0 -111
  278. package/core/audits/installable-manifest.js +0 -248
  279. package/core/audits/layout-shift-elements.d.ts +0 -16
  280. package/core/audits/layout-shift-elements.js +0 -101
  281. package/core/audits/manual/pwa-cross-browser.d.ts +0 -12
  282. package/core/audits/manual/pwa-cross-browser.js +0 -40
  283. package/core/audits/manual/pwa-each-page-has-url.d.ts +0 -12
  284. package/core/audits/manual/pwa-each-page-has-url.js +0 -38
  285. package/core/audits/manual/pwa-page-transitions.d.ts +0 -12
  286. package/core/audits/manual/pwa-page-transitions.js +0 -38
  287. package/core/audits/maskable-icon.d.ts +0 -25
  288. package/core/audits/maskable-icon.js +0 -70
  289. package/core/audits/multi-check-audit.d.ts +0 -28
  290. package/core/audits/multi-check-audit.js +0 -82
  291. package/core/audits/no-unload-listeners.d.ts +0 -16
  292. package/core/audits/no-unload-listeners.js +0 -86
  293. package/core/audits/performance-budget.d.ts +0 -39
  294. package/core/audits/performance-budget.js +0 -154
  295. package/core/audits/script-elements-test-audit.d.ts +0 -14
  296. package/core/audits/script-elements-test-audit.js +0 -29
  297. package/core/audits/seo/plugins.d.ts +0 -15
  298. package/core/audits/seo/plugins.js +0 -150
  299. package/core/audits/seo/tap-targets.d.ts +0 -50
  300. package/core/audits/seo/tap-targets.js +0 -352
  301. package/core/audits/splash-screen.d.ts +0 -37
  302. package/core/audits/splash-screen.js +0 -98
  303. package/core/audits/themed-omnibox.d.ts +0 -42
  304. package/core/audits/themed-omnibox.js +0 -102
  305. package/core/audits/timing-budget.d.ts +0 -43
  306. package/core/audits/timing-budget.js +0 -175
  307. package/core/computed/manifest-values.d.ts +0 -24
  308. package/core/computed/manifest-values.js +0 -136
  309. package/core/config/budget.d.ts +0 -74
  310. package/core/config/budget.js +0 -340
  311. package/core/config/metrics-to-audits.d.ts +0 -19
  312. package/core/config/metrics-to-audits.js +0 -62
  313. package/core/gather/driver/service-workers.d.ts +0 -16
  314. package/core/gather/driver/service-workers.js +0 -52
  315. package/core/gather/gatherers/dobetterweb/tags-blocking-first-paint.d.ts +0 -47
  316. package/core/gather/gatherers/dobetterweb/tags-blocking-first-paint.js +0 -233
  317. package/core/gather/gatherers/global-listeners.d.ts +0 -27
  318. package/core/gather/gatherers/global-listeners.js +0 -108
  319. package/core/gather/gatherers/installability-errors.d.ts +0 -17
  320. package/core/gather/gatherers/installability-errors.js +0 -56
  321. package/core/gather/gatherers/script-elements.d.ts +0 -21
  322. package/core/gather/gatherers/script-elements.js +0 -100
  323. package/core/gather/gatherers/seo/embedded-content.d.ts +0 -10
  324. package/core/gather/gatherers/seo/embedded-content.js +0 -63
  325. package/core/gather/gatherers/seo/tap-targets.d.ts +0 -21
  326. package/core/gather/gatherers/seo/tap-targets.js +0 -389
  327. package/core/gather/gatherers/service-worker.d.ts +0 -10
  328. package/core/gather/gatherers/service-worker.js +0 -32
  329. package/core/gather/gatherers/web-app-manifest.d.ts +0 -32
  330. package/core/gather/gatherers/web-app-manifest.js +0 -106
  331. package/report/renderer/pwa-category-renderer.d.ts +0 -55
  332. package/report/renderer/pwa-category-renderer.js +0 -184
  333. package/third-party/chromium-synchronization/installability-errors-test.js +0 -79
  334. package/types/lhr/budget.d.ts +0 -60
@@ -498,9 +498,8 @@ async function waitForFullyLoaded(session, networkMonitor, options) {
498
498
  if (await isPageHung(session)) {
499
499
  log.warn('waitFor', 'Page appears to be hung, killing JavaScript...');
500
500
  // We don't await these, as we want to exit with PAGE_HUNG
501
- void session.sendCommand('Emulation.setScriptExecutionDisabled', {value: true})
502
- .catch(_ => {});
503
- void session.sendCommand('Runtime.terminateExecution').catch(_ => {});
501
+ void session.sendCommandAndIgnore('Emulation.setScriptExecutionDisabled', {value: true});
502
+ void session.sendCommandAndIgnore('Runtime.terminateExecution');
504
503
  throw new LighthouseError(LighthouseError.errors.PAGE_HUNG);
505
504
  }
506
505
 
@@ -14,6 +14,10 @@ export class Driver implements LH.Gatherer.Driver {
14
14
  /** @type {Fetcher|undefined} */
15
15
  _fetcher: Fetcher | undefined;
16
16
  defaultSession: import("../../types/gatherer.js").default.ProtocolSession;
17
+ fatalRejection: {
18
+ promise: Promise<never>;
19
+ rej: (_: Error) => void;
20
+ };
17
21
  /** @return {LH.Gatherer.Driver['executionContext']} */
18
22
  get executionContext(): ExecutionContext;
19
23
  get fetcher(): any;
@@ -23,6 +27,14 @@ export class Driver implements LH.Gatherer.Driver {
23
27
  url(): Promise<string>;
24
28
  /** @return {Promise<void>} */
25
29
  connect(): Promise<void>;
30
+ /**
31
+ * If the target crashes, we can't continue gathering.
32
+ *
33
+ * FWIW, if the target unexpectedly detaches (eg the user closed the tab), pptr will
34
+ * catch that and reject into our this._cdpSession.send, which we'll alrady handle appropriately
35
+ * @return {void}
36
+ */
37
+ listenForCrashes(): void;
26
38
  /** @return {Promise<void>} */
27
39
  disconnect(): Promise<void>;
28
40
  }
@@ -8,6 +8,7 @@ import log from 'lighthouse-logger';
8
8
 
9
9
  import {ExecutionContext} from './driver/execution-context.js';
10
10
  import {TargetManager} from './driver/target-manager.js';
11
+ import {LighthouseError} from '../lib/lh-error.js';
11
12
  import {Fetcher} from './fetcher.js';
12
13
  import {NetworkMonitor} from './driver/network-monitor.js';
13
14
 
@@ -26,6 +27,7 @@ const throwingSession = {
26
27
  once: throwNotConnectedFn,
27
28
  off: throwNotConnectedFn,
28
29
  sendCommand: throwNotConnectedFn,
30
+ sendCommandAndIgnore: throwNotConnectedFn,
29
31
  dispose: throwNotConnectedFn,
30
32
  };
31
33
 
@@ -46,6 +48,12 @@ class Driver {
46
48
  this._fetcher = undefined;
47
49
 
48
50
  this.defaultSession = throwingSession;
51
+
52
+ // Poor man's Promise.withResolvers()
53
+ /** @param {Error} _ */
54
+ let rej = _ => {};
55
+ const promise = /** @type {Promise<never>} */ (new Promise((_, theRej) => rej = theRej));
56
+ this.fatalRejection = {promise, rej};
49
57
  }
50
58
 
51
59
  /** @return {LH.Gatherer.Driver['executionContext']} */
@@ -85,11 +93,30 @@ class Driver {
85
93
  this._networkMonitor = new NetworkMonitor(this._targetManager);
86
94
  await this._networkMonitor.enable();
87
95
  this.defaultSession = this._targetManager.rootSession();
96
+ this.listenForCrashes();
88
97
  this._executionContext = new ExecutionContext(this.defaultSession);
89
98
  this._fetcher = new Fetcher(this.defaultSession);
90
99
  log.timeEnd(status);
91
100
  }
92
101
 
102
+ /**
103
+ * If the target crashes, we can't continue gathering.
104
+ *
105
+ * FWIW, if the target unexpectedly detaches (eg the user closed the tab), pptr will
106
+ * catch that and reject into our this._cdpSession.send, which we'll alrady handle appropriately
107
+ * @return {void}
108
+ */
109
+ listenForCrashes() {
110
+ this.defaultSession.on('Inspector.targetCrashed', async _ => {
111
+ log.error('Driver', 'Inspector.targetCrashed');
112
+ // Manually detach so no more CDP traffic is attempted.
113
+ // Don't await, else our rejection will be a 'Target closed' protocol error on cross-talk CDP calls.
114
+ void this.defaultSession.dispose();
115
+ this.fatalRejection.rej(new LighthouseError(LighthouseError.errors.TARGET_CRASHED));
116
+ });
117
+ }
118
+
119
+
93
120
  /** @return {Promise<void>} */
94
121
  async disconnect() {
95
122
  if (this.defaultSession === throwingSession) return;
@@ -43,16 +43,15 @@ async function runA11yChecks() {
43
43
  'area-alt': {enabled: false},
44
44
  'aria-allowed-role': {enabled: true},
45
45
  'aria-braille-equivalent': {enabled: false},
46
- 'aria-conditional-attr': {enabled: false},
47
- 'aria-deprecated-role': {enabled: false},
46
+ 'aria-conditional-attr': {enabled: true},
47
+ 'aria-deprecated-role': {enabled: true},
48
48
  'aria-dialog-name': {enabled: true},
49
- 'aria-prohibited-attr': {enabled: false},
49
+ 'aria-prohibited-attr': {enabled: true},
50
50
  'aria-roledescription': {enabled: false},
51
51
  'aria-treeitem-name': {enabled: true},
52
52
  'aria-text': {enabled: true},
53
53
  'audio-caption': {enabled: false},
54
54
  'blink': {enabled: false},
55
- 'duplicate-id-active': {enabled: true},
56
55
  'duplicate-id': {enabled: false},
57
56
  'empty-heading': {enabled: true},
58
57
  'frame-focusable-content': {enabled: false},
@@ -79,8 +78,7 @@ async function runA11yChecks() {
79
78
  'tabindex': {enabled: true},
80
79
  'table-duplicate-name': {enabled: true},
81
80
  'table-fake-caption': {enabled: true},
82
- // TODO: https://github.com/GoogleChrome/lighthouse/issues/15824
83
- // 'target-size': {enabled: true},
81
+ 'target-size': {enabled: true},
84
82
  'td-has-header': {enabled: true},
85
83
  },
86
84
  });
@@ -1,26 +1,5 @@
1
1
  export default CSSUsage;
2
2
  declare class CSSUsage extends BaseGatherer {
3
- /** @type {LH.Gatherer.ProtocolSession|undefined} */
4
- _session: LH.Gatherer.ProtocolSession | undefined;
5
- /** @type {Map<string, Promise<LH.Artifacts.CSSStyleSheetInfo|Error>>} */
6
- _sheetPromises: Map<string, Promise<LH.Artifacts.CSSStyleSheetInfo | Error>>;
7
- /**
8
- * Initialize as undefined so we can assert results are fetched.
9
- * @type {LH.Crdp.CSS.RuleUsage[]|undefined}
10
- */
11
- _ruleUsage: LH.Crdp.CSS.RuleUsage[] | undefined;
12
- /**
13
- * @param {LH.Crdp.CSS.StyleSheetAddedEvent} event
14
- */
15
- _onStylesheetAdded(event: LH.Crdp.CSS.StyleSheetAddedEvent): void;
16
- /**
17
- * @param {LH.Gatherer.Context} context
18
- */
19
- startInstrumentation(context: LH.Gatherer.Context): Promise<void>;
20
- /**
21
- * @param {LH.Gatherer.Context} context
22
- */
23
- stopInstrumentation(context: LH.Gatherer.Context): Promise<void>;
24
3
  /**
25
4
  * @param {LH.Gatherer.Context} context
26
5
  * @return {Promise<LH.Artifacts['CSSUsage']>}
@@ -8,145 +8,35 @@
8
8
  * @fileoverview Tracks unused CSS rules.
9
9
  */
10
10
 
11
- import log from 'lighthouse-logger';
12
-
13
11
  import BaseGatherer from '../base-gatherer.js';
14
- import {Sentry} from '../../lib/sentry.js';
15
12
 
16
13
  class CSSUsage extends BaseGatherer {
17
- constructor() {
18
- super();
19
- /** @type {LH.Gatherer.ProtocolSession|undefined} */
20
- this._session = undefined;
21
- /** @type {Map<string, Promise<LH.Artifacts.CSSStyleSheetInfo|Error>>} */
22
- this._sheetPromises = new Map();
23
- /**
24
- * Initialize as undefined so we can assert results are fetched.
25
- * @type {LH.Crdp.CSS.RuleUsage[]|undefined}
26
- */
27
- this._ruleUsage = undefined;
28
- this._onStylesheetAdded = this._onStylesheetAdded.bind(this);
29
- }
30
-
31
14
  /** @type {LH.Gatherer.GathererMeta} */
32
15
  meta = {
33
- supportedModes: ['snapshot', 'timespan', 'navigation'],
16
+ supportedModes: ['snapshot', 'navigation'],
34
17
  };
35
18
 
36
- /**
37
- * @param {LH.Crdp.CSS.StyleSheetAddedEvent} event
38
- */
39
- _onStylesheetAdded(event) {
40
- if (!this._session) throw new Error('Session not initialized');
41
- const styleSheetId = event.header.styleSheetId;
42
- const sheetPromise = this._session.sendCommand('CSS.getStyleSheetText', {styleSheetId})
43
- .then(content => ({
44
- header: event.header,
45
- content: content.text,
46
- }))
47
- .catch(/** @param {Error} err */ (err) => {
48
- log.warn(
49
- 'CSSUsage',
50
- `Error fetching content of stylesheet with URL "${event.header.sourceURL}"`
51
- );
52
- Sentry.captureException(err, {
53
- tags: {
54
- gatherer: 'CSSUsage',
55
- },
56
- extra: {
57
- url: event.header.sourceURL,
58
- },
59
- level: 'error',
60
- });
61
- return err;
62
- });
63
- this._sheetPromises.set(styleSheetId, sheetPromise);
64
- }
65
-
66
19
  /**
67
20
  * @param {LH.Gatherer.Context} context
21
+ * @return {Promise<LH.Artifacts['CSSUsage']>}
68
22
  */
69
- async startInstrumentation(context) {
23
+ async getArtifact(context) {
70
24
  const session = context.driver.defaultSession;
71
- this._session = session;
72
-
73
- // Calling `CSS.enable` will emit events for stylesheets currently on the page.
74
- // We want to ignore these events in navigation mode because they are not relevant to the
75
- // navigation that is about to happen. Adding the event listener *after* calling `CSS.enable`
76
- // ensures that the events for pre-existing stylesheets are ignored.
77
- const isNavigation = context.gatherMode === 'navigation';
78
- if (!isNavigation) {
79
- session.on('CSS.styleSheetAdded', this._onStylesheetAdded);
80
- }
25
+ const executionContext = context.driver.executionContext;
81
26
 
82
27
  await session.sendCommand('DOM.enable');
83
28
  await session.sendCommand('CSS.enable');
84
29
  await session.sendCommand('CSS.startRuleUsageTracking');
85
30
 
86
- if (isNavigation) {
87
- session.on('CSS.styleSheetAdded', this._onStylesheetAdded);
88
- }
89
- }
90
-
91
-
92
- /**
93
- * @param {LH.Gatherer.Context} context
94
- */
95
- async stopInstrumentation(context) {
96
- const session = context.driver.defaultSession;
97
- const coverageResponse = await session.sendCommand('CSS.stopRuleUsageTracking');
98
- this._ruleUsage = coverageResponse.ruleUsage;
99
- session.off('CSS.styleSheetAdded', this._onStylesheetAdded);
100
-
101
- // Ensure we finish fetching all stylesheet contents before disabling the CSS domain
102
- await Promise.all(this._sheetPromises.values());
31
+ // Force style to recompute.
32
+ // Doesn't appear to be necessary in newer versions of Chrome.
33
+ await executionContext.evaluateAsync('getComputedStyle(document.body)');
103
34
 
35
+ const {ruleUsage} = await session.sendCommand('CSS.stopRuleUsageTracking');
104
36
  await session.sendCommand('CSS.disable');
105
37
  await session.sendCommand('DOM.disable');
106
- }
107
-
108
- /**
109
- * @param {LH.Gatherer.Context} context
110
- * @return {Promise<LH.Artifacts['CSSUsage']>}
111
- */
112
- async getArtifact(context) {
113
- const executionContext = context.driver.executionContext;
114
-
115
- if (context.gatherMode === 'snapshot') {
116
- await this.startInstrumentation(context);
117
-
118
- // Force style to recompute.
119
- // Doesn't appear to be necessary in newer versions of Chrome.
120
- await executionContext.evaluateAsync('getComputedStyle(document.body)');
121
-
122
- await this.stopInstrumentation(context);
123
- }
124
-
125
- /** @type {Map<string, LH.Artifacts.CSSStyleSheetInfo>} */
126
- const dedupedStylesheets = new Map();
127
- const sheets = await Promise.all(this._sheetPromises.values());
128
-
129
- for (const sheet of sheets) {
130
- // Erroneous sheets will be reported via sentry and the log.
131
- // We can ignore them here without throwing a fatal error.
132
- if (sheet instanceof Error) {
133
- continue;
134
- }
135
-
136
- // Exclude empty stylesheets.
137
- if (sheet.header.length === 0) {
138
- continue;
139
- }
140
-
141
- dedupedStylesheets.set(sheet.content, sheet);
142
- }
143
-
144
- if (!this._ruleUsage) throw new Error('Issue collecting rule usages');
145
38
 
146
- return {
147
- rules: this._ruleUsage,
148
- stylesheets: Array.from(dedupedStylesheets.values()),
149
- };
39
+ return ruleUsage;
150
40
  }
151
41
  }
152
42
 
@@ -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
 
@@ -0,0 +1,26 @@
1
+ export default Stylesheets;
2
+ declare class Stylesheets extends BaseGatherer {
3
+ /** @type {LH.Gatherer.ProtocolSession|undefined} */
4
+ _session: LH.Gatherer.ProtocolSession | undefined;
5
+ /** @type {Map<string, Promise<LH.Artifacts.CSSStyleSheetInfo|null>>} */
6
+ _sheetPromises: Map<string, Promise<LH.Artifacts.CSSStyleSheetInfo | null>>;
7
+ /**
8
+ * @param {LH.Crdp.CSS.StyleSheetAddedEvent} event
9
+ */
10
+ _onStylesheetAdded(event: LH.Crdp.CSS.StyleSheetAddedEvent): void;
11
+ /**
12
+ * @param {LH.Gatherer.Context} context
13
+ */
14
+ startInstrumentation(context: LH.Gatherer.Context): Promise<void>;
15
+ /**
16
+ * @param {LH.Gatherer.Context} context
17
+ */
18
+ stopInstrumentation(context: LH.Gatherer.Context): Promise<void>;
19
+ /**
20
+ * @param {LH.Gatherer.Context} context
21
+ * @return {Promise<LH.Artifacts['Stylesheets']>}
22
+ */
23
+ getArtifact(context: LH.Gatherer.Context): Promise<LH.Artifacts['Stylesheets']>;
24
+ }
25
+ import BaseGatherer from '../base-gatherer.js';
26
+ //# sourceMappingURL=stylesheets.d.ts.map
@@ -0,0 +1,138 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2024 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ /**
8
+ * @fileoverview Collects all stylesheets and their contents.
9
+ */
10
+
11
+ import log from 'lighthouse-logger';
12
+
13
+ import BaseGatherer from '../base-gatherer.js';
14
+ import {Sentry} from '../../lib/sentry.js';
15
+
16
+ class Stylesheets extends BaseGatherer {
17
+ constructor() {
18
+ super();
19
+ /** @type {LH.Gatherer.ProtocolSession|undefined} */
20
+ this._session = undefined;
21
+ /** @type {Map<string, Promise<LH.Artifacts.CSSStyleSheetInfo|null>>} */
22
+ this._sheetPromises = new Map();
23
+ this._onStylesheetAdded = this._onStylesheetAdded.bind(this);
24
+ }
25
+
26
+ /** @type {LH.Gatherer.GathererMeta} */
27
+ meta = {
28
+ supportedModes: ['snapshot', 'timespan', 'navigation'],
29
+ };
30
+
31
+ /**
32
+ * @param {LH.Crdp.CSS.StyleSheetAddedEvent} event
33
+ */
34
+ _onStylesheetAdded(event) {
35
+ if (!this._session) throw new Error('Session not initialized');
36
+ const styleSheetId = event.header.styleSheetId;
37
+ const sheetPromise = this._session.sendCommand('CSS.getStyleSheetText', {styleSheetId})
38
+ .then(content => ({
39
+ header: event.header,
40
+ content: content.text,
41
+ }))
42
+ .catch(err => {
43
+ log.warn(
44
+ 'Stylesheets',
45
+ `Error fetching content of stylesheet with URL "${event.header.sourceURL}"`
46
+ );
47
+ Sentry.captureException(err, {
48
+ tags: {
49
+ gatherer: 'Stylesheets',
50
+ },
51
+ extra: {
52
+ url: event.header.sourceURL,
53
+ },
54
+ level: 'error',
55
+ });
56
+ return null;
57
+ });
58
+ this._sheetPromises.set(styleSheetId, sheetPromise);
59
+ }
60
+
61
+ /**
62
+ * @param {LH.Gatherer.Context} context
63
+ */
64
+ async startInstrumentation(context) {
65
+ const session = context.driver.defaultSession;
66
+ this._session = session;
67
+
68
+ // Calling `CSS.enable` will emit events for stylesheets currently on the page.
69
+ // We want to ignore these events in navigation mode because they are not relevant to the
70
+ // navigation that is about to happen. Adding the event listener *after* calling `CSS.enable`
71
+ // ensures that the events for pre-existing stylesheets are ignored.
72
+ const isNavigation = context.gatherMode === 'navigation';
73
+ if (!isNavigation) {
74
+ session.on('CSS.styleSheetAdded', this._onStylesheetAdded);
75
+ }
76
+
77
+ await session.sendCommand('DOM.enable');
78
+ await session.sendCommand('CSS.enable');
79
+
80
+ if (isNavigation) {
81
+ session.on('CSS.styleSheetAdded', this._onStylesheetAdded);
82
+ }
83
+ }
84
+
85
+
86
+ /**
87
+ * @param {LH.Gatherer.Context} context
88
+ */
89
+ async stopInstrumentation(context) {
90
+ const session = context.driver.defaultSession;
91
+ session.off('CSS.styleSheetAdded', this._onStylesheetAdded);
92
+
93
+ // Ensure we finish fetching all stylesheet contents before disabling the CSS domain
94
+ await Promise.all(this._sheetPromises.values());
95
+
96
+ await session.sendCommand('CSS.disable');
97
+ await session.sendCommand('DOM.disable');
98
+ }
99
+
100
+ /**
101
+ * @param {LH.Gatherer.Context} context
102
+ * @return {Promise<LH.Artifacts['Stylesheets']>}
103
+ */
104
+ async getArtifact(context) {
105
+ const executionContext = context.driver.executionContext;
106
+
107
+ if (context.gatherMode === 'snapshot') {
108
+ await this.startInstrumentation(context);
109
+
110
+ // Force style to recompute.
111
+ // Doesn't appear to be necessary in newer versions of Chrome.
112
+ await executionContext.evaluateAsync('getComputedStyle(document.body)');
113
+
114
+ await this.stopInstrumentation(context);
115
+ }
116
+
117
+ /** @type {Map<string, LH.Artifacts.CSSStyleSheetInfo>} */
118
+ const dedupedStylesheets = new Map();
119
+ const sheets = await Promise.all(this._sheetPromises.values());
120
+
121
+ for (const sheet of sheets) {
122
+ // Erroneous sheets will be reported via sentry and the log.
123
+ // We can ignore them here without throwing a fatal error.
124
+ if (!sheet) continue;
125
+
126
+ // Exclude empty stylesheets.
127
+ if (sheet.header.length === 0) {
128
+ continue;
129
+ }
130
+
131
+ dedupedStylesheets.set(sheet.content, sheet);
132
+ }
133
+
134
+ return Array.from(dedupedStylesheets.values());
135
+ }
136
+ }
137
+
138
+ export default Stylesheets;
@@ -9,37 +9,28 @@ export type TraceElementData = {
9
9
  type?: string;
10
10
  };
11
11
  declare class TraceElements extends BaseGatherer {
12
- /**
13
- * This function finds the top (up to 15) elements that shift on the page.
14
- *
15
- * @param {LH.Trace} trace
16
- * @param {LH.Gatherer.Context} context
17
- * @return {Promise<Array<{nodeId: number}>>}
18
- */
19
- static getTopLayoutShiftElements(trace: LH.Trace, context: LH.Gatherer.Context): Promise<Array<{
20
- nodeId: number;
21
- }>>;
22
12
  /**
23
13
  * We want to a single representative node to represent the shift, so let's pick
24
14
  * the one with the largest impact (size x distance moved).
25
15
  *
26
16
  * @param {LH.Artifacts.TraceImpactedNode[]} impactedNodes
27
17
  * @param {Map<number, number>} impactByNodeId
18
+ * @param {import('../../lib/trace-engine.js').SaneSyntheticLayoutShift} event Only for debugging
28
19
  * @return {number|undefined}
29
20
  */
30
- static getBiggestImpactNodeForShiftEvent(impactedNodes: LH.Artifacts.TraceImpactedNode[], impactByNodeId: Map<number, number>): number | undefined;
21
+ static getBiggestImpactNodeForShiftEvent(impactedNodes: LH.Artifacts.TraceImpactedNode[], impactByNodeId: Map<number, number>, event: import('../../lib/trace-engine.js').SaneSyntheticLayoutShift): number | undefined;
31
22
  /**
32
23
  * This function finds the top (up to 15) layout shifts on the page, and returns
33
24
  * the id of the largest impacted node of each shift, along with any related nodes
34
25
  * that may have caused the shift.
35
26
  *
36
27
  * @param {LH.Trace} trace
37
- * @param {LH.Artifacts.TraceEngineResult} traceEngineResult
28
+ * @param {LH.Artifacts.TraceEngineResult['data']} traceEngineResult
38
29
  * @param {LH.Artifacts.TraceEngineRootCauses} rootCauses
39
30
  * @param {LH.Gatherer.Context} context
40
31
  * @return {Promise<Array<{nodeId: number}>>}
41
32
  */
42
- 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<{
43
34
  nodeId: number;
44
35
  }>>;
45
36
  /**