lighthouse 8.3.0-dev.20210907 → 8.3.0

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 (196) hide show
  1. package/changelog.md +1 -1
  2. package/dist/report/standalone.js +196 -0
  3. package/jest.config.js +0 -4
  4. package/lighthouse-cli/cli-flags.js +6 -14
  5. package/lighthouse-cli/run.js +1 -1
  6. package/lighthouse-cli/test/smokehouse/frontends/smokehouse-bin.js +3 -9
  7. package/lighthouse-cli/test/smokehouse/report-assert.js +16 -36
  8. package/lighthouse-core/audits/large-javascript-libraries.js +166 -0
  9. package/lighthouse-core/audits/seo/http-status-code.js +6 -2
  10. package/lighthouse-core/audits/viewport.js +1 -2
  11. package/lighthouse-core/config/config-helpers.js +75 -9
  12. package/lighthouse-core/config/config.js +50 -6
  13. package/lighthouse-core/config/constants.js +0 -1
  14. package/lighthouse-core/config/default-config.js +0 -3
  15. package/lighthouse-core/config/experimental-config.js +8 -0
  16. package/lighthouse-core/config/metrics-to-audits.js +0 -1
  17. package/lighthouse-core/fraggle-rock/config/config.js +4 -3
  18. package/lighthouse-core/fraggle-rock/config/default-config.js +1 -3
  19. package/lighthouse-core/fraggle-rock/config/validation.js +0 -201
  20. package/lighthouse-core/fraggle-rock/gather/base-artifacts.js +0 -4
  21. package/lighthouse-core/fraggle-rock/gather/navigation-runner.js +8 -16
  22. package/lighthouse-core/fraggle-rock/gather/runner-helpers.js +0 -4
  23. package/lighthouse-core/fraggle-rock/gather/session.js +5 -30
  24. package/lighthouse-core/gather/driver/navigation.js +6 -7
  25. package/lighthouse-core/gather/driver/wait-for-condition.js +0 -27
  26. package/lighthouse-core/gather/fetcher.js +7 -17
  27. package/lighthouse-core/gather/gather-runner.js +0 -1
  28. package/lighthouse-core/gather/gatherers/seo/robots-txt.js +6 -2
  29. package/lighthouse-core/gather/gatherers/seo/tap-targets.js +2 -2
  30. package/lighthouse-core/gather/gatherers/stacks.js +1 -1
  31. package/lighthouse-core/lib/asset-saver.js +2 -2
  32. package/lighthouse-core/lib/cdt/SDK.js +0 -1
  33. package/lighthouse-core/lib/csp-evaluator.js +4 -17
  34. package/{report/generator → lighthouse-core/lib}/file-namer.js +0 -0
  35. package/lighthouse-core/lib/i18n/README.md +1 -1
  36. package/lighthouse-core/lib/i18n/i18n.js +7 -7
  37. package/lighthouse-core/lib/i18n/locales/ar-XB.json +18 -0
  38. package/lighthouse-core/lib/i18n/locales/ar.json +18 -0
  39. package/lighthouse-core/lib/i18n/locales/bg.json +18 -0
  40. package/lighthouse-core/lib/i18n/locales/ca.json +18 -0
  41. package/lighthouse-core/lib/i18n/locales/cs.json +18 -0
  42. package/lighthouse-core/lib/i18n/locales/da.json +18 -0
  43. package/lighthouse-core/lib/i18n/locales/de.json +18 -0
  44. package/lighthouse-core/lib/i18n/locales/el.json +18 -0
  45. package/lighthouse-core/lib/i18n/locales/en-GB.json +18 -0
  46. package/lighthouse-core/lib/i18n/locales/en-US.json +18 -18
  47. package/lighthouse-core/lib/i18n/locales/en-XA.json +18 -0
  48. package/lighthouse-core/lib/i18n/locales/en-XL.json +18 -18
  49. package/lighthouse-core/lib/i18n/locales/es-419.json +18 -0
  50. package/lighthouse-core/lib/i18n/locales/es.json +18 -0
  51. package/lighthouse-core/lib/i18n/locales/fi.json +18 -0
  52. package/lighthouse-core/lib/i18n/locales/fil.json +18 -0
  53. package/lighthouse-core/lib/i18n/locales/fr.json +18 -0
  54. package/lighthouse-core/lib/i18n/locales/he.json +18 -0
  55. package/lighthouse-core/lib/i18n/locales/hi.json +18 -0
  56. package/lighthouse-core/lib/i18n/locales/hr.json +18 -0
  57. package/lighthouse-core/lib/i18n/locales/hu.json +18 -0
  58. package/lighthouse-core/lib/i18n/locales/id.json +18 -0
  59. package/lighthouse-core/lib/i18n/locales/it.json +18 -0
  60. package/lighthouse-core/lib/i18n/locales/ja.json +18 -0
  61. package/lighthouse-core/lib/i18n/locales/ko.json +18 -0
  62. package/lighthouse-core/lib/i18n/locales/lt.json +18 -0
  63. package/lighthouse-core/lib/i18n/locales/lv.json +18 -0
  64. package/lighthouse-core/lib/i18n/locales/nl.json +18 -0
  65. package/lighthouse-core/lib/i18n/locales/no.json +18 -0
  66. package/lighthouse-core/lib/i18n/locales/pl.json +18 -0
  67. package/lighthouse-core/lib/i18n/locales/pt-PT.json +18 -0
  68. package/lighthouse-core/lib/i18n/locales/pt.json +18 -0
  69. package/lighthouse-core/lib/i18n/locales/ro.json +18 -0
  70. package/lighthouse-core/lib/i18n/locales/ru.json +18 -0
  71. package/lighthouse-core/lib/i18n/locales/sk.json +18 -0
  72. package/lighthouse-core/lib/i18n/locales/sl.json +18 -0
  73. package/lighthouse-core/lib/i18n/locales/sr-Latn.json +18 -0
  74. package/lighthouse-core/lib/i18n/locales/sr.json +18 -0
  75. package/lighthouse-core/lib/i18n/locales/sv.json +18 -0
  76. package/lighthouse-core/lib/i18n/locales/ta.json +18 -0
  77. package/lighthouse-core/lib/i18n/locales/te.json +18 -0
  78. package/lighthouse-core/lib/i18n/locales/th.json +18 -0
  79. package/lighthouse-core/lib/i18n/locales/tr.json +18 -0
  80. package/lighthouse-core/lib/i18n/locales/uk.json +18 -0
  81. package/lighthouse-core/lib/i18n/locales/vi.json +18 -0
  82. package/lighthouse-core/lib/i18n/locales/zh-HK.json +18 -0
  83. package/lighthouse-core/lib/i18n/locales/zh-TW.json +18 -0
  84. package/lighthouse-core/lib/i18n/locales/zh.json +18 -0
  85. package/lighthouse-core/lib/large-javascript-libraries/bundlephobia-database.json +62 -0
  86. package/lighthouse-core/lib/large-javascript-libraries/library-suggestions.js +20 -0
  87. package/lighthouse-core/lib/network-request.js +1 -3
  88. package/lighthouse-core/lib/traces/pwmetrics-events.js +2 -2
  89. package/lighthouse-core/lib/url-shim.js +1 -7
  90. package/lighthouse-core/runner.js +1 -4
  91. package/lighthouse-core/util-commonjs.js +2 -2
  92. package/now.json +1 -1
  93. package/package.json +13 -20
  94. package/report/README.md +2 -2
  95. package/report/assets/standalone-template.html +1 -0
  96. package/report/assets/styles.css +27 -23
  97. package/report/assets/templates.html +40 -37
  98. package/report/clients/bundle.js +0 -1
  99. package/report/{package.json → clients/package.json} +0 -0
  100. package/report/clients/psi.js +3 -13
  101. package/report/clients/standalone.js +1 -1
  102. package/report/{types/buffer.d.ts → clients/treemap.js} +13 -11
  103. package/report/renderer/category-renderer.js +2 -2
  104. package/report/renderer/components.js +48 -46
  105. package/report/renderer/crc-details-renderer.js +16 -16
  106. package/report/renderer/details-renderer.js +1 -1
  107. package/report/renderer/dom.js +0 -17
  108. package/report/renderer/element-screenshot-renderer.js +9 -9
  109. package/{flow-report/test/setup/global-setup.ts → report/renderer/file-namer.js} +2 -5
  110. package/report/renderer/logger.js +4 -4
  111. package/report/renderer/package.json +4 -0
  112. package/report/renderer/report-renderer.js +11 -4
  113. package/report/renderer/report-ui-features.js +656 -43
  114. package/report/renderer/text-encoding.js +1 -1
  115. package/report/renderer/util.js +2 -2
  116. package/report/{generator/report-assets.js → report-assets.js} +3 -9
  117. package/report/{generator/report-generator.js → report-generator.js} +9 -37
  118. package/report/test/clients/package.json +4 -0
  119. package/report/test/clients/psi-test.js +1 -3
  120. package/report/test/renderer/category-renderer-test.js +3 -8
  121. package/report/test/renderer/components-test.js +0 -35
  122. package/report/test/renderer/crc-details-renderer-test.js +4 -5
  123. package/report/test/renderer/details-renderer-test.js +1 -3
  124. package/report/test/renderer/dom-test.js +6 -8
  125. package/report/test/renderer/i18n-test.js +0 -1
  126. package/report/test/renderer/package.json +4 -0
  127. package/report/test/renderer/performance-category-renderer-test.js +27 -30
  128. package/report/test/renderer/pwa-category-renderer-test.js +1 -2
  129. package/report/test/renderer/report-renderer-test.js +0 -1
  130. package/report/test/renderer/report-ui-features-test.js +28 -20
  131. package/report/test/renderer/snippet-renderer-test.js +0 -1
  132. package/report/test/renderer/text-encoding-test.js +0 -2
  133. package/report/test/renderer/util-test.js +0 -1
  134. package/report/test/{generator/report-generator-test.js → report-generator-test.js} +7 -9
  135. package/third-party/chromium-synchronization/installability-errors-test.js +27 -28
  136. package/third-party/snyk/snapshot.json +0 -2
  137. package/tsconfig.json +5 -24
  138. package/types/artifacts.d.ts +18 -5
  139. package/types/{lhr/audit-details.d.ts → audit-details.d.ts} +3 -18
  140. package/types/audit.d.ts +58 -7
  141. package/types/{lhr/budget.d.ts → budget.d.ts} +0 -0
  142. package/types/bundle-phobia-cli/index.d.ts +40 -0
  143. package/types/config.d.ts +8 -3
  144. package/types/externs.d.ts +119 -5
  145. package/types/gatherer.d.ts +2 -3
  146. package/types/global-lh.d.ts +14 -16
  147. package/{report/types/report-result.d.ts → types/html-renderer.d.ts} +17 -8
  148. package/types/{lhr/i18n.d.ts → i18n.d.ts} +25 -2
  149. package/types/{lhr/lhr.d.ts → lhr.d.ts} +9 -41
  150. package/types/smokehouse.d.ts +3 -3
  151. package/types/{lhr/treemap.d.ts → treemap.d.ts} +3 -3
  152. package/flow-report/assets/standalone-flow-template.html +0 -38
  153. package/flow-report/assets/styles.css +0 -310
  154. package/flow-report/jest.config.js +0 -24
  155. package/flow-report/src/app.tsx +0 -40
  156. package/flow-report/src/common.tsx +0 -72
  157. package/flow-report/src/icons.tsx +0 -96
  158. package/flow-report/src/sidebar/flow.tsx +0 -72
  159. package/flow-report/src/sidebar/sidebar.tsx +0 -89
  160. package/flow-report/src/summary/summary.tsx +0 -178
  161. package/flow-report/src/util.ts +0 -160
  162. package/flow-report/src/wrappers/gauge.tsx +0 -40
  163. package/flow-report/src/wrappers/report-renderer.tsx +0 -46
  164. package/flow-report/src/wrappers/report.tsx +0 -71
  165. package/flow-report/standalone-flow.tsx +0 -23
  166. package/flow-report/test/app-test.tsx +0 -48
  167. package/flow-report/test/common-test.tsx +0 -131
  168. package/flow-report/test/setup/env-setup.ts +0 -25
  169. package/flow-report/test/sidebar/flow-test.tsx +0 -71
  170. package/flow-report/test/sidebar/sidebar-test.tsx +0 -53
  171. package/flow-report/test/summary/summary-test.tsx +0 -132
  172. package/flow-report/test/util-test.tsx +0 -109
  173. package/flow-report/tsconfig.json +0 -39
  174. package/flow-report/types/flow-report.d.ts +0 -27
  175. package/lighthouse-core/audits/lcp-lazy-loaded.js +0 -83
  176. package/report/.eslintrc.cjs +0 -33
  177. package/report/generator/README.md +0 -9
  178. package/report/generator/flow-report-assets.js +0 -20
  179. package/report/generator/package.json +0 -4
  180. package/report/generator/tsconfig.json +0 -28
  181. package/report/renderer/drop-down-menu.js +0 -215
  182. package/report/renderer/features-util.js +0 -26
  183. package/report/renderer/open-tab.js +0 -137
  184. package/report/renderer/topbar-features.js +0 -309
  185. package/report/test/generator/file-namer-test.js +0 -28
  186. package/report/test/generator/package.json +0 -4
  187. package/report/test-assets/faux-psi-template.html +0 -126
  188. package/report/test-assets/faux-psi.js +0 -92
  189. package/report/tsconfig.json +0 -39
  190. package/report/types/augment-dom.d.ts +0 -24
  191. package/report/types/html-renderer.d.ts +0 -28
  192. package/types/lhr/audit-result.d.ts +0 -59
  193. package/types/lhr/flow.d.ts +0 -16
  194. package/types/lhr/settings.d.ts +0 -105
  195. package/types/lhr/tsconfig.json +0 -24
  196. package/types/query-selector.d.ts +0 -36
