lighthouse 9.5.0-dev.20220607 → 9.6.2

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 (262) hide show
  1. package/dist/report/bundle.esm.js +467 -520
  2. package/dist/report/flow.js +22 -22
  3. package/dist/report/standalone.js +19 -19
  4. package/flow-report/src/summary/category.tsx +1 -1
  5. package/flow-report/src/topbar.tsx +2 -10
  6. package/flow-report/src/util.ts +1 -1
  7. package/flow-report/test/topbar-test.tsx +8 -7
  8. package/jest.config.js +1 -1
  9. package/lighthouse-cli/.eslintrc.cjs +4 -3
  10. package/lighthouse-cli/bin.js +1 -0
  11. package/lighthouse-cli/cli-flags.js +4 -3
  12. package/lighthouse-cli/commands/commands.js +1 -0
  13. package/lighthouse-cli/commands/list-audits.js +1 -0
  14. package/lighthouse-cli/commands/list-locales.js +1 -0
  15. package/lighthouse-cli/commands/list-trace-categories.js +1 -0
  16. package/lighthouse-cli/index.js +1 -0
  17. package/lighthouse-cli/printer.js +1 -0
  18. package/lighthouse-cli/run.js +24 -9
  19. package/lighthouse-cli/sentry-prompt.js +1 -0
  20. package/lighthouse-cli/test/smokehouse/core-tests.js +1 -0
  21. package/lighthouse-cli/test/smokehouse/frontends/back-compat-util.js +1 -0
  22. package/lighthouse-cli/test/smokehouse/frontends/lib.js +1 -0
  23. package/lighthouse-cli/test/smokehouse/frontends/node.js +1 -0
  24. package/lighthouse-cli/test/smokehouse/frontends/smokehouse-bin.js +1 -0
  25. package/lighthouse-cli/test/smokehouse/lib/child-process-error.js +1 -0
  26. package/lighthouse-cli/test/smokehouse/lib/concurrent-mapper.js +1 -0
  27. package/lighthouse-cli/test/smokehouse/lib/local-console.js +1 -0
  28. package/lighthouse-cli/test/smokehouse/lighthouse-runners/bundle.js +23 -109
  29. package/lighthouse-cli/test/smokehouse/lighthouse-runners/cli.js +3 -2
  30. package/lighthouse-cli/test/smokehouse/lighthouse-runners/devtools.js +19 -28
  31. package/lighthouse-cli/test/smokehouse/report-assert.js +4 -3
  32. package/lighthouse-cli/test/smokehouse/smokehouse.js +1 -1
  33. package/lighthouse-cli/test/smokehouse/version-check-test.js +3 -0
  34. package/lighthouse-cli/test/smokehouse/version-check.js +1 -0
  35. package/lighthouse-core/audits/accessibility/accesskeys.js +1 -1
  36. package/lighthouse-core/audits/accessibility/aria-allowed-attr.js +1 -1
  37. package/lighthouse-core/audits/accessibility/aria-command-name.js +1 -1
  38. package/lighthouse-core/audits/accessibility/aria-hidden-body.js +1 -1
  39. package/lighthouse-core/audits/accessibility/aria-hidden-focus.js +1 -1
  40. package/lighthouse-core/audits/accessibility/aria-input-field-name.js +1 -1
  41. package/lighthouse-core/audits/accessibility/aria-meter-name.js +1 -1
  42. package/lighthouse-core/audits/accessibility/aria-progressbar-name.js +1 -1
  43. package/lighthouse-core/audits/accessibility/aria-required-attr.js +1 -1
  44. package/lighthouse-core/audits/accessibility/aria-required-children.js +1 -1
  45. package/lighthouse-core/audits/accessibility/aria-required-parent.js +1 -1
  46. package/lighthouse-core/audits/accessibility/aria-roles.js +1 -1
  47. package/lighthouse-core/audits/accessibility/aria-toggle-field-name.js +1 -1
  48. package/lighthouse-core/audits/accessibility/aria-tooltip-name.js +1 -1
  49. package/lighthouse-core/audits/accessibility/aria-treeitem-name.js +1 -1
  50. package/lighthouse-core/audits/accessibility/aria-valid-attr-value.js +1 -1
  51. package/lighthouse-core/audits/accessibility/aria-valid-attr.js +1 -1
  52. package/lighthouse-core/audits/accessibility/button-name.js +1 -1
  53. package/lighthouse-core/audits/accessibility/bypass.js +1 -1
  54. package/lighthouse-core/audits/accessibility/color-contrast.js +1 -1
  55. package/lighthouse-core/audits/accessibility/definition-list.js +1 -1
  56. package/lighthouse-core/audits/accessibility/dlitem.js +1 -1
  57. package/lighthouse-core/audits/accessibility/document-title.js +1 -1
  58. package/lighthouse-core/audits/accessibility/duplicate-id-active.js +1 -1
  59. package/lighthouse-core/audits/accessibility/duplicate-id-aria.js +1 -1
  60. package/lighthouse-core/audits/accessibility/form-field-multiple-labels.js +1 -1
  61. package/lighthouse-core/audits/accessibility/frame-title.js +1 -1
  62. package/lighthouse-core/audits/accessibility/heading-order.js +1 -1
  63. package/lighthouse-core/audits/accessibility/html-has-lang.js +1 -1
  64. package/lighthouse-core/audits/accessibility/html-lang-valid.js +1 -1
  65. package/lighthouse-core/audits/accessibility/image-alt.js +1 -1
  66. package/lighthouse-core/audits/accessibility/input-image-alt.js +1 -1
  67. package/lighthouse-core/audits/accessibility/label.js +1 -1
  68. package/lighthouse-core/audits/accessibility/link-name.js +1 -1
  69. package/lighthouse-core/audits/accessibility/list.js +1 -1
  70. package/lighthouse-core/audits/accessibility/listitem.js +5 -5
  71. package/lighthouse-core/audits/accessibility/meta-refresh.js +1 -1
  72. package/lighthouse-core/audits/accessibility/meta-viewport.js +1 -1
  73. package/lighthouse-core/audits/accessibility/object-alt.js +1 -1
  74. package/lighthouse-core/audits/accessibility/tabindex.js +1 -1
  75. package/lighthouse-core/audits/accessibility/td-headers-attr.js +1 -1
  76. package/lighthouse-core/audits/accessibility/th-has-data-cells.js +1 -1
  77. package/lighthouse-core/audits/accessibility/valid-lang.js +1 -1
  78. package/lighthouse-core/audits/accessibility/video-caption.js +1 -1
  79. package/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js +1 -2
  80. package/lighthouse-core/audits/byte-efficiency/duplicated-javascript.js +12 -11
  81. package/lighthouse-core/audits/byte-efficiency/efficient-animated-content.js +1 -1
  82. package/lighthouse-core/audits/byte-efficiency/legacy-javascript.js +24 -22
  83. package/lighthouse-core/audits/byte-efficiency/offscreen-images.js +2 -3
  84. package/lighthouse-core/audits/byte-efficiency/render-blocking-resources.js +3 -2
  85. package/lighthouse-core/audits/byte-efficiency/unminified-css.js +5 -3
  86. package/lighthouse-core/audits/byte-efficiency/unminified-javascript.js +11 -10
  87. package/lighthouse-core/audits/byte-efficiency/unused-css-rules.js +1 -0
  88. package/lighthouse-core/audits/byte-efficiency/unused-javascript.js +8 -11
  89. package/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js +1 -1
  90. package/lighthouse-core/audits/byte-efficiency/uses-text-compression.js +1 -1
  91. package/lighthouse-core/audits/deprecations.js +598 -8
  92. package/lighthouse-core/audits/diagnostics.js +3 -4
  93. package/lighthouse-core/audits/dobetterweb/doctype.js +9 -20
  94. package/lighthouse-core/audits/dobetterweb/geolocation-on-start.js +1 -1
  95. package/lighthouse-core/audits/dobetterweb/no-document-write.js +1 -1
  96. package/lighthouse-core/audits/dobetterweb/notification-on-start.js +1 -1
  97. package/lighthouse-core/audits/dobetterweb/uses-http2.js +1 -2
  98. package/lighthouse-core/audits/dobetterweb/uses-passive-event-listeners.js +1 -1
  99. package/lighthouse-core/audits/errors-in-console.js +2 -2
  100. package/lighthouse-core/audits/long-tasks.js +2 -3
  101. package/lighthouse-core/audits/metrics/first-contentful-paint-3g.js +2 -2
  102. package/lighthouse-core/audits/metrics/first-contentful-paint.js +2 -3
  103. package/lighthouse-core/audits/metrics/first-meaningful-paint.js +2 -3
  104. package/lighthouse-core/audits/metrics/interactive.js +2 -3
  105. package/lighthouse-core/audits/metrics/largest-contentful-paint.js +2 -3
  106. package/lighthouse-core/audits/metrics/max-potential-fid.js +2 -3
  107. package/lighthouse-core/audits/metrics/speed-index.js +2 -3
  108. package/lighthouse-core/audits/metrics/total-blocking-time.js +2 -3
  109. package/lighthouse-core/audits/metrics.js +2 -3
  110. package/lighthouse-core/audits/no-unload-listeners.js +15 -7
  111. package/lighthouse-core/audits/predictive-perf.js +1 -2
  112. package/lighthouse-core/audits/preload-lcp-image.js +3 -3
  113. package/lighthouse-core/audits/redirects.js +1 -1
  114. package/lighthouse-core/audits/script-treemap-data.js +45 -45
  115. package/lighthouse-core/audits/seo/http-status-code.js +4 -2
  116. package/lighthouse-core/audits/seo/link-text.js +0 -2
  117. package/lighthouse-core/audits/server-response-time.js +17 -2
  118. package/lighthouse-core/audits/service-worker.js +2 -7
  119. package/lighthouse-core/audits/timing-budget.js +2 -3
  120. package/lighthouse-core/audits/uses-rel-preconnect.js +1 -1
  121. package/lighthouse-core/audits/uses-rel-preload.js +1 -1
  122. package/lighthouse-core/audits/valid-source-maps.js +14 -12
  123. package/lighthouse-core/audits/violation-audit.js +1 -1
  124. package/lighthouse-core/audits/work-during-interaction.js +2 -2
  125. package/lighthouse-core/computed/critical-request-chains.js +9 -5
  126. package/lighthouse-core/computed/js-bundles.js +11 -11
  127. package/lighthouse-core/computed/main-resource.js +2 -3
  128. package/lighthouse-core/computed/metrics/first-contentful-paint-all-frames.js +1 -1
  129. package/lighthouse-core/computed/metrics/first-contentful-paint.js +1 -1
  130. package/lighthouse-core/computed/metrics/first-meaningful-paint.js +1 -1
  131. package/lighthouse-core/computed/metrics/interactive.js +1 -1
  132. package/lighthouse-core/computed/metrics/lantern-first-contentful-paint.js +1 -1
  133. package/lighthouse-core/computed/metrics/lantern-first-meaningful-paint.js +1 -1
  134. package/lighthouse-core/computed/metrics/lantern-interactive.js +1 -1
  135. package/lighthouse-core/computed/metrics/lantern-largest-contentful-paint.js +1 -1
  136. package/lighthouse-core/computed/metrics/lantern-max-potential-fid.js +1 -1
  137. package/lighthouse-core/computed/metrics/lantern-metric.js +5 -3
  138. package/lighthouse-core/computed/metrics/lantern-speed-index.js +1 -1
  139. package/lighthouse-core/computed/metrics/lantern-total-blocking-time.js +1 -1
  140. package/lighthouse-core/computed/metrics/largest-contentful-paint-all-frames.js +1 -1
  141. package/lighthouse-core/computed/metrics/largest-contentful-paint.js +1 -1
  142. package/lighthouse-core/computed/metrics/max-potential-fid.js +1 -1
  143. package/lighthouse-core/computed/metrics/metric.js +0 -1
  144. package/lighthouse-core/computed/metrics/speed-index.js +1 -1
  145. package/lighthouse-core/computed/metrics/timing-summary.js +6 -13
  146. package/lighthouse-core/computed/metrics/total-blocking-time.js +1 -1
  147. package/lighthouse-core/computed/module-duplication.js +5 -6
  148. package/lighthouse-core/computed/page-dependency-graph.js +28 -55
  149. package/lighthouse-core/computed/resource-summary.js +5 -5
  150. package/lighthouse-core/computed/unused-css.js +7 -6
  151. package/lighthouse-core/computed/unused-javascript-summary.js +36 -16
  152. package/lighthouse-core/config/budget.js +2 -2
  153. package/lighthouse-core/config/default-config.js +1 -2
  154. package/lighthouse-core/fraggle-rock/api.js +1 -1
  155. package/lighthouse-core/fraggle-rock/config/default-config.js +0 -3
  156. package/lighthouse-core/fraggle-rock/gather/base-artifacts.js +3 -6
  157. package/lighthouse-core/fraggle-rock/gather/driver.js +2 -2
  158. package/lighthouse-core/fraggle-rock/gather/navigation-runner.js +26 -51
  159. package/lighthouse-core/fraggle-rock/gather/runner-helpers.js +3 -0
  160. package/lighthouse-core/fraggle-rock/gather/session.js +22 -23
  161. package/lighthouse-core/fraggle-rock/gather/snapshot-runner.js +4 -5
  162. package/lighthouse-core/fraggle-rock/gather/timespan-runner.js +6 -6
  163. package/lighthouse-core/gather/connections/cri.js +1 -5
  164. package/lighthouse-core/gather/driver/execution-context.js +2 -5
  165. package/lighthouse-core/gather/driver/navigation.js +10 -13
  166. package/lighthouse-core/gather/driver/network-monitor.js +3 -16
  167. package/lighthouse-core/gather/driver.js +1 -6
  168. package/lighthouse-core/gather/fetcher.js +210 -34
  169. package/lighthouse-core/gather/gather-runner.js +17 -13
  170. package/lighthouse-core/gather/gatherers/accessibility.js +4 -16
  171. package/lighthouse-core/gather/gatherers/console-messages.js +2 -4
  172. package/lighthouse-core/gather/gatherers/dobetterweb/doctype.js +2 -4
  173. package/lighthouse-core/gather/gatherers/full-page-screenshot.js +2 -3
  174. package/lighthouse-core/gather/gatherers/iframe-elements.js +1 -4
  175. package/lighthouse-core/gather/gatherers/image-elements.js +0 -2
  176. package/lighthouse-core/gather/gatherers/js-usage.js +87 -12
  177. package/lighthouse-core/gather/gatherers/link-elements.js +14 -12
  178. package/lighthouse-core/gather/gatherers/main-document-content.js +8 -8
  179. package/lighthouse-core/gather/gatherers/script-elements.js +56 -7
  180. package/lighthouse-core/gather/gatherers/seo/robots-txt.js +30 -2
  181. package/lighthouse-core/gather/gatherers/source-maps.js +1 -8
  182. package/lighthouse-core/gather/gatherers/web-app-manifest.js +2 -2
  183. package/lighthouse-core/index.js +2 -26
  184. package/lighthouse-core/lib/cdt/generated/SourceMap.js +1 -1
  185. package/lighthouse-core/lib/dependency-graph/simulator/network-analyzer.js +29 -8
  186. package/lighthouse-core/lib/navigation-error.js +4 -13
  187. package/lighthouse-core/lib/page-functions.js +1 -3
  188. package/lighthouse-core/lib/script-helpers.js +6 -11
  189. package/lighthouse-core/lib/url-shim.js +2 -2
  190. package/lighthouse-core/runner.js +5 -0
  191. package/lighthouse-core/scripts/manual-chrome-launcher.js +1 -0
  192. package/lighthouse-core/util-commonjs.js +6 -8
  193. package/package.json +13 -13
  194. package/readme.md +1 -1
  195. package/report/.eslintrc.cjs +1 -3
  196. package/report/assets/styles.css +5 -10
  197. package/report/assets/templates.html +9 -10
  198. package/report/clients/bundle.js +1 -0
  199. package/report/clients/standalone.js +1 -0
  200. package/report/generator/report-generator.js +21 -46
  201. package/report/renderer/api.js +2 -0
  202. package/report/renderer/category-renderer.js +24 -20
  203. package/report/renderer/components.js +304 -303
  204. package/report/renderer/crc-details-renderer.js +24 -21
  205. package/report/renderer/details-renderer.js +12 -12
  206. package/report/renderer/dom.js +7 -6
  207. package/report/renderer/drop-down-menu.js +1 -0
  208. package/report/renderer/element-screenshot-renderer.js +5 -1
  209. package/report/renderer/features-util.js +1 -0
  210. package/report/renderer/i18n.js +62 -118
  211. package/report/renderer/logger.js +1 -0
  212. package/report/renderer/open-tab.js +3 -1
  213. package/report/renderer/performance-category-renderer.js +11 -10
  214. package/report/renderer/pwa-category-renderer.js +4 -3
  215. package/report/renderer/report-renderer.js +17 -17
  216. package/report/renderer/report-ui-features.js +1 -0
  217. package/report/renderer/snippet-renderer.js +3 -2
  218. package/report/renderer/swap-locale-feature.js +1 -0
  219. package/report/renderer/text-encoding.js +1 -0
  220. package/report/renderer/topbar-features.js +1 -0
  221. package/report/renderer/util.js +6 -7
  222. package/report/test/clients/bundle-test.js +5 -0
  223. package/report/test/generator/file-namer-test.js +1 -0
  224. package/report/test/generator/report-generator-test.js +20 -25
  225. package/report/test/renderer/category-renderer-test.js +4 -3
  226. package/report/test/renderer/components-test.js +3 -0
  227. package/report/test/renderer/crc-details-renderer-test.js +3 -0
  228. package/report/test/renderer/details-renderer-test.js +3 -49
  229. package/report/test/renderer/dom-test.js +3 -0
  230. package/report/test/renderer/element-screenshot-renderer-test.js +6 -3
  231. package/report/test/renderer/i18n-test.js +25 -70
  232. package/report/test/renderer/performance-category-renderer-test.js +5 -14
  233. package/report/test/renderer/pwa-category-renderer-test.js +4 -3
  234. package/report/test/renderer/report-renderer-axe-test.js +4 -3
  235. package/report/test/renderer/report-renderer-test.js +4 -3
  236. package/report/test/renderer/report-ui-features-test.js +4 -3
  237. package/report/test/renderer/snippet-renderer-test.js +3 -0
  238. package/report/test/renderer/text-encoding-test.js +3 -0
  239. package/report/test/renderer/util-test.js +5 -4
  240. package/report/test-assets/faux-psi.js +1 -2
  241. package/shared/localization/format.js +3 -7
  242. package/shared/localization/locales/en-US.json +238 -55
  243. package/shared/localization/locales/en-XL.json +238 -55
  244. package/shared/test/localization/format-test.js +2 -8
  245. package/shared/test/localization/locales-test.js +2 -0
  246. package/shared/test/localization/swap-locale-test.js +2 -0
  247. package/third-party/chromium-synchronization/inspector-issueAdded-types-test.js +2 -0
  248. package/third-party/chromium-synchronization/installability-errors-test.js +2 -0
  249. package/tsconfig.json +0 -1
  250. package/types/artifacts.d.ts +8 -47
  251. package/types/config.d.ts +0 -2
  252. package/types/externs.d.ts +2 -3
  253. package/types/gatherer.d.ts +2 -1
  254. package/types/global-lh.d.ts +0 -3
  255. package/types/lhr/lhr.d.ts +2 -2
  256. package/types/lhr/treemap.d.ts +1 -0
  257. package/changelog.md +0 -5902
  258. package/lighthouse-core/audits/script-elements-test-audit.js +0 -30
  259. package/lighthouse-core/gather/gatherers/scripts.js +0 -154
  260. package/lighthouse-core/scripts/package.json +0 -4
  261. package/report/test/.eslintrc.cjs +0 -11
  262. package/shared/test/localization/.eslintrc.cjs +0 -11