@@ -5,10 +5,6 @@
5
5
  */
6
6
  'use strict';
7
7
 
8
- const Audit = require('../../audits/audit.js');
9
- const BaseFRGatherer = require('../gather/base-gatherer.js');
10
- const i18n = require('../../lib/i18n/i18n.js');
11
-
12
8
  /**
13
9
  * @param {LH.Config.GathererDefn | LH.Config.AnyFRGathererDefn} gathererDefn
14
10
  * @return {gathererDefn is LH.Config.AnyFRGathererDefn}
@@ -38,177 +34,6 @@ function isValidArtifactDependency(dependent, dependency) {
38
34
  return true;
39
35
  }
40
36
 
41
- /**
42
- * Throws an error if the provided object does not implement the required Fraggle Rock gatherer interface.
43
- * @param {LH.Config.AnyFRGathererDefn} gathererDefn
44
- */
45
- function assertValidFRGatherer(gathererDefn) {
46
- const gatherer = gathererDefn.instance;
47
- const gathererName = gatherer.name;
48
-
49
- if (typeof gatherer.meta !== 'object') {
50
- throw new Error(`${gathererName} gatherer did not provide a meta object.`);
51
- }
52
-
53
- if (gatherer.meta.supportedModes.length === 0) {
54
- throw new Error(`${gathererName} gatherer did not support any gather modes.`);
55
- }
56
-
57
- if (
58
- typeof gatherer.getArtifact !== 'function' ||
59
- gatherer.getArtifact === BaseFRGatherer.prototype.getArtifact
60
- ) {
61
- throw new Error(`${gathererName} gatherer did not define a "getArtifact" method.`);
62
- }
63
- }
64
-
65
- /**
66
- * Throws an error if the provided object does not implement the required navigations interface.
67
- * @param {LH.Config.FRConfig['navigations']} navigationsDefn
68
- * @return {{warnings: string[]}}
69
- */
70
- function assertValidFRNavigations(navigationsDefn) {
71
- if (!navigationsDefn || !navigationsDefn.length) return {warnings: []};
72
-
73
- /** @type {string[]} */
74
- const warnings = [];
75
-
76
- // Assert that the first navigation has loadFailureMode fatal.
77
- const firstNavigation = navigationsDefn[0];
78
- if (firstNavigation.loadFailureMode !== 'fatal') {
79
- const currentMode = firstNavigation.loadFailureMode;
80
- const warning = [
81
- `"${firstNavigation.id}" is the first navigation but had a failure mode of ${currentMode}.`,
82
- `The first navigation will always be treated as loadFailureMode=fatal.`,
83
- ].join(' ');
84
-
85
- warnings.push(warning);
86
- firstNavigation.loadFailureMode = 'fatal';
87
- }
88
-
89
- // Assert that navigations have unique IDs.
90
- const navigationIds = navigationsDefn.map(navigation => navigation.id);
91
- const duplicateId = navigationIds.find(
92
- (id, i) => navigationIds.slice(i + 1).some(other => id === other)
93
- );
94
-
95
- if (duplicateId) {
96
- throw new Error(`Navigation must have unique identifiers, but "${duplicateId}" was repeated.`);
97
- }
98
-
99
- return {warnings};
100
- }
101
-
102
- /**
103
- * Throws an error if the provided object does not implement the required properties of an audit
104
- * definition.
105
- * @param {LH.Config.AuditDefn} auditDefinition
106
- */
107
- function assertValidAudit(auditDefinition) {
108
- const {implementation, path: auditPath} = auditDefinition;
109
- const auditName = auditPath ||
110
- (implementation && implementation.meta && implementation.meta.id) ||
111
- 'Unknown audit';
112
-
113
- if (typeof implementation.audit !== 'function' || implementation.audit === Audit.audit) {
114
- throw new Error(`${auditName} has no audit() method.`);
115
- }
116
-
117
- if (typeof implementation.meta.id !== 'string') {
118
- throw new Error(`${auditName} has no meta.id property, or the property is not a string.`);
119
- }
120
-
121
- if (!i18n.isStringOrIcuMessage(implementation.meta.title)) {
122
- throw new Error(`${auditName} has no meta.title property, or the property is not a string.`);
123
- }
124
-
125
- // If it'll have a ✔ or ✖ displayed alongside the result, it should have failureTitle
126
- if (
127
- !i18n.isStringOrIcuMessage(implementation.meta.failureTitle) &&
128
- implementation.meta.scoreDisplayMode === Audit.SCORING_MODES.BINARY
129
- ) {
130
- throw new Error(`${auditName} has no meta.failureTitle and should.`);
131
- }
132
-
133
- if (!i18n.isStringOrIcuMessage(implementation.meta.description)) {
134
- throw new Error(
135
- `${auditName} has no meta.description property, or the property is not a string.`
136
- );
137
- } else if (implementation.meta.description === '') {
138
- throw new Error(
139
- `${auditName} has an empty meta.description string. Please add a description for the UI.`
140
- );
141
- }
142
-
143
- if (!Array.isArray(implementation.meta.requiredArtifacts)) {
144
- throw new Error(
145
- `${auditName} has no meta.requiredArtifacts property, or the property is not an array.`
146
- );
147
- }
148
- }
149
-
150
- /**
151
- * @param {LH.Config.FRConfig['categories']} categories
152
- * @param {LH.Config.FRConfig['audits']} audits
153
- * @param {LH.Config.FRConfig['groups']} groups
154
- */
155
- function assertValidCategories(categories, audits, groups) {
156
- if (!categories) {
157
- return;
158
- }
159
-
160
- /** @type {Map<string, LH.Config.AuditDefn>} */
161
- const auditsKeyedById = new Map((audits || []).map(audit => {
162
- return [audit.implementation.meta.id, audit];
163
- }));
164
-
165
- Object.keys(categories).forEach(categoryId => {
166
- categories[categoryId].auditRefs.forEach((auditRef, index) => {
167
- if (!auditRef.id) {
168
- throw new Error(`missing an audit id at ${categoryId}[${index}]`);
169
- }
170
-
171
- const audit = auditsKeyedById.get(auditRef.id);
172
- if (!audit) {
173
- throw new Error(`could not find ${auditRef.id} audit for category ${categoryId}`);
174
- }
175
-
176
- const auditImpl = audit.implementation;
177
- const isManual = auditImpl.meta.scoreDisplayMode === 'manual';
178
- if (categoryId === 'accessibility' && !auditRef.group && !isManual) {
179
- throw new Error(`${auditRef.id} accessibility audit does not have a group`);
180
- }
181
-
182
- if (auditRef.weight > 0 && isManual) {
183
- throw new Error(`${auditRef.id} is manual but has a positive weight`);
184
- }
185
-
186
- if (auditRef.group && (!groups || !groups[auditRef.group])) {
187
- throw new Error(`${auditRef.id} references unknown group ${auditRef.group}`);
188
- }
189
- });
190
- });
191
- }
192
-
193
- /**
194
- * Validate the settings after they've been built.
195
- * @param {LH.Config.Settings} settings
196
- */
197
- function assertValidSettings(settings) {
198
- if (!settings.formFactor) {
199
- throw new Error(`\`settings.formFactor\` must be defined as 'mobile' or 'desktop'. See https://github.com/GoogleChrome/lighthouse/blob/master/docs/emulation.md`);
200
- }
201
-
202
- if (!settings.screenEmulation.disabled) {
203
- // formFactor doesn't control emulation. So we don't want a mismatch:
204
- // Bad mismatch A: user wants mobile emulation but scoring is configured for desktop
205
- // Bad mismtach B: user wants everything desktop and set formFactor, but accidentally not screenEmulation
206
- if (settings.screenEmulation.mobile !== (settings.formFactor === 'mobile')) {
207
- throw new Error(`Screen emulation mobile setting (${settings.screenEmulation.mobile}) does not match formFactor setting (${settings.formFactor}). See https://github.com/GoogleChrome/lighthouse/blob/master/docs/emulation.md`);
208
- }
209
- }
210
- }
211
-
212
37
  /**
213
38
  * Asserts that artifacts are in a valid dependency order that can be computed.
214
39
  *
@@ -230,26 +55,6 @@ function assertArtifactTopologicalOrder(navigations) {
230
55
  }
231
56
  }
232
57
 
233
- /**
234
- * @param {LH.Config.FRConfig} config
235
- * @return {{warnings: string[]}}
236
- */
237
- function assertValidConfig(config) {
238
- const {warnings} = assertValidFRNavigations(config.navigations);
239
-
240
- for (const artifactDefn of config.artifacts || []) {
241
- assertValidFRGatherer(artifactDefn.gatherer);
242
- }
243
-
244
- for (const auditDefn of config.audits || []) {
245
- assertValidAudit(auditDefn);
246
- }
247
-
248
- assertValidCategories(config.categories, config.audits, config.groups);
249
- assertValidSettings(config.settings);
250
- return {warnings};
251
- }
252
-
253
58
  /**
254
59
  * @param {string} artifactId
255
60
  * @param {string} dependencyKey
@@ -283,13 +88,7 @@ function throwInvalidArtifactDependency(artifactId, dependencyKey) {
283
88
  module.exports = {
284
89
  isFRGathererDefn,
285
90
  isValidArtifactDependency,
286
- assertValidFRGatherer,
287
- assertValidFRNavigations,
288
- assertValidAudit,
289
- assertValidCategories,
290
- assertValidSettings,
291
91
  assertArtifactTopologicalOrder,
292
- assertValidConfig,
293
92
  throwInvalidDependencyOrder,
294
93
  throwInvalidArtifactDependency,
295
94
  };
@@ -77,10 +77,6 @@ function finalizeArtifacts(baseArtifacts, gathererArtifacts) {
77
77
  artifacts.Timing = log.getTimeEntries();
78
78
  artifacts.LighthouseRunWarnings = deduplicateWarnings(warnings);
79
79
 
80
- if (artifacts.PageLoadError && !artifacts.URL.finalUrl) {
81
- artifacts.URL.finalUrl = artifacts.URL.requestedUrl;
82
- }
83
-
84
80
  // Check that the runner remembered to mutate the special-case URL artifact.
85
81
  if (!artifacts.URL.requestedUrl) throw new Error('Runner did not set requestedUrl');
86
82
  if (!artifacts.URL.finalUrl) throw new Error('Runner did not set finalUrl');
@@ -88,7 +88,6 @@ async function _navigate(navigationContext) {
88
88
  try {
89
89
  const {finalUrl, warnings} = await gotoURL(driver, requestedUrl, {
90
90
  ...navigationContext.navigation,
91
- debugNavigation: config.settings.debugNavigation,
92
91
  maxWaitForFcp: config.settings.maxWaitForFcp,
93
92
  maxWaitForLoad: config.settings.maxWaitForLoad,
94
93
  waitUntil: navigationContext.navigation.pauseAfterFcpMs ? ['fcp', 'load'] : ['load'],
@@ -105,9 +104,9 @@ async function _navigate(navigationContext) {
105
104
  /**
106
105
  * @param {NavigationContext} navigationContext
107
106
  * @param {PhaseState} phaseState
108
- * @return {Promise<{devtoolsLog: LH.DevtoolsLog, records: Array<LH.Artifacts.NetworkRequest>} | undefined>}
107
+ * @return {Promise<Array<LH.Artifacts.NetworkRequest> | undefined>}
109
108
  */