@@ -71,7 +71,7 @@ class GatherRunner {
71
71
  log.time(status);
72
72
  try {
73
73
  const requestedUrl = passContext.url;
74
- const {mainDocumentUrl, warnings} = await navigation.gotoURL(driver, requestedUrl, {
74
+ const {finalUrl, warnings} = await navigation.gotoURL(driver, requestedUrl, {
75
75
  waitUntil: passContext.passConfig.recordTrace ?
76
76
  ['load', 'fcp'] : ['load'],
77
77
  debugNavigation: passContext.settings.debugNavigation,
@@ -79,12 +79,7 @@ class GatherRunner {
79
79
  maxWaitForLoad: passContext.settings.maxWaitForLoad,
80
80
  ...passContext.passConfig,
81
81
  });
82
- passContext.url = mainDocumentUrl;
83
- const {URL} = passContext.baseArtifacts;
84
- if (!URL.finalUrl || !URL.mainDocumentUrl) {
85
- URL.finalUrl = mainDocumentUrl;
86
- URL.mainDocumentUrl = mainDocumentUrl;
87
- }
82
+ passContext.url = finalUrl;
88
83
  if (passContext.passConfig.loadFailureMode === 'fatal') {
89
84
  passContext.LighthouseRunWarnings.push(...warnings);
90
85
  }
@@ -183,6 +178,11 @@ class GatherRunner {
183
178
  const resetStorage = !options.settings.disableStorageReset;
184
179
  if (resetStorage) await storage.clearDataForOrigin(session, options.requestedUrl);
185
180
 
181
+ // Disable fetcher, in case a gatherer enabled it.
182
+ // This cleanup should be removed once the only usage of
183
+ // fetcher (fetching arbitrary URLs) is replaced by new protocol support.
184
+ await driver.fetcher.disable();
185
+
186
186
  await driver.disconnect();
187
187
  } catch (err) {
188
188
  // Ignore disconnecting error if browser was already closed.
@@ -407,12 +407,7 @@ class GatherRunner {
407
407
  devtoolsLogs: {},
408
408
  settings: options.settings,
409
409
  GatherContext: {gatherMode: 'navigation'},
410
- URL: {
411
- initialUrl: await options.driver.url(),
412
- requestedUrl: options.requestedUrl,
413
- mainDocumentUrl: '',
414
- finalUrl: '',
415
- },
410
+ URL: {requestedUrl: options.requestedUrl, finalUrl: options.requestedUrl},
416
411
  Timing: [],
417
412
  PageLoadError: null,
418
413
  };
@@ -430,6 +425,9 @@ class GatherRunner {
430
425
 
431
426
  const baseArtifacts = passContext.baseArtifacts;
432
427
 
428
+ // Copy redirected URL to artifact.
429
+ baseArtifacts.URL.finalUrl = passContext.url;
430
+
433
431
  // Fetch the manifest, if it exists.
434
432
  try {
435
433
  baseArtifacts.WebAppManifest = await WebAppManifest.getWebAppManifest(
@@ -519,6 +517,12 @@ class GatherRunner {
519
517
  await GatherRunner.populateBaseArtifacts(passContext);
520
518
  isFirstPass = false;
521
519
  }
520
+
521
+ // Disable fetcher for every pass, in case a gatherer enabled it.
522
+ // Noop if fetcher was never enabled.
523
+ // This cleanup should be removed once the only usage of
524
+ // fetcher (fetching arbitrary URLs) is replaced by new protocol support.
525
+ await driver.fetcher.disable();
522
526
  }
523
527
 
524
528
  await GatherRunner.disposeDriver(driver, options);
@@ -12,6 +12,9 @@ const axeLibSource = require('../../lib/axe.js').source;
12
12
  const pageFunctions = require('../../lib/page-functions.js');
13
13
 
14
14
  /**
15
+ * This is run in the page, not Lighthouse itself.
16
+ * axe.run returns a promise which fulfills with a results object
17
+ * containing any violations.
15
18
  * @return {Promise<LH.Artifacts.Accessibility>}
16
19
  */
17
20
  /* c8 ignore start */
@@ -35,11 +38,8 @@ async function runA11yChecks() {
35
38
  'wcag2aa',
36
39
  ],
37
40
  },
38
- // resultTypes doesn't limit the output of the axeResults object. Instead, if it's defined,
39
- // some expensive element identification is done only for the respective types. https://github.com/dequelabs/axe-core/blob/f62f0cf18f7b69b247b0b6362cf1ae71ffbf3a1b/lib/core/reporters/helpers/process-aggregate.js#L61-L97
40
41
  resultTypes: ['violations', 'inapplicable'],
41
42
  rules: {
42
- // Consider http://go/prcpg for expert review of the aXe rules.
43
43
  'tabindex': {enabled: true},
44
44
  'accesskeys': {enabled: true},
45
45
  'heading-order': {enabled: true},
@@ -60,12 +60,6 @@ async function runA11yChecks() {
60
60
  // https://github.com/dequelabs/axe-core/issues/2958
61
61
  'nested-interactive': {enabled: false},
62
62
  'frame-focusable-content': {enabled: false},
63
- 'aria-roledescription': {enabled: false},
64
- 'scrollable-region-focusable': {enabled: false},
65
- // TODO(paulirish): create audits and enable these 3.
66
- 'input-button-name': {enabled: false},
67
- 'role-img-alt': {enabled: false},
68
- 'select-name': {enabled: false},
69
63
  },
70
64
  });
71
65
 
@@ -76,8 +70,7 @@ async function runA11yChecks() {
76
70
  return {
77
71
  violations: axeResults.violations.map(createAxeRuleResultArtifact),
78
72
  incomplete: axeResults.incomplete.map(createAxeRuleResultArtifact),
79
- notApplicable: axeResults.inapplicable.map(result => ({id: result.id})), // FYI: inapplicable => notApplicable!
80
- passes: axeResults.passes.map(result => ({id: result.id})),
73
+ notApplicable: axeResults.inapplicable.map(result => ({id: result.id})),
81
74
  version: axeResults.testEngine.version,
82
75
  };
83
76
  }
@@ -157,11 +150,6 @@ class Accessibility extends FRGatherer {
157
150
  supportedModes: ['snapshot', 'navigation'],
158
151
  };
159
152
 
160
- static pageFns = {
161
- runA11yChecks,
162
- createAxeRuleResultArtifact,
163
- };
164
-
165
153
  /**
166
154
  * @param {LH.Gatherer.FRTransitionalContext} passContext
167
155
  * @return {Promise<LH.Artifacts.Accessibility>}
@@ -101,7 +101,6 @@ class ConsoleMessages extends FRGatherer {
101
101
  stackTrace: event.exceptionDetails.stackTrace,
102
102
  timestamp: event.timestamp,
103
103
  url: event.exceptionDetails.url,
104
- scriptId: event.exceptionDetails.scriptId,
105
104
  lineNumber: event.exceptionDetails.lineNumber,
106
105
  columnNumber: event.exceptionDetails.columnNumber,
107
106
  };
@@ -118,7 +117,7 @@ class ConsoleMessages extends FRGatherer {
118
117
 
119
118
  // JS events have a stack trace, which we use to get the column.
120
119
  // CSS/HTML events only expose a line number.
121
- const firstStackFrame = event.entry.stackTrace?.callFrames[0];
120
+ const {columnNumber} = event.entry.stackTrace?.callFrames[0] || {};
122
121
 
123
122
  this._logEntries.push({
124
123
  eventType: 'protocolLog',
@@ -128,9 +127,8 @@ class ConsoleMessages extends FRGatherer {
128
127
  stackTrace,
129
128
  timestamp,
130
129
  url,
131
- scriptId: firstStackFrame?.scriptId,
132
130
  lineNumber,
133
- columnNumber: firstStackFrame?.columnNumber,
131
+ columnNumber,
134
132
  });
135
133
  }
136
134
 
@@ -12,8 +12,7 @@ const FRGatherer = require('../../../fraggle-rock/gather/base-gatherer.js');
12
12
  /**
13
13
  * Get and return `name`, `publicId`, `systemId` from
14
14
  * `document.doctype`
15
- * and `compatMode` from `document` to check `quirks-mode`
16
- * @return {{name: string, publicId: string, systemId: string, documentCompatMode: string} | null}
15
+ * @return {{name: string, publicId: string, systemId: string} | null}
17
16
  */
18
17
  function getDoctype() {
19
18
  // An example of this is warnerbros.com/archive/spacejam/movie/jam.htm
@@ -21,9 +20,8 @@ function getDoctype() {
21
20
  return null;
22
21
  }
23
22
 
24
- const documentCompatMode = document.compatMode;
25
23
  const {name, publicId, systemId} = document.doctype;
26
- return {name, publicId, systemId, documentCompatMode};
24
+ return {name, publicId, systemId};
27
25
  }
28
26
 
29
27
  class Doctype extends FRGatherer {
@@ -15,7 +15,6 @@ const {waitForNetworkIdle} = require('../driver/wait-for-condition.js');
15
15
 
16
16
  // JPEG quality setting
17
17
  // Exploration and examples of reports using different quality settings: https://docs.google.com/document/d/1ZSffucIca9XDW2eEwfoevrk-OTl7WQFeMf0CgeJAA8M/edit#
18
- // Note: this analysis was done for JPEG, but now we use WEBP.
19
18
  const FULL_PAGE_SCREENSHOT_QUALITY = 30;
20
19
 
21
20
  /**
@@ -119,10 +118,10 @@ class FullPageScreenshot extends FRGatherer {
119
118
  await context.driver.executionContext.evaluate(waitForDoubleRaf, {args: []});
120
119
 
121
120
  const result = await session.sendCommand('Page.captureScreenshot', {
122
- format: 'webp',
121
+ format: 'jpeg',
123
122
  quality: FULL_PAGE_SCREENSHOT_QUALITY,
124
123
  });
125
- const data = 'data:image/webp;base64,' + result.data;
124
+ const data = 'data:image/jpeg;base64,' + result.data;
126
125
 
127
126
  return {
128
127
  data,
@@ -17,13 +17,10 @@ const pageFunctions = require('../../lib/page-functions.js');
17
17
  */
18
18
  /* c8 ignore start */
19
19
  function collectIFrameElements() {
20
- const realBoundingClientRect = window.__HTMLElementBoundingClientRect ||
21
- window.HTMLElement.prototype.getBoundingClientRect;
22
-
23
20
  // @ts-expect-error - put into scope via stringification
24
21
  const iFrameElements = getElementsInDocument('iframe'); // eslint-disable-line no-undef
25
22
  return iFrameElements.map(/** @param {HTMLIFrameElement} node */ (node) => {
26
- const clientRect = realBoundingClientRect.call(node);
23
+ const clientRect = node.getBoundingClientRect();
27
24
  const {top, bottom, left, right, width, height} = clientRect;
28
25
  return {
29
26
  id: node.id,
@@ -258,7 +258,6 @@ class ImageElements extends FRGatherer {
258
258
  driver.defaultSession.setNextProtocolTimeout(250);
259
259
  size = await driver.executionContext.evaluate(determineNaturalSize, {
260
260
  args: [url],
261
- useIsolation: true,
262
261
  });
263
262
  this._naturalSizeCache.set(url, size);
264
263
  } catch (_) {
@@ -341,7 +340,6 @@ class ImageElements extends FRGatherer {
341
340
 
342
341
  const elements = await executionContext.evaluate(collectImageElementInfo, {
343
342
  args: [],
344
- useIsolation: true,
345
343
  deps: [
346
344
  pageFunctions.getElementsInDocumentString,
347
345
  pageFunctions.getBoundingClientRectString,
@@ -18,8 +18,20 @@ class JsUsage extends FRGatherer {
18
18
 
19
19
  constructor() {
20
20
  super();
21
+ /** @type {LH.Crdp.Debugger.ScriptParsedEvent[]} */
22
+ this._scriptParsedEvents = [];
21
23
  /** @type {LH.Crdp.Profiler.ScriptCoverage[]} */
22
24
  this._scriptUsages = [];
25
+ this.onScriptParsed = this.onScriptParsed.bind(this);
26
+ }
27
+
28
+ /**
29
+ * @param {LH.Crdp.Debugger.ScriptParsedEvent} event
30
+ */
31
+ onScriptParsed(event) {
32
+ if (event.embedderName) {
33
+ this._scriptParsedEvents.push(event);
34
+ }
23
35
  }
24
36
 
25
37
  /**
@@ -43,27 +55,90 @@ class JsUsage extends FRGatherer {
43
55
  }
44
56
 
45
57
  /**
58
+ * @param {LH.Gatherer.FRTransitionalContext} context
59
+ */
60
+ async startSensitiveInstrumentation(context) {
61
+ const session = context.driver.defaultSession;
62
+ session.on('Debugger.scriptParsed', this.onScriptParsed);
63
+ await session.sendCommand('Debugger.enable');
64
+ }
65
+
66
+ /**
67
+ * @param {LH.Gatherer.FRTransitionalContext} context
68
+ */
69
+ async stopSensitiveInstrumentation(context) {
70
+ const session = context.driver.defaultSession;
71
+ await session.sendCommand('Debugger.disable');
72
+ session.off('Debugger.scriptParsed', this.onScriptParsed);
73
+ }
74
+
75
+ /**
76
+ * Usages alone do not always generate an exhaustive list of scripts in timespan and snapshot.
77
+ * For audits which use this for url/scriptId mappings, we can include an empty usage object.
78
+ *
79
+ * @param {Record<string, Array<LH.Crdp.Profiler.ScriptCoverage>>} usageByUrl
80
+ */
81
+ _addMissingScriptIds(usageByUrl) {
82
+ for (const scriptParsedEvent of this._scriptParsedEvents) {
83
+ const url = scriptParsedEvent.embedderName;
84
+ if (!url) continue;
85
+
86
+ const scripts = usageByUrl[url] || [];
87
+ if (!scripts.find(s => s.scriptId === scriptParsedEvent.scriptId)) {
88
+ scripts.push({
89
+ url,
90
+ scriptId: scriptParsedEvent.scriptId,
91
+ functions: [],
92
+ });
93
+ }
94
+ usageByUrl[url] = scripts;
95
+ }
96
+ }
97
+
98
+ /**
99
+ * @param {LH.Gatherer.FRTransitionalContext} context
46
100
  * @return {Promise<LH.Artifacts['JsUsage']>}
47
101
  */
48
- async getArtifact() {
49
- /** @type {Record<string, LH.Crdp.Profiler.ScriptCoverage>} */
50
- const usageByScriptId = {};
102
+ async getArtifact(context) {
103
+ /** @type {Record<string, Array<LH.Crdp.Profiler.ScriptCoverage>>} */
104
+ const usageByUrl = {};
105
+
106
+ // Force `Debugger.scriptParsed` events for url to scriptId mappings in snapshot mode.
107
+ if (context.gatherMode === 'snapshot') {
108
+ await this.startSensitiveInstrumentation(context);
109
+ await this.stopSensitiveInstrumentation(context);
110
+ }
51
111
 
52
112
  for (const scriptUsage of this._scriptUsages) {
53
- // If `url` is blank, that means the script was dynamically
54
- // created (eval, new Function, onload, ...)
55
- if (scriptUsage.url === '' || scriptUsage.url === '_lighthouse-eval.js') {
56
- // We currently don't consider coverage of dynamic scripts, and we definitely don't want
57
- // coverage of code Lighthouse ran to inspect the page, so we ignore this ScriptCoverage.
58
- // Audits would work the same without this, it is only an optimization (not tracking coverage
59
- // for scripts we don't care about).
113
+ // `ScriptCoverage.url` can be overridden by a magic sourceURL comment.
114
+ // Get the associated ScriptParsedEvent and use embedderName, which is the original url.
115
+ // See https://chromium-review.googlesource.com/c/v8/v8/+/2317310
116
+ let url = scriptUsage.url;
117
+ const scriptParsedEvent =
118
+ this._scriptParsedEvents.find(e => e.scriptId === scriptUsage.scriptId);
119
+ if (scriptParsedEvent?.embedderName) {
120
+ url = scriptParsedEvent.embedderName;
121
+ }
122
+
123
+ // If `url` is blank, that means the script was anonymous (eval, new Function, onload, ...).
124
+ // Or, it's because it was code Lighthouse over the protocol via `Runtime.evaluate`.
125
+ // We currently don't consider coverage of anonymous scripts, and we definitely don't want
126
+ // coverage of code Lighthouse ran to inspect the page, so we ignore this ScriptCoverage if
127
+ // url is blank.
128
+ if (scriptUsage.url === '' || (scriptParsedEvent && scriptParsedEvent.embedderName === '')) {
60
129
  continue;
61
130
  }
62
131
 
63
- usageByScriptId[scriptUsage.scriptId] = scriptUsage;
132
+ const scripts = usageByUrl[url] || [];
133
+ scripts.push(scriptUsage);
134
+ usageByUrl[url] = scripts;
135
+ }
136
+
137
+ if (context.gatherMode !== 'navigation') {
138
+ this._addMissingScriptIds(usageByUrl);
64
139
  }
65
140
 
66
- return usageByScriptId;
141
+ return usageByUrl;
67
142
  }
68
143
  }
69
144
 
@@ -8,9 +8,10 @@
8
8
  const LinkHeader = require('http-link-header');
9
9
  const FRGatherer = require('../../fraggle-rock/gather/base-gatherer.js');
10
10
  const {URL} = require('../../lib/url-shim.js');
11
+ const NetworkRecords = require('../../computed/network-records.js');
12
+ const NetworkAnalyzer = require('../../lib/dependency-graph/simulator/network-analyzer.js');
11
13
  const pageFunctions = require('../../lib/page-functions.js');
12
14
  const DevtoolsLog = require('./devtools-log.js');
13
- const MainResource = require('../../computed/main-resource.js');
14
15
 
15
16
  /* globals HTMLLinkElement getNodeDetails */
16
17
 
@@ -113,12 +114,12 @@ class LinkElements extends FRGatherer {
113
114
 
114
115
  /**
115
116
  * @param {LH.Gatherer.FRTransitionalContext} context
116
- * @param {LH.Artifacts['DevtoolsLog']} devtoolsLog
117
- * @return {Promise<LH.Artifacts['LinkElements']>}
117
+ * @param {LH.Artifacts.NetworkRequest[]} networkRecords
118
+ * @return {LH.Artifacts['LinkElements']}
118
119
  */
119
- static async getLinkElementsInHeaders(context, devtoolsLog) {
120
- const mainDocument =
121
- await MainResource.request({devtoolsLog, URL: context.baseArtifacts.URL}, context);
120
+ static getLinkElementsInHeaders(context, networkRecords) {
121
+ const finalUrl = context.url;
122
+ const mainDocument = NetworkAnalyzer.findMainDocument(networkRecords, finalUrl);
122
123
 
123
124
  /** @type {LH.Artifacts['LinkElements']} */
124
125
  const linkElements = [];
@@ -129,7 +130,7 @@ class LinkElements extends FRGatherer {
129
130
  for (const link of LinkHeader.parse(header.value).refs) {
130
131
  linkElements.push({
131
132
  rel: link.rel || '',
132
- href: normalizeUrlOrNull(link.uri, context.baseArtifacts.URL.finalUrl),
133
+ href: normalizeUrlOrNull(link.uri, finalUrl),
133
134
  hrefRaw: link.uri || '',
134
135
  hreflang: link.hreflang || '',
135
136
  as: link.as || '',
@@ -145,12 +146,12 @@ class LinkElements extends FRGatherer {
145
146
 
146
147
  /**
147
148
  * @param {LH.Gatherer.FRTransitionalContext} context
148
- * @param {LH.Artifacts['DevtoolsLog']} devtoolsLog
149
+ * @param {LH.Artifacts.NetworkRequest[]} networkRecords
149
150
  * @return {Promise<LH.Artifacts['LinkElements']>}
150
151
  */
151
- async _getArtifact(context, devtoolsLog) {
152
+ async _getArtifact(context, networkRecords) {
152
153
  const fromDOM = await LinkElements.getLinkElementsInDOM(context);
153
- const fromHeaders = await LinkElements.getLinkElementsInHeaders(context, devtoolsLog);
154
+ const fromHeaders = LinkElements.getLinkElementsInHeaders(context, networkRecords);
154
155
  const linkElements = fromDOM.concat(fromHeaders);
155
156
 
156
157
  for (const link of linkElements) {
@@ -167,7 +168,7 @@ class LinkElements extends FRGatherer {
167
168
  * @return {Promise<LH.Artifacts['LinkElements']>}
168
169
  */
169
170
  async afterPass(context, loadData) {
170
- return this._getArtifact({...context, dependencies: {}}, loadData.devtoolsLog);
171
+ return this._getArtifact({...context, dependencies: {}}, loadData.networkRecords);
171
172
  }
172
173
 
173
174
  /**
@@ -175,7 +176,8 @@ class LinkElements extends FRGatherer {
175
176
  * @return {Promise<LH.Artifacts['LinkElements']>}
176
177
  */
177
178
  async getArtifact(context) {
178
- return this._getArtifact(context, context.dependencies.DevtoolsLog);
179
+ const records = await NetworkRecords.request(context.dependencies.DevtoolsLog, context);
180
+ return this._getArtifact(context, records);
179
181
  }
180
182
  }
181
183
 
@@ -6,9 +6,10 @@
6
6
  'use strict';
7
7
 
8
8
  const FRGatherer = require('../../fraggle-rock/gather/base-gatherer.js');
9
+ const NetworkAnalyzer = require('../../lib/dependency-graph/simulator/network-analyzer.js');
10
+ const NetworkRecords = require('../../computed/network-records.js');
9
11
  const DevtoolsLog = require('./devtools-log.js');
10
12
  const {fetchResponseBodyFromCache} = require('../driver/network.js');
11
- const MainResource = require('../../computed/main-resource.js');
12
13
 
13
14
  /**
14
15
  * Collects the content of the main html document.
@@ -23,16 +24,14 @@ class MainDocumentContent extends FRGatherer {
23
24
  /**
24
25
  *
25
26
  * @param {LH.Gatherer.FRTransitionalContext} context
26
- * @param {LH.Artifacts['DevtoolsLog']} devtoolsLog
27
+ * @param {LH.Artifacts.NetworkRequest[]} networkRecords
27
28
  * @return {Promise<LH.Artifacts['MainDocumentContent']>}
28
29
  */
29
- async _getArtifact(context, devtoolsLog) {
30
- const mainResource =
31
- await MainResource.request({devtoolsLog, URL: context.baseArtifacts.URL}, context);
30
+ async _getArtifact(context, networkRecords) {
31
+ const mainResource = NetworkAnalyzer.findMainDocument(networkRecords, context.url);
32
32
  const session = context.driver.defaultSession;
33
33
  return fetchResponseBodyFromCache(session, mainResource.requestId);
34
34
  }
35
-
36
35
  /**
37
36
  *
38
37
  * @param {LH.Gatherer.FRTransitionalContext<'DevtoolsLog'>} context
@@ -40,7 +39,8 @@ class MainDocumentContent extends FRGatherer {
40
39
  */
41
40
  async getArtifact(context) {
42
41
  const devtoolsLog = context.dependencies.DevtoolsLog;
43
- return this._getArtifact(context, devtoolsLog);
42
+ const networkRecords = await NetworkRecords.request(devtoolsLog, context);
43
+ return this._getArtifact(context, networkRecords);
44
44
  }
45
45
 
46
46
  /**
@@ -49,7 +49,7 @@ class MainDocumentContent extends FRGatherer {
49
49
  * @return {Promise<LH.Artifacts['MainDocumentContent']>}
50
50
  */
51
51
  async afterPass(passContext, loadData) {
52
- return this._getArtifact({...passContext, dependencies: {}}, loadData.devtoolsLog);
52
+ return this._getArtifact({...passContext, dependencies: {}}, loadData.networkRecords);
53
53
  }
54
54
  }
55
55
 
@@ -7,8 +7,10 @@
7
7
 
8
8
  const FRGatherer = require('../../fraggle-rock/gather/base-gatherer.js');
9
9
  const NetworkRecords = require('../../computed/network-records.js');
10
+ const NetworkAnalyzer = require('../../lib/dependency-graph/simulator/network-analyzer.js');
10
11
  const NetworkRequest = require('../../lib/network-request.js');
11
12
  const pageFunctions = require('../../lib/page-functions.js');
13
+ const {fetchResponseBodyFromCache} = require('../driver/network.js');
12
14
  const DevtoolsLog = require('./devtools-log.js');
13
15
 
14
16
  /* global getNodeDetails */
@@ -30,6 +32,8 @@ function collectAllScriptElements() {
30
32
  async: script.async,
31
33
  defer: script.defer,
32
34
  source: script.closest('head') ? 'head' : 'body',
35
+ content: script.src ? null : script.text,
36
+ requestId: null,
33
37
  // @ts-expect-error - getNodeDetails put into scope via stringification
34
38
  node: getNodeDetails(script),
35
39
  };
@@ -37,6 +41,27 @@ function collectAllScriptElements() {
37
41
  }
38
42
  /* c8 ignore stop */
39
43
 
44
+ /**
45
+ * @template T, U
46
+ * @param {Array<T>} values
47
+ * @param {(value: T) => Promise<U>} promiseMapper
48
+ * @param {boolean} runInSeries
49
+ * @return {Promise<Array<U>>}
50
+ */
51
+ async function runInSeriesOrParallel(values, promiseMapper, runInSeries) {
52
+ if (runInSeries) {
53
+ const results = [];
54
+ for (const value of values) {
55
+ const result = await promiseMapper(value);
56
+ results.push(result);
57
+ }
58
+ return results;
59
+ } else {
60
+ const promises = values.map(promiseMapper);
61
+ return await Promise.all(promises);
62
+ }
63
+ }
64
+
40
65
  /**
41
66
  * @fileoverview Gets JavaScript file contents.
42
67
  */
@@ -50,10 +75,13 @@ class ScriptElements extends FRGatherer {
50
75
  /**
51
76
  * @param {LH.Gatherer.FRTransitionalContext} context
52
77
  * @param {LH.Artifacts.NetworkRequest[]} networkRecords
78
+ * @param {LH.Artifacts['HostFormFactor']} formFactor
53
79
  * @return {Promise<LH.Artifacts['ScriptElements']>}
54
80
  */
55
- async _getArtifact(context, networkRecords) {
81
+ async _getArtifact(context, networkRecords, formFactor) {
82
+ const session = context.driver.defaultSession;
56
83
  const executionContext = context.driver.executionContext;
84
+ const mainResource = NetworkAnalyzer.findOptionalMainDocument(networkRecords, context.url);
57
85
 
58
86
  const scripts = await executionContext.evaluate(collectAllScriptElements, {
59
87
  args: [],
@@ -64,16 +92,34 @@ class ScriptElements extends FRGatherer {
64
92
  ],
65
93
  });
66
94
 
95
+ for (const script of scripts) {
96
+ if (mainResource && script.content) script.requestId = mainResource.requestId;
97
+ }
98
+
67
99
  const scriptRecords = networkRecords
68
- .filter(record => record.resourceType === NetworkRequest.TYPES.Script)
69
100
  // Ignore records from OOPIFs
70
- .filter(record => !record.sessionId);
101
+ .filter(record => !record.sessionId)
102
+ // Only get the content of script requests
103
+ .filter(record => record.resourceType === NetworkRequest.TYPES.Script);
104
+
105
+ // If run on a mobile device, be sensitive to memory limitations and only request one
106
+ // record at a time.
107
+ const scriptRecordContents = await runInSeriesOrParallel(
108
+ scriptRecords,
109
+ record => fetchResponseBodyFromCache(session, record.requestId).catch(() => ''),
110
+ formFactor === 'mobile' /* runInSeries */
111
+ );
71
112
 
72
113
  for (let i = 0; i < scriptRecords.length; i++) {
73
114
  const record = scriptRecords[i];
115
+ const content = scriptRecordContents[i];
116
+ if (!content) continue;
74
117
 
75
118
  const matchedScriptElement = scripts.find(script => script.src === record.url);
76
- if (!matchedScriptElement) {
119
+ if (matchedScriptElement) {
120
+ matchedScriptElement.requestId = record.requestId;
121
+ matchedScriptElement.content = content;
122
+ } else {
77
123
  scripts.push({
78
124
  type: null,
79
125
  src: record.url,
@@ -81,11 +127,12 @@ class ScriptElements extends FRGatherer {
81
127
  async: false,
82
128
  defer: false,
83
129
  source: 'network',
130
+ requestId: record.requestId,
131
+ content,
84
132
  node: null,
85
133
  });
86
134
  }
87
135
  }
88
-
89
136
  return scripts;
90
137
  }
91
138
 
@@ -94,8 +141,9 @@ class ScriptElements extends FRGatherer {
94
141
  */
95
142
  async getArtifact(context) {
96
143
  const devtoolsLog = context.dependencies.DevtoolsLog;
144
+ const formFactor = context.baseArtifacts.HostFormFactor;
97
145
  const networkRecords = await NetworkRecords.request(devtoolsLog, context);
98
- return this._getArtifact(context, networkRecords);
146
+ return this._getArtifact(context, networkRecords, formFactor);
99
147
  }
100
148
 
101
149
  /**
@@ -104,7 +152,8 @@ class ScriptElements extends FRGatherer {
104
152
  */
105
153
  async afterPass(passContext, loadData) {
106
154
  const networkRecords = loadData.networkRecords;
107
- return this._getArtifact({...passContext, dependencies: {}}, networkRecords);
155
+ const formFactor = passContext.baseArtifacts.HostFormFactor;
156
+ return this._getArtifact({...passContext, dependencies: {}}, networkRecords, formFactor);
108
157
  }
109
158
  }
110
159
 
@@ -7,6 +7,25 @@
7
7
 
8
8
  const FRGatherer = require('../../../fraggle-rock/gather/base-gatherer.js');
9
9
 
10
+ /* global fetch, location */
11
+
12
+ /** @return {Promise<LH.Artifacts['RobotsTxt']>} */
13
+ /* c8 ignore start */
14
+ async function getRobotsTxtContent() {
15
+ try {
16
+ const response = await fetch(new URL('/robots.txt', location.href).href);
17
+ if (!response.ok) {
18
+ return {status: response.status, content: null};
19
+ }
20
+
21
+ const content = await response.text();
22
+ return {status: response.status, content};
23
+ } catch (err) {
24
+ return {status: null, content: null, errorMessage: err.message};
25
+ }
26
+ }
27
+ /* c8 ignore stop */
28
+
10
29
  class RobotsTxt extends FRGatherer {
11
30
  /** @type {LH.Gatherer.GathererMeta} */
12
31
  meta = {
@@ -18,8 +37,17 @@ class RobotsTxt extends FRGatherer {
18
37
  * @return {Promise<LH.Artifacts['RobotsTxt']>}
19
38
  */
20
39
  async getArtifact(passContext) {
21
- const {finalUrl} = passContext.baseArtifacts.URL;
22
- const robotsUrl = new URL('/robots.txt', finalUrl).href;
40
+ // Iframe fetcher still has issues with CSPs.
41
+ // Only use the fetcher if we are fetching over the protocol.
42
+ if (await passContext.driver.fetcher.shouldUseLegacyFetcher()) {
43
+ return passContext.driver.executionContext.evaluate(getRobotsTxtContent, {
44
+ args: [],
45
+ useIsolation: true,
46
+ });
47
+ }
48
+
49
+ const robotsUrl = new URL('/robots.txt', passContext.url).href;
50
+ await passContext.driver.fetcher.enable();
23
51
  return passContext.driver.fetcher.fetchResource(robotsUrl)
24
52
  .catch(err => ({status: null, content: null, errorMessage: err.message}));
25
53
  }