110
- async function _collectNetworkData(navigationContext, phaseState) {
109
+ async function _collectNetworkRecords(navigationContext, phaseState) {
111
110
  const devtoolsLogArtifactDefn = phaseState.artifactDefinitions.find(
112
111
  definition => definition.gatherer.instance.meta.symbol === DevtoolsLog.symbol
113
112
  );
@@ -119,7 +118,7 @@ async function _collectNetworkData(navigationContext, phaseState) {
119
118
 
120
119
  const devtoolsLog = await phaseState.artifactState.getArtifact[devtoolsLogArtifactId];
121
120
  const records = await NetworkRecords.request(devtoolsLog, navigationContext);
122
- return {devtoolsLog, records};
121
+ return records;
123
122
  }
124
123
 
125
124
  /**
@@ -137,12 +136,12 @@ async function _computeNavigationResult(
137
136
  ) {
138
137
  const {navigationError, finalUrl} = navigateResult;
139
138
  const warnings = [...setupResult.warnings, ...navigateResult.warnings];
140
- const networkData = await _collectNetworkData(navigationContext, phaseState);
141
- const pageLoadError = networkData
139
+ const networkRecords = await _collectNetworkRecords(navigationContext, phaseState);
140
+ const pageLoadError = networkRecords
142
141
  ? getPageLoadError(navigationError, {
143
142
  url: finalUrl,
144
143
  loadFailureMode: navigationContext.navigation.loadFailureMode,
145
- networkRecords: networkData.records,
144
+ networkRecords,
146
145
  })
147
146
  : navigationError;
148
147
 
@@ -151,15 +150,10 @@ async function _computeNavigationResult(
151
150
  const localizedMessage = i18n.getFormatted(pageLoadError.friendlyMessage, locale);
152
151
  log.error('NavigationRunner', localizedMessage, navigationContext.requestedUrl);
153
152
 
154
- /** @type {Partial<LH.GathererArtifacts>} */
155
- const artifacts = {};
156
- const pageLoadErrorId = `pageLoadError-${navigationContext.navigation.id}`;
157
- if (networkData) artifacts.devtoolsLogs = {[pageLoadErrorId]: networkData.devtoolsLog};
158
-
159
153
  return {
160
154
  finalUrl,
161
155
  pageLoadError,
162
- artifacts,
156
+ artifacts: {},
163
157
  warnings: [...warnings, pageLoadError.friendlyMessage],
164
158
  };
165
159
  } else {
@@ -217,12 +211,11 @@ async function _navigations({driver, config, requestedUrl, computedCache}) {
217
211
  computedCache,
218
212
  };
219
213
 
220
- let shouldHaltNavigations = false;
221
214
  const navigationResult = await _navigation(navigationContext);
222
215
  if (navigation.loadFailureMode === 'fatal') {
223
216
  if (navigationResult.pageLoadError) {
224
217
  artifacts.PageLoadError = navigationResult.pageLoadError;
225
- shouldHaltNavigations = true;
218
+ break;
226
219
  }
227
220
 
228
221
  artifacts.URL = {requestedUrl, finalUrl: navigationResult.finalUrl};
@@ -230,7 +223,6 @@ async function _navigations({driver, config, requestedUrl, computedCache}) {
230
223
 
231
224
  LighthouseRunWarnings.push(...navigationResult.warnings);
232
225
  Object.assign(artifacts, navigationResult.artifacts);
233
- if (shouldHaltNavigations) break;
234
226
  }
235
227
 
236
228
  return {artifacts: {...artifacts, LighthouseRunWarnings}};
@@ -22,8 +22,6 @@
22
22
 
23
23
  /** @typedef {LH.Gatherer.FRTransitionalContext<LH.Gatherer.DependencyKey>['dependencies']} Dependencies */
24
24
 
25
- const log = require('lighthouse-logger');
26
-
27
25
  /**
28
26
  *
29
27
  * @param {{id: string}} dependency
@@ -77,8 +75,6 @@ async function collectPhaseArtifacts(options) {
77
75
  const priorPhaseArtifacts = (priorPhase && artifactState[priorPhase]) || {};
78
76
 
79
77
  for (const artifactDefn of artifactDefinitions) {
80
- const logLevel = phase === 'getArtifact' ? 'log' : 'verbose';
81
- log[logLevel](`artifacts:${phase}`, artifactDefn.id);
82
78
  const gatherer = artifactDefn.gatherer.instance;
83
79
 
84
80
  const priorArtifactPromise = priorPhaseArtifacts[artifactDefn.id] || Promise.resolve();
@@ -5,12 +5,8 @@
5
5
  */
6
6
  'use strict';
7
7
 
8
- const LHError = require('../../lib/lh-error.js');
9
8
  const SessionEmitMonkeypatch = Symbol('monkeypatch');
10
9
 
11
- // Controls how long to wait for a response after sending a DevTools protocol command.
12
- const DEFAULT_PROTOCOL_TIMEOUT = 30000;
13
-
14
10
  /** @implements {LH.Gatherer.FRProtocolSession} */
15
11
  class ProtocolSession {
16
12
  /**
@@ -18,8 +14,6 @@ class ProtocolSession {
18
14
  */
19
15
  constructor(session) {
20
16
  this._session = session;
21
- /** @type {number|undefined} */
22
- this._nextProtocolTimeout = undefined;
23
17
 
24
18
  // FIXME: Monkeypatch puppeteer to be able to listen to *all* protocol events.
25
19
  // This patched method will now emit a copy of every event on `*`.
@@ -38,21 +32,21 @@ class ProtocolSession {
38
32
  * @return {boolean}
39
33
  */
40
34
  hasNextProtocolTimeout() {
41
- return this._nextProtocolTimeout !== undefined;
35
+ return false;
42
36
  }
43
37
 
44
38
  /**
45
39
  * @return {number}
46
40
  */
47
41
  getNextProtocolTimeout() {
48
- return this._nextProtocolTimeout || DEFAULT_PROTOCOL_TIMEOUT;
42
+ return Number.MAX_SAFE_INTEGER;
49
43
  }
50
44
 
51
45
  /**
52
46
  * @param {number} ms
53
47
  */
54
- setNextProtocolTimeout(ms) {
55
- this._nextProtocolTimeout = ms;
48
+ setNextProtocolTimeout(ms) { // eslint-disable-line no-unused-vars
49
+ // TODO(FR-COMPAT): support protocol timeout
56
50
  }
57
51
 
58
52
  /**
@@ -108,26 +102,7 @@ class ProtocolSession {
108
102
  * @return {Promise<LH.CrdpCommands[C]['returnType']>}
109
103
  */
110
104
  sendCommand(method, ...params) {
111
- const timeoutMs = this.getNextProtocolTimeout();
112
- this._nextProtocolTimeout = undefined;
113
-
114
- /** @type {NodeJS.Timer|undefined} */
115
- let timeout;
116
- const timeoutPromise = new Promise((resolve, reject) => {
117
- if (timeoutMs === Infinity) return;
118
-
119
- timeout = setTimeout((() => {
120
- const err = new LHError(LHError.errors.PROTOCOL_TIMEOUT, {protocolMethod: method});
121
- reject(err);
122
- }), timeoutMs);
123
- });
124
-
125
- const resultPromise = this._session.send(method, ...params);
126
- const resultWithTimeoutPromise = Promise.race([resultPromise, timeoutPromise]);
127
-
128
- return resultWithTimeoutPromise.finally(() => {
129
- if (timeout) clearTimeout(timeout);
130
- });
105
+ return this._session.send(method, ...params);
131
106
  }
132
107
  }
133
108
 
@@ -6,7 +6,7 @@
6
6
  'use strict';
7
7
 
8
8
  const NetworkMonitor = require('./network-monitor.js');
9
- const {waitForFullyLoaded, waitForFrameNavigated, waitForUserToContinue} = require('./wait-for-condition.js'); // eslint-disable-line max-len
9
+ const {waitForFullyLoaded, waitForFrameNavigated} = require('./wait-for-condition.js');
10
10
  const constants = require('../../config/constants.js');
11
11
  const i18n = require('../../lib/i18n/i18n.js');
12
12
  const URL = require('../../lib/url-shim.js');
@@ -39,7 +39,7 @@ const DEFAULT_NETWORK_QUIET_THRESHOLD = 5000;
39
39
  // Controls how long to wait between longtasks before determining the CPU is idle, off by default
40
40
  const DEFAULT_CPU_QUIET_THRESHOLD = 0;
41
41
 
42
- /** @typedef {{waitUntil: Array<'fcp'|'load'|'navigated'>} & LH.Config.SharedPassNavigationJson & Partial<Pick<LH.Config.Settings, 'maxWaitForFcp'|'maxWaitForLoad'|'debugNavigation'>>} NavigationOptions */
42
+ /** @typedef {{waitUntil: Array<'fcp'|'load'|'navigated'>} & LH.Config.SharedPassNavigationJson & Partial<Pick<LH.Config.Settings, 'maxWaitForFcp'|'maxWaitForLoad'>>} NavigationOptions */
43
43
 
44
44
  /** @param {NavigationOptions} options */
45
45
  function resolveWaitForFullyLoadedOptions(options) {
@@ -121,10 +121,6 @@ async function gotoURL(driver, url, options) {
121
121
  await waitforPageNavigateCmd;
122
122
  await networkMonitor.disable();
123
123
 
124
- if (options.debugNavigation) {
125
- await waitForUserToContinue(driver);
126
- }
127
-
128
124
  return {
129
125
  finalUrl,
130
126
  warnings: getNavigationWarnings({timedOut, finalUrl, requestedUrl: url}),
@@ -142,7 +138,10 @@ function getNavigationWarnings(navigation) {
142
138
 
143
139
  if (navigation.timedOut) warnings.push(str_(UIStrings.warningTimeout));
144
140
 
145
- if (!URL.equalWithExcludedFragments(requestedUrl, finalUrl)) {
141
+ if (
142
+ !URL.equalWithExcludedFragments(requestedUrl, finalUrl) &&
143
+ !finalUrl.startsWith('chrome-error://')
144
+ ) {
146
145
  warnings.push(str_(UIStrings.warningRedirected, {
147
146
  requested: requestedUrl,
148
147
  final: finalUrl,
@@ -490,32 +490,6 @@ async function waitForFullyLoaded(session, networkMonitor, options) {
490
490
  return cleanupFn();
491
491
  }
492
492
 
493
- /**
494
- * @param {LH.Gatherer.FRTransitionalDriver} driver
495
- */
496
- function waitForUserToContinue(driver) {
497
- /* c8 ignore start */
498
- function createInPagePromise() {
499
- let resolve = () => {};
500
- /** @type {Promise<void>} */
501
- const promise = new Promise(r => resolve = r);
502
-
503
- // eslint-disable-next-line no-console
504
- console.log([
505
- `You have enabled Lighthouse navigation debug mode.`,
506
- `When you have finished inspecting the page, evaluate "continueLighthouseRun()"`,
507
- `in the console to continue with the Lighthouse run.`,
508
- ].join(' '));
509
-
510
- window.continueLighthouseRun = resolve;
511
- return promise;
512
- }
513
- /* c8 ignore stop */
514
-
515
- driver.defaultSession.setNextProtocolTimeout(2 ** 31 - 1);
516
- return driver.executionContext.evaluate(createInPagePromise, {args: []});
517
- }
518
-
519
493
  module.exports = {
520
494
  waitForNothing,
521
495
  waitForFrameNavigated,
@@ -524,5 +498,4 @@ module.exports = {
524
498
  waitForNetworkIdle,
525
499
  waitForCPUIdle,
526
500
  waitForFullyLoaded,
527
- waitForUserToContinue,
528
501
  };
@@ -92,19 +92,6 @@ class Fetcher {
92
92
  }
93
93
  }
94
94
 
95
- /**
96
- * `Network.loadNetworkResource` was introduced in M88.
97
- * The long timeout bug with `IO.read` was fixed in M92:
98
- * https://bugs.chromium.org/p/chromium/issues/detail?id=1191757
99
- * Lightrider has a bug forcing us to use the old version for now:
100
- * https://docs.google.com/document/d/1V-DxgsOFMPxUuFrdGPQpyiCqSljvgNlOqXCtqDtd0b8/edit?usp=sharing&resourcekey=0-aIaIqcHFKG-0dX4MAudBEw
101
- * @return {Promise<boolean>}
102
- */
103
- async shouldUseLegacyFetcher() {
104
- const {milestone} = await getBrowserVersion(this.session);
105
- return milestone < 92 || Boolean(global.isLightrider);
106
- }
107
-
108
95
  /**
109
96
  * Requires that `fetcher.enable` has been called.
110
97
  *
@@ -119,11 +106,14 @@ class Fetcher {
119
106
  throw new Error('Must call `enable` before using fetchResource');
120
107
  }
121
108
 
122
- if (await this.shouldUseLegacyFetcher()) {
123
- return this._fetchResourceIframe(url, options);
109
+ // `Network.loadNetworkResource` was introduced in M88.
110
+ // The long timeout bug with `IO.read` was fixed in M92:
111
+ // https://bugs.chromium.org/p/chromium/issues/detail?id=1191757
112
+ const {milestone} = await getBrowserVersion(this.session);
113
+ if (milestone >= 92) {
114
+ return await this._fetchResourceOverProtocol(url, options);
124
115
  }
125
-
126
- return this._fetchResourceOverProtocol(url, options);
116
+ return await this._fetchResourceIframe(url, options);
127
117
  }
128
118
 
129
119
  /**
@@ -74,7 +74,6 @@ class GatherRunner {
74
74
  const {finalUrl, warnings} = await navigation.gotoURL(driver, requestedUrl, {
75
75
  waitUntil: passContext.passConfig.recordTrace ?
76
76
  ['load', 'fcp'] : ['load'],
77
- debugNavigation: passContext.settings.debugNavigation,
78
77
  maxWaitForFcp: passContext.settings.maxWaitForFcp,
79
78
  maxWaitForLoad: passContext.settings.maxWaitForLoad,
80
79
  ...passContext.passConfig,
@@ -6,6 +6,7 @@
6
6
  'use strict';
7
7
 
8
8
  const FRGatherer = require('../../../fraggle-rock/gather/base-gatherer.js');
9
+ const {getBrowserVersion} = require('../../driver/environment.js');
9
10
 
10
11
  /* global fetch, location */
11
12
 
@@ -37,9 +38,12 @@ class RobotsTxt extends FRGatherer {
37
38
  * @return {Promise<LH.Artifacts['RobotsTxt']>}
38
39
  */
39
40
  async getArtifact(passContext) {
41
+ const {milestone} = await getBrowserVersion(passContext.driver.defaultSession);
42
+
43
+ // TODO: Remove when 92 hits stable.
40
44
  // 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()) {
45
+ // Only use the fetcher if we are fetching over the CDP.
46
+ if (milestone < 92) {
43
47
  return passContext.driver.executionContext.evaluate(getRobotsTxtContent, {
44
48
  args: [],
45
49
  useIsolation: true,
@@ -205,7 +205,7 @@ function gatherTapTargets(tapTargetsSelector, className) {
205
205
 
206
206
  /** @type {{
207
207
  tapTargetElement: Element,
208
- clientRects: LH.Artifacts.Rect[]
208
+ clientRects: ClientRect[]
209
209
  }[]} */
210
210
  const tapTargetsWithClientRects = [];
211
211
  tapTargetElements.forEach(tapTargetElement => {
@@ -238,7 +238,7 @@ function gatherTapTargets(tapTargetsSelector, className) {
238
238
 
239
239
  /** @type {{
240
240
  tapTargetElement: Element,
241
- visibleClientRects: LH.Artifacts.Rect[]
241
+ visibleClientRects: ClientRect[]
242
242
  }[]} */
243
243
  const tapTargetsWithVisibleClientRects = [];
244
244
  // We use separate loop here to get visible client rects because that involves
@@ -120,7 +120,7 @@ class Stacks extends FRGatherer {
120
120
  * @param {LH.Gatherer.FRTransitionalContext} context
121
121
  * @return {Promise<LH.Artifacts['Stacks']>}
122
122
  */
123
- async getArtifact(context) {
123
+ async snapshot(context) {
124
124
  return Stacks.collectStacks(context.driver.executionContext);
125
125
  }
126
126
  }
@@ -142,7 +142,7 @@ function saveLhr(lhr, basePath) {
142
142
  /**
143
143
  * Filter traces and extract screenshots to prepare for saving.
144
144
  * @param {LH.Artifacts} artifacts
145
- * @param {LH.Result['audits']} [audits]
145
+ * @param {LH.Audit.Results} [audits]
146
146
  * @return {Promise<Array<PreparedAssets>>}
147
147
  */
148
148
  async function prepareAssets(artifacts, audits) {
@@ -272,7 +272,7 @@ async function saveLanternDebugTraces(pathWithBasename) {
272
272
  /**
273
273
  * Writes trace(s) and associated asset(s) to disk.
274
274
  * @param {LH.Artifacts} artifacts
275
- * @param {LH.Result['audits']} audits
275
+ * @param {LH.Audit.Results} audits
276
276
  * @param {string} pathWithBasename
277
277
  * @return {Promise<void>}
278
278
  */
@@ -46,7 +46,6 @@ SDK.TextSourceMap.prototype.mappings = function() {
46
46
  };
47
47
 
48
48
  const originalReversedMappings = SDK.TextSourceMap.prototype._reversedMappings;
49
- /** @param {string} sourceURL */
50
49
  SDK.TextSourceMap.prototype._reversedMappings = function(sourceURL) {
51
50
  const mappings = originalReversedMappings.call(this, sourceURL);
52
51
  extendArray(mappings);
@@ -28,11 +28,12 @@ const UIStrings = {
28
28
  missingScriptSrc: 'script-src directive is missing. ' +
29
29
  'This can allow the execution of unsafe scripts.',
30
30
  /** Message shown when a CSP does not have a script-src directive. Shown in a table with a list of other CSP vulnerabilities and suggestions. "CSP" stands for "Content Security Policy". "object-src" and "'none'" do not need to be translated. */
31
- missingObjectSrc: 'Missing object-src allows the injection of plugins ' +
32
- 'that execute unsafe scripts. Consider setting object-src to \'none\' if you can.',
31
+ missingObjectSrc: 'Elements controlled by object-src are considered legacy features. ' +
32
+ 'Consider setting object-src to \'none\' to prevent the injection of ' +
33
+ 'plugins that execute unsafe scripts.',
33
34
  /** Message shown when a CSP uses a domain allowlist to filter out malicious scripts. Shown in a table with a list of other CSP vulnerabilities and suggestions. "CSP" stands for "Content Security Policy". "CSP", "'strict-dynamic'", "nonces", and "hashes" do not need to be translated. "allowlists" can be interpreted as "whitelist". */
34
35
  strictDynamic: 'Host allowlists can frequently be bypassed. Consider using ' +
35
- 'CSP nonces or hashes instead, along with \'strict-dynamic\' if necessary.',
36
+ '\'strict-dynamic\' in combination with CSP nonces or hashes.',
36
37
  /** Message shown when a CSP allows inline scripts to be run in the page. Shown in a table with a list of other CSP vulnerabilities and suggestions. "CSP" stands for "Content Security Policy". "CSP", "'unsafe-inline'", "nonces", and "hashes" do not need to be translated. */
37
38
  unsafeInline: '\'unsafe-inline\' allows the execution of unsafe in-page scripts ' +
38
39
  'and event handlers. Consider using CSP nonces or hashes to allow scripts individually.',
@@ -75,18 +76,6 @@ const UIStrings = {
75
76
  /** Message shown when a CSP uses the deprecated disown-opener directive. Shown in a table with a list of other CSP vulnerabilities and suggestions. "CSP" stands for "Content Security Policy". "disown-opener", "CSP3" and "Cross-Origin-Opener-Policy" do not need to be translated. */
76
77
  deprecatedDisownOpener: 'disown-opener is deprecated since CSP3. ' +
77
78
  'Please, use the Cross-Origin-Opener-Policy header instead.',
78
- /**
79
- * @description Message shown when a CSP wildcard allows unsafe scripts to be run in the page. Shown in a table with a list of other CSP vulnerabilities and suggestions. "CSP" stands for "Content Security Policy".
80
- * @example {*} keyword
81
- */
82
- plainWildcards: 'Avoid using plain wildcards ({keyword}) in this directive. ' +
83
- 'Plain wildcards allow scripts to be sourced from an unsafe domain.',
84
- /**
85
- * @description Message shown when a CSP URL scheme allows unsafe scripts to be run in the page. Shown in a table with a list of other CSP vulnerabilities and suggestions. "CSP" stands for "Content Security Policy".
86
- * @example {https:} keyword
87
- */
88
- plainUrlScheme: 'Avoid using plain URL schemes ({keyword}) in this directive. ' +
89
- 'Plain URL schemes allow scripts to be sourced from an unsafe domain.',
90
79
  };
91
80
 
92
81
  const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
@@ -102,8 +91,6 @@ const FINDING_TO_UI_STRING = {
102
91
  [Directive.OBJECT_SRC]: str_(UIStrings.missingObjectSrc),
103
92
  },
104
93
  [Type.SCRIPT_UNSAFE_INLINE]: str_(UIStrings.unsafeInline),
105
- [Type.PLAIN_WILDCARD]: UIStrings.plainWildcards,
106
- [Type.PLAIN_URL_SCHEMES]: UIStrings.plainUrlScheme,
107
94
  [Type.NONCE_LENGTH]: str_(UIStrings.nonceLength),
108
95
  [Type.NONCE_CHARSET]: str_(UIStrings.nonceCharset),
109
96
  [Type.DEPRECATED_DIRECTIVE]: {