lighthouse 8.4.0 → 8.5.0-dev.20210921

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 (107) hide show
  1. package/changelog.md +79 -0
  2. package/flow-report/assets/styles.css +85 -61
  3. package/flow-report/src/app.tsx +6 -2
  4. package/flow-report/src/common.tsx +0 -41
  5. package/flow-report/src/icons.tsx +11 -0
  6. package/flow-report/src/summary/summary.tsx +32 -25
  7. package/flow-report/src/topbar.tsx +60 -0
  8. package/flow-report/src/util.ts +0 -1
  9. package/flow-report/src/wrappers/{gauge.tsx → category-score.tsx} +11 -5
  10. package/flow-report/test/summary/summary-test.tsx +3 -3
  11. package/flow-report/tsconfig.json +6 -22
  12. package/{lighthouse-core/gather/gatherers/gather-context.js → lighthouse-cli/.eslintrc.cjs} +20 -18
  13. package/lighthouse-cli/bin.js +31 -21
  14. package/lighthouse-cli/cli-flags.js +12 -7
  15. package/lighthouse-cli/commands/commands.js +2 -7
  16. package/lighthouse-cli/commands/list-audits.js +2 -2
  17. package/lighthouse-cli/commands/list-trace-categories.js +2 -2
  18. package/lighthouse-cli/index.js +3 -1
  19. package/lighthouse-cli/package.json +4 -0
  20. package/lighthouse-cli/printer.js +4 -3
  21. package/lighthouse-cli/run.js +14 -15
  22. package/lighthouse-cli/sentry-prompt.js +5 -6
  23. package/lighthouse-cli/test/smokehouse/frontends/back-compat-util.js +1 -1
  24. package/lighthouse-cli/test/smokehouse/frontends/lib.js +5 -4
  25. package/lighthouse-cli/test/smokehouse/frontends/node.js +1 -1
  26. package/lighthouse-cli/test/smokehouse/frontends/smokehouse-bin.js +93 -14
  27. package/lighthouse-cli/test/smokehouse/lib/child-process-error.js +1 -1
  28. package/lighthouse-cli/test/smokehouse/lib/concurrent-mapper.js +1 -1
  29. package/lighthouse-cli/test/smokehouse/lib/local-console.js +1 -1
  30. package/lighthouse-cli/test/smokehouse/lighthouse-runners/bundle.js +20 -16
  31. package/lighthouse-cli/test/smokehouse/lighthouse-runners/cli.js +15 -12
  32. package/lighthouse-cli/test/smokehouse/report-assert.js +23 -13
  33. package/lighthouse-cli/test/smokehouse/smokehouse.js +32 -11
  34. package/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js +0 -2
  35. package/lighthouse-core/audits/byte-efficiency/uses-responsive-images-snapshot.js +6 -1
  36. package/lighthouse-core/audits/dobetterweb/js-libraries.js +5 -0
  37. package/lighthouse-core/audits/dobetterweb/uses-http2.js +1 -0
  38. package/lighthouse-core/audits/lcp-lazy-loaded.js +1 -0
  39. package/lighthouse-core/audits/oopif-iframe-test-audit.js +30 -0
  40. package/lighthouse-core/computed/metrics/metric.js +2 -1
  41. package/lighthouse-core/config/config-helpers.js +47 -3
  42. package/lighthouse-core/config/config.js +3 -46
  43. package/lighthouse-core/config/default-config.js +0 -1
  44. package/lighthouse-core/fraggle-rock/config/config.js +22 -4
  45. package/lighthouse-core/fraggle-rock/config/default-config.js +0 -9
  46. package/lighthouse-core/fraggle-rock/config/filters.js +3 -0
  47. package/lighthouse-core/fraggle-rock/config/validation.js +19 -1
  48. package/lighthouse-core/fraggle-rock/gather/base-artifacts.js +8 -3
  49. package/lighthouse-core/fraggle-rock/gather/driver.js +10 -0
  50. package/lighthouse-core/fraggle-rock/gather/navigation-runner.js +40 -19
  51. package/lighthouse-core/fraggle-rock/gather/runner-helpers.js +3 -0
  52. package/lighthouse-core/fraggle-rock/gather/session.js +44 -1
  53. package/lighthouse-core/fraggle-rock/gather/snapshot-runner.js +4 -1
  54. package/lighthouse-core/fraggle-rock/gather/timespan-runner.js +5 -1
  55. package/lighthouse-core/gather/devtools-log.js +9 -3
  56. package/lighthouse-core/gather/driver/network-monitor.js +57 -13
  57. package/lighthouse-core/gather/driver/prepare.js +43 -17
  58. package/lighthouse-core/gather/driver/target-manager.js +125 -0
  59. package/lighthouse-core/gather/driver.js +20 -0
  60. package/lighthouse-core/gather/fetcher.js +2 -2
  61. package/lighthouse-core/gather/gather-runner.js +1 -1
  62. package/lighthouse-core/gather/gatherers/css-usage.js +0 -2
  63. package/lighthouse-core/gather/gatherers/devtools-log.js +11 -9
  64. package/lighthouse-core/gather/gatherers/js-usage.js +0 -1
  65. package/lighthouse-core/gather/gatherers/script-elements.js +4 -5
  66. package/lighthouse-core/gather/gatherers/seo/robots-txt.js +3 -3
  67. package/lighthouse-core/gather/gatherers/trace.js +4 -5
  68. package/lighthouse-core/index.js +4 -0
  69. package/lighthouse-core/lib/emulation.js +0 -1
  70. package/lighthouse-core/lib/i18n/locales/en-US.json +6 -0
  71. package/lighthouse-core/lib/i18n/locales/en-XL.json +6 -0
  72. package/lighthouse-core/lib/network-recorder.js +8 -0
  73. package/lighthouse-core/lib/proto-preprocessor.js +5 -10
  74. package/lighthouse-core/runner.js +1 -3
  75. package/package.json +12 -9
  76. package/readme.md +2 -0
  77. package/report/assets/styles.css +120 -12
  78. package/report/assets/templates.html +13 -0
  79. package/report/clients/psi.js +5 -1
  80. package/report/generator/tsconfig.json +5 -16
  81. package/report/renderer/category-renderer.js +58 -5
  82. package/report/renderer/components.js +25 -5
  83. package/report/renderer/performance-category-renderer.js +5 -5
  84. package/report/renderer/pwa-category-renderer.js +10 -0
  85. package/report/renderer/report-renderer.js +12 -3
  86. package/report/renderer/report-ui-features.js +1 -4
  87. package/report/test/renderer/category-renderer-test.js +19 -0
  88. package/report/tsconfig.json +6 -21
  89. package/report/types/report-result.d.ts +1 -1
  90. package/root.js +3 -1
  91. package/third-party/snyk/snapshot.json +2 -2
  92. package/tsconfig-all.json +15 -0
  93. package/tsconfig-base.json +24 -0
  94. package/tsconfig.json +8 -18
  95. package/types/artifacts.d.ts +7 -7
  96. package/types/config.d.ts +1 -1
  97. package/types/gatherer.d.ts +7 -1
  98. package/types/lhr/flow.d.ts +0 -1
  99. package/types/lhr/lhr.d.ts +5 -1
  100. package/types/lhr/tsconfig.json +4 -14
  101. package/types/protocol.d.ts +2 -0
  102. package/types/smokehouse.d.ts +2 -1
  103. package/dist/report/flow.js +0 -149
  104. package/dist/report/standalone.js +0 -228
  105. package/flow-report/test/common-test.tsx +0 -131
  106. package/lighthouse-core/gather/gatherers/host-form-factor.js +0 -31
  107. package/lighthouse-core/gather/gatherers/host-user-agent.js +0 -29
@@ -88,25 +88,25 @@ async function resetStorageForNavigation(session, navigation) {
88
88
  }
89
89
 
90
90
  /**
91
- * Prepares a target for a particular navigation by resetting storage and setting throttling.
91
+ * Prepares a target for observational analysis by setting throttling and network headers/blocked patterns.
92
92
  *
93
- * This method assumes `prepareTargetForNavigationMode` has already been invoked.
93
+ * This method assumes `prepareTargetForNavigationMode` or `prepareTargetForTimespanMode` has already been invoked.
94
94
  *
95
95
  * @param {LH.Gatherer.FRProtocolSession} session
96
96
  * @param {LH.Config.Settings} settings
97
- * @param {Pick<LH.Config.NavigationDefn, 'disableThrottling'|'disableStorageReset'|'blockedUrlPatterns'> & {url: string}} navigation
97
+ * @param {{disableThrottling: boolean, blockedUrlPatterns?: string[]}} options
98
98
  */
99
- async function prepareNetworkForNavigation(session, settings, navigation) {
100
- const status = {msg: 'Preparing network conditions', id: `lh:gather:prepareNetworkForNavigation`};
99
+ async function prepareThrottlingAndNetwork(session, settings, options) {
100
+ const status = {msg: 'Preparing network conditions', id: `lh:gather:prepareThrottlingAndNetwork`};
101
101
  log.time(status);
102
102
 
103
- if (navigation.disableThrottling) await emulation.clearThrottling(session);
103
+ if (options.disableThrottling) await emulation.clearThrottling(session);
104
104
  else await emulation.throttle(session, settings);
105
105
 
106
106
  // Set request blocking before any network activity.
107
- // No "clearing" is done at the end of the navigation since Network.setBlockedURLs([]) will unset all if
108
- // neccessary at the beginning of the next navigation.
109
- const blockedUrls = (navigation.blockedUrlPatterns || []).concat(
107
+ // No "clearing" is done at the end of the recording since Network.setBlockedURLs([]) will unset all if
108
+ // neccessary at the beginning of the next section.
109
+ const blockedUrls = (options.blockedUrlPatterns || []).concat(
110
110
  settings.blockedUrlPatterns || []
111
111
  );
112
112
  await session.sendCommand('Network.setBlockedURLs', {urls: blockedUrls});
@@ -118,16 +118,14 @@ async function prepareNetworkForNavigation(session, settings, navigation) {
118
118
  }
119
119
 
120
120
  /**
121
- * Prepares a target to be analyzed in navigation mode by enabling protocol domains, emulation, and new document
122
- * handlers for global APIs or error handling.
123
- *
124
- * This method should be used in combination with `prepareTargetForIndividualNavigation` before a specific navigation occurs.
121
+ * Prepares a target to be analyzed by setting up device emulation (screen/UA, not throttling) and
122
+ * async stack traces for network initiators.
125
123
  *
126
124
  * @param {LH.Gatherer.FRTransitionalDriver} driver
127
125
  * @param {LH.Config.Settings} settings
128
126
  */
129
- async function prepareTargetForNavigationMode(driver, settings) {
130
- // Enable network domain here so future calls to `emulate()` don't clear cache (#12631)
127
+ async function prepareDeviceEmulationAndAsyncStacks(driver, settings) {
128
+ // Enable network domain here so future calls to `emulate()` don't clear cache (https://github.com/GoogleChrome/lighthouse/issues/12631)
131
129
  await driver.defaultSession.sendCommand('Network.enable');
132
130
 
133
131
  // Emulate our target device screen and user agent.
@@ -135,6 +133,33 @@ async function prepareTargetForNavigationMode(driver, settings) {
135
133
 
136
134
  // Enable better stacks on network requests.
137
135
  await enableAsyncStacks(driver.defaultSession);
136
+ }
137
+
138
+ /**
139
+ * Prepares a target to be analyzed in timespan mode by enabling protocol domains, emulation, and throttling.
140
+ *
141
+ * @param {LH.Gatherer.FRTransitionalDriver} driver
142
+ * @param {LH.Config.Settings} settings
143
+ */
144
+ async function prepareTargetForTimespanMode(driver, settings) {
145
+ await prepareDeviceEmulationAndAsyncStacks(driver, settings);
146
+ await prepareThrottlingAndNetwork(driver.defaultSession, settings, {
147
+ disableThrottling: false,
148
+ blockedUrlPatterns: undefined,
149
+ });
150
+ }
151
+
152
+ /**
153
+ * Prepares a target to be analyzed in navigation mode by enabling protocol domains, emulation, and new document
154
+ * handlers for global APIs or error handling.
155
+ *
156
+ * This method should be used in combination with `prepareTargetForIndividualNavigation` before a specific navigation occurs.
157
+ *
158
+ * @param {LH.Gatherer.FRTransitionalDriver} driver
159
+ * @param {LH.Config.Settings} settings
160
+ */
161
+ async function prepareTargetForNavigationMode(driver, settings) {
162
+ await prepareDeviceEmulationAndAsyncStacks(driver, settings);
138
163
 
139
164
  // Automatically handle any JavaScript dialogs to prevent a hung renderer.
140
165
  await dismissJavaScriptDialogs(driver.defaultSession);
@@ -168,13 +193,14 @@ async function prepareTargetForIndividualNavigation(session, settings, navigatio
168
193
  warnings.push(...storageWarnings);
169
194
  }
170
195
 
171
- await prepareNetworkForNavigation(session, settings, navigation);
196
+ await prepareThrottlingAndNetwork(session, settings, navigation);
172
197
 
173
198
  return {warnings};
174
199
  }
175
200
 
176
201
  module.exports = {
177
- prepareNetworkForNavigation,
202
+ prepareThrottlingAndNetwork,
203
+ prepareTargetForTimespanMode,
178
204
  prepareTargetForNavigationMode,
179
205
  prepareTargetForIndividualNavigation,
180
206
  };
@@ -0,0 +1,125 @@
1
+ /**
2
+ * @license Copyright 2021 The Lighthouse Authors. All Rights Reserved.
3
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
4
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
5
+ */
6
+ 'use strict';
7
+
8
+ /**
9
+ * @fileoverview This class tracks multiple targets (the page itself and its OOPIFs) and allows consumers to
10
+ * listen for protocol events before each target is resumed.
11
+ */
12
+
13
+ const log = require('lighthouse-logger');
14
+
15
+ /** @typedef {{target: LH.Crdp.Target.TargetInfo, session: LH.Gatherer.FRProtocolSession}} TargetWithSession */
16
+
17
+ class TargetManager {
18
+ /** @param {LH.Gatherer.FRProtocolSession} session */
19
+ constructor(session) {
20
+ this._enabled = false;
21
+ this._session = session;
22
+ /** @type {Array<(targetWithSession: TargetWithSession) => Promise<void>|void>} */
23
+ this._listeners = [];
24
+
25
+ this._onSessionAttached = this._onSessionAttached.bind(this);
26
+
27
+ /** @type {Map<string, TargetWithSession>} */
28
+ this._targets = new Map();
29
+
30
+ /** @param {LH.Crdp.Page.FrameNavigatedEvent} event */
31
+ this._onFrameNavigated = async event => {
32
+ // Child frames are handled in `_onSessionAttached`.
33
+ if (event.frame.parentId) return;
34
+
35
+ // It's not entirely clear when this is necessary, but when the page switches processes on
36
+ // navigating from about:blank to the `requestedUrl`, resetting `setAutoAttach` has been
37
+ // necessary in the past.
38
+ await this._session.sendCommand('Target.setAutoAttach', {
39
+ autoAttach: true,
40
+ flatten: true,
41
+ waitForDebuggerOnStart: true,
42
+ });
43
+ };
44
+ }
45
+
46
+ /**
47
+ * @param {LH.Gatherer.FRProtocolSession} session
48
+ */
49
+ async _onSessionAttached(session) {
50
+ try {
51
+ const target = await session.sendCommand('Target.getTargetInfo').catch(() => null);
52
+ const targetType = target && target.targetInfo && target.targetInfo.type;
53
+ const hasValidTargetType = targetType === 'page' || targetType === 'iframe';
54
+ if (!target || !hasValidTargetType) return;
55
+
56
+ const targetId = target.targetInfo.targetId;
57
+ session.setTargetInfo(target.targetInfo);
58
+ if (this._targets.has(targetId)) return;
59
+
60
+ const targetName = target.targetInfo.url || target.targetInfo.targetId;
61
+ log.verbose('target-manager', `target ${targetName} attached`);
62
+
63
+ const targetWithSession = {target: target.targetInfo, session};
64
+ this._targets.set(targetId, targetWithSession);
65
+ for (const listener of this._listeners) await listener(targetWithSession);
66
+
67
+ await session.sendCommand('Target.setAutoAttach', {
68
+ autoAttach: true,
69
+ flatten: true,
70
+ waitForDebuggerOnStart: true,
71
+ });
72
+ } catch (err) {
73
+ // Sometimes targets can be closed before we even have a chance to listen to their network activity.
74
+ if (/Target closed/.test(err.message)) return;
75
+
76
+ throw err;
77
+ } finally {
78
+ // Resume the target if it was paused, but if it's unnecessary, we don't care about the error.
79
+ await session.sendCommand('Runtime.runIfWaitingForDebugger').catch(() => {});
80
+ }
81
+ }
82
+
83
+ /**
84
+ * @return {Promise<void>}
85
+ */
86
+ async enable() {
87
+ if (this._enabled) return;
88
+
89
+ this._enabled = true;
90
+ this._targets = new Map();
91
+
92
+ this._session.on('Page.frameNavigated', this._onFrameNavigated);
93
+ this._session.addSessionAttachedListener(this._onSessionAttached);
94
+
95
+ await this._session.sendCommand('Page.enable');
96
+ await this._onSessionAttached(this._session);
97
+ }
98
+
99
+ /**
100
+ * @return {Promise<void>}
101
+ */
102
+ async disable() {
103
+ this._session.off('Page.frameNavigated', this._onFrameNavigated);
104
+ this._session.removeSessionAttachedListener(this._onSessionAttached);
105
+
106
+ this._enabled = false;
107
+ this._targets = new Map();
108
+ }
109
+
110
+ /**
111
+ * @param {(targetWithSession: TargetWithSession) => Promise<void>|void} listener
112
+ */
113
+ addTargetAttachedListener(listener) {
114
+ this._listeners.push(listener);
115
+ }
116
+
117
+ /**
118
+ * @param {(targetWithSession: TargetWithSession) => Promise<void>|void} listener
119
+ */
120
+ removeTargetAttachedListener(listener) {
121
+ this._listeners = this._listeners.filter(entry => entry !== listener);
122
+ }
123
+ }
124
+
125
+ module.exports = TargetManager;
@@ -127,6 +127,11 @@ class Driver {
127
127
  return this._connection.disconnect();
128
128
  }
129
129
 
130
+ /** @return {Promise<void>} */
131
+ dispose() {
132
+ return this.disconnect();
133
+ }
134
+
130
135
  /**
131
136
  * Get the browser WebSocket endpoint for devtools protocol clients like Puppeteer.
132
137
  * Only works with WebSocket connection, not extension or devtools.
@@ -198,6 +203,21 @@ class Driver {
198
203
  this._connection.off('protocolevent', callback);
199
204
  }
200
205
 
206
+ /** @param {LH.Crdp.Target.TargetInfo} targetInfo */
207
+ setTargetInfo(targetInfo) { // eslint-disable-line no-unused-vars
208
+ // OOPIF handling in legacy driver is implicit.
209
+ }
210
+
211
+ /** @param {(session: LH.Gatherer.FRProtocolSession) => void} callback */
212
+ addSessionAttachedListener(callback) { // eslint-disable-line no-unused-vars
213
+ // OOPIF handling in legacy driver is implicit.
214
+ }
215
+
216
+ /** @param {(session: LH.Gatherer.FRProtocolSession) => void} callback */
217
+ removeSessionAttachedListener(callback) { // eslint-disable-line no-unused-vars
218
+ // OOPIF handling in legacy driver is implicit.
219
+ }
220
+
201
221
  /**
202
222
  * Debounce enabling or disabling domains to prevent driver users from
203
223
  * stomping on each other. Maintains an internal count of the times a domain
@@ -114,7 +114,7 @@ class Fetcher {
114
114
  * @param {{timeout: number}=} options timeout is in ms
115
115
  * @return {Promise<FetchResponse>}
116
116
  */
117
- async fetchResource(url, options = {timeout: 500}) {
117
+ async fetchResource(url, options = {timeout: 2_000}) {
118
118
  if (!this._enabled) {
119
119
  throw new Error('Must call `enable` before using fetchResource');
120
120
  }
@@ -131,7 +131,7 @@ class Fetcher {
131
131
  * @param {{timeout: number}=} options,
132
132
  * @return {Promise<string>}
133
133
  */
134
- async _readIOStream(handle, options = {timeout: 500}) {
134
+ async _readIOStream(handle, options = {timeout: 2_000}) {
135
135
  const startTime = Date.now();
136
136
 
137
137
  let ioResponse;
@@ -155,7 +155,6 @@ class GatherRunner {
155
155
  const session = driver.defaultSession;
156
156
 
157
157
  // Assert no service workers are still installed, so we test that they would actually be installed for a new user.
158
- // TODO(FR-COMPAT): re-evaluate the necessity of this check
159
158
  await GatherRunner.assertNoSameOriginServiceWorkerClients(session, options.requestedUrl);
160
159
 
161
160
  await prepare.prepareTargetForNavigationMode(driver, options.settings);
@@ -407,6 +406,7 @@ class GatherRunner {
407
406
  traces: {},
408
407
  devtoolsLogs: {},
409
408
  settings: options.settings,
409
+ GatherContext: {gatherMode: 'navigation'},
410
410
  URL: {requestedUrl: options.requestedUrl, finalUrl: options.requestedUrl},
411
411
  Timing: [],
412
412
  PageLoadError: null,
@@ -20,7 +20,6 @@ class CSSUsage extends FRGatherer {
20
20
  /** @param {LH.Crdp.CSS.StyleSheetRemovedEvent} sheet */
21
21
  this._onStylesheetRemoved = sheet => {
22
22
  // We can't fetch the content of removed stylesheets, so we ignore them completely.
23
- // TODO(FR-COMPAT): Refactor use of CSSUsage artifact to not *always* rely on the content of stylesheets.
24
23
  const styleSheetId = sheet.styleSheetId;
25
24
  this._stylesheets = this._stylesheets.filter(s => s.header.styleSheetId !== styleSheetId);
26
25
  };
@@ -84,7 +83,6 @@ class CSSUsage extends FRGatherer {
84
83
  const session = context.driver.defaultSession;
85
84
  const executionContext = context.driver.executionContext;
86
85
 
87
- // TODO(FR-COMPAT): Do this only for snapshot once legacy runner is deprecated.
88
86
  if (context.gatherMode !== 'timespan') {
89
87
  await this.startCSSUsageTracking(context);
90
88
 
@@ -11,6 +11,7 @@
11
11
  * This protocol log can be used to recreate the network records using lib/network-recorder.js.
12
12
  */
13
13
 
14
+ const NetworkMonitor = require('../driver/network-monitor.js');
14
15
  const MessageLog = require('../devtools-log.js');
15
16
  const FRGatherer = require('../../fraggle-rock/gather/base-gatherer.js');
16
17
 
@@ -26,6 +27,9 @@ class DevtoolsLog extends FRGatherer {
26
27
  constructor() {
27
28
  super();
28
29
 
30
+ /** @type {NetworkMonitor|undefined} */
31
+ this._networkMonitor = undefined;
32
+
29
33
  this._messageLog = new MessageLog(/^(Page|Network)\./);
30
34
 
31
35
  /** @param {LH.Protocol.RawEventMessage} e */
@@ -38,19 +42,17 @@ class DevtoolsLog extends FRGatherer {
38
42
  async startSensitiveInstrumentation({driver}) {
39
43
  this._messageLog.reset();
40
44
  this._messageLog.beginRecording();
41
- driver.defaultSession.addProtocolMessageListener(this._onProtocolMessage);
42
45
 
43
- // TODO(FR-COMPAT): use a dedicated session for these
44
- await driver.defaultSession.sendCommand('Page.enable');
45
- await driver.defaultSession.sendCommand('Network.enable');
46
+ this._networkMonitor = new NetworkMonitor(driver.defaultSession);
47
+ this._networkMonitor.on('protocolmessage', this._onProtocolMessage);
48
+ this._networkMonitor.enable();
46
49
  }
47
50
 
48
- /**
49
- * @param {LH.Gatherer.FRTransitionalContext} passContext
50
- */
51
- async stopSensitiveInstrumentation({driver}) {
51
+ async stopSensitiveInstrumentation() {
52
+ if (!this._networkMonitor) return;
52
53
  this._messageLog.endRecording();
53
- driver.defaultSession.removeProtocolMessageListener(this._onProtocolMessage);
54
+ this._networkMonitor.disable();
55
+ this._networkMonitor.off('protocolmessage', this._onProtocolMessage);
54
56
  }
55
57
 
56
58
  /**
@@ -134,7 +134,6 @@ class JsUsage extends FRGatherer {
134
134
  usageByUrl[url] = scripts;
135
135
  }
136
136
 
137
- // TODO(FR-COMPAT): Enable this logic for legacy and navigation or make a separate artifact for url/scriptId mappings.
138
137
  if (context.gatherMode !== 'navigation') {
139
138
  this._addMissingScriptIds(usageByUrl);
140
139
  }
@@ -12,7 +12,6 @@ const NetworkRequest = require('../../lib/network-request.js');
12
12
  const pageFunctions = require('../../lib/page-functions.js');
13
13
  const {fetchResponseBodyFromCache} = require('../driver/network.js');
14
14
  const DevtoolsLog = require('./devtools-log.js');
15
- const HostFormFactor = require('./host-form-factor.js');
16
15
 
17
16
  /* global getNodeDetails */
18
17
 
@@ -67,10 +66,10 @@ async function runInSeriesOrParallel(values, promiseMapper, runInSeries) {
67
66
  * @fileoverview Gets JavaScript file contents.
68
67
  */
69
68
  class ScriptElements extends FRGatherer {
70
- /** @type {LH.Gatherer.GathererMeta<'DevtoolsLog'|'HostFormFactor'>} */
69
+ /** @type {LH.Gatherer.GathererMeta<'DevtoolsLog'>} */
71
70
  meta = {
72
71
  supportedModes: ['timespan', 'navigation'],
73
- dependencies: {DevtoolsLog: DevtoolsLog.symbol, HostFormFactor: HostFormFactor.symbol},
72
+ dependencies: {DevtoolsLog: DevtoolsLog.symbol},
74
73
  }
75
74
 
76
75
  /**
@@ -138,11 +137,11 @@ class ScriptElements extends FRGatherer {
138
137
  }
139
138
 
140
139
  /**
141
- * @param {LH.Gatherer.FRTransitionalContext<'DevtoolsLog'|'HostFormFactor'>} context
140
+ * @param {LH.Gatherer.FRTransitionalContext<'DevtoolsLog'>} context
142
141
  */
143
142
  async getArtifact(context) {
144
143
  const devtoolsLog = context.dependencies.DevtoolsLog;
145
- const formFactor = context.dependencies.HostFormFactor;
144
+ const formFactor = context.baseArtifacts.HostFormFactor;
146
145
  const networkRecords = await NetworkRecords.request(devtoolsLog, context);
147
146
  return this._getArtifact(context, networkRecords, formFactor);
148
147
  }
@@ -20,8 +20,8 @@ async function getRobotsTxtContent() {
20
20
 
21
21
  const content = await response.text();
22
22
  return {status: response.status, content};
23
- } catch (_) {
24
- return {status: null, content: null};
23
+ } catch (err) {
24
+ return {status: null, content: null, errorMessage: err.message};
25
25
  }
26
26
  }
27
27
  /* c8 ignore stop */
@@ -49,7 +49,7 @@ class RobotsTxt extends FRGatherer {
49
49
  const robotsUrl = new URL('/robots.txt', passContext.url).href;
50
50
  await passContext.driver.fetcher.enable();
51
51
  return passContext.driver.fetcher.fetchResource(robotsUrl)
52
- .catch(() => ({status: null, content: null}));
52
+ .catch(err => ({status: null, content: null, errorMessage: err.message}));
53
53
  }
54
54
  }
55
55
 
@@ -60,7 +60,6 @@ class Trace extends FRGatherer {
60
60
  // A bug introduced in M92 causes these categories to crash targets on Linux.
61
61
  // See https://github.com/GoogleChrome/lighthouse/issues/12835 for full investigation.
62
62
  // 'disabled-by-default-v8.cpu_profiler',
63
- // 'disabled-by-default-v8.cpu_profiler.hires',
64
63
  ];
65
64
  }
66
65
 
@@ -102,12 +101,12 @@ class Trace extends FRGatherer {
102
101
  /**
103
102
  * @param {LH.Gatherer.FRTransitionalContext} passContext
104
103
  */
105
- async startSensitiveInstrumentation({driver, gatherMode}) {
106
- // TODO(FR-COMPAT): read additional trace categories from overall settings?
107
- // TODO(FR-COMPAT): check if CSS/DOM domains have been enabled in another session and warn?
104
+ async startSensitiveInstrumentation({driver, gatherMode, settings}) {
105
+ const traceCategories = Trace.getDefaultTraceCategories()
106
+ .concat(settings.additionalTraceCategories || []);
108
107
  await driver.defaultSession.sendCommand('Page.enable');
109
108
  await driver.defaultSession.sendCommand('Tracing.start', {
110
- categories: Trace.getDefaultTraceCategories().join(','),
109
+ categories: traceCategories.join(','),
111
110
  options: 'sampling-frequency=10000', // 1000 is default and too slow.
112
111
  });
113
112
 
@@ -70,6 +70,10 @@ lighthouse.getAuditList = Runner.getAuditList;
70
70
  lighthouse.traceCategories = require('./gather/driver.js').traceCategories;
71
71
  lighthouse.Audit = require('./audits/audit.js');
72
72
  lighthouse.Gatherer = require('./gather/gatherers/gatherer.js');
73
+
74
+ // Explicit type reference (hidden by makeComputedArtifact) for d.ts export.
75
+ // TODO(esmodules): should be a workaround for module.export and can be removed when in esm.
76
+ /** @type {typeof import('./computed/network-records.js')} */
73
77
  lighthouse.NetworkRecords = require('./computed/network-records.js');
74
78
 
75
79
  module.exports = lighthouse;
@@ -47,7 +47,6 @@ async function emulate(session, settings) {
47
47
  * @return {Promise<void>}
48
48
  */
49
49
  async function throttle(session, settings) {
50
- // TODO(FR-COMPAT): reconsider if this should be resetting anything
51
50
  if (settings.throttlingMethod !== 'devtools') return clearNetworkThrottling(session);
52
51
 
53
52
  await Promise.all([
@@ -554,6 +554,12 @@
554
554
  "lighthouse-core/audits/byte-efficiency/uses-responsive-images-snapshot.js | columnDisplayedDimensions": {
555
555
  "message": "Displayed dimensions"
556
556
  },
557
+ "lighthouse-core/audits/byte-efficiency/uses-responsive-images-snapshot.js | failureTitle": {
558
+ "message": "Images were larger than their displayed size"
559
+ },
560
+ "lighthouse-core/audits/byte-efficiency/uses-responsive-images-snapshot.js | title": {
561
+ "message": "Images were appropriate for their displayed size"
562
+ },
557
563
  "lighthouse-core/audits/byte-efficiency/uses-responsive-images.js | description": {
558
564
  "message": "Serve images that are appropriately-sized to save cellular data and improve load time. [Learn more](https://web.dev/uses-responsive-images/)."
559
565
  },
@@ -554,6 +554,12 @@
554
554
  "lighthouse-core/audits/byte-efficiency/uses-responsive-images-snapshot.js | columnDisplayedDimensions": {
555
555
  "message": "D̂íŝṕl̂áŷéd̂ d́îḿêńŝíôńŝ"
556
556
  },
557
+ "lighthouse-core/audits/byte-efficiency/uses-responsive-images-snapshot.js | failureTitle": {
558
+ "message": "Îḿâǵêś ŵér̂é l̂ár̂ǵêŕ t̂h́âń t̂h́êír̂ d́îśp̂ĺâýêd́ ŝíẑé"
559
+ },
560
+ "lighthouse-core/audits/byte-efficiency/uses-responsive-images-snapshot.js | title": {
561
+ "message": "Îḿâǵêś ŵér̂é âṕp̂ŕôṕr̂íât́ê f́ôŕ t̂h́êír̂ d́îśp̂ĺâýêd́ ŝíẑé"
562
+ },
557
563
  "lighthouse-core/audits/byte-efficiency/uses-responsive-images.js | description": {
558
564
  "message": "Ŝér̂v́ê ím̂áĝéŝ t́ĥát̂ ár̂é âṕp̂ŕôṕr̂íât́êĺŷ-śîźêd́ t̂ó ŝáv̂é ĉél̂ĺûĺâŕ d̂át̂á âńd̂ ím̂ṕr̂óv̂é l̂óâd́ t̂ím̂é. [L̂éâŕn̂ ḿôŕê](https://web.dev/uses-responsive-images/)."
559
565
  },
@@ -5,6 +5,7 @@
5
5
  */
6
6
  'use strict';
7
7
 
8
+ const log = require('lighthouse-logger');
8
9
  const NetworkRequest = require('./network-request.js');
9
10
  const EventEmitter = require('events').EventEmitter;
10
11
 
@@ -88,6 +89,7 @@ class NetworkRecorder extends EventEmitter {
88
89
  request.onRequestWillBeSent(data);
89
90
  request.sessionId = event.sessionId;
90
91
  this.onRequestStarted(request);
92
+ log.verbose('network', `request will be sent to ${request.url}`);
91
93
  return;
92
94
  }
93
95
 
@@ -108,6 +110,7 @@ class NetworkRecorder extends EventEmitter {
108
110
 
109
111
  redirectedRequest.onRequestWillBeSent(modifiedData);
110
112
  originalRequest.onRedirectResponse(data);
113
+ log.verbose('network', `${originalRequest.url} redirected to ${redirectedRequest.url}`);
111
114
 
112
115
  originalRequest.redirectDestination = redirectedRequest;
113
116
  redirectedRequest.redirectSource = originalRequest;
@@ -124,6 +127,7 @@ class NetworkRecorder extends EventEmitter {
124
127
  const data = event.params;
125
128
  const request = this._findRealRequestAndSetSession(data.requestId, event.sessionId);
126
129
  if (!request) return;
130
+ log.verbose('network', `${request.url} served from cache`);
127
131
  request.onRequestServedFromCache();
128
132
  }
129
133
 
@@ -134,6 +138,7 @@ class NetworkRecorder extends EventEmitter {
134
138
  const data = event.params;
135
139
  const request = this._findRealRequestAndSetSession(data.requestId, event.sessionId);
136
140
  if (!request) return;
141
+ log.verbose('network', `${request.url} response received`);
137
142
  request.onResponseReceived(data);
138
143
  }
139
144
 
@@ -144,6 +149,7 @@ class NetworkRecorder extends EventEmitter {
144
149
  const data = event.params;
145
150
  const request = this._findRealRequestAndSetSession(data.requestId, event.sessionId);
146
151
  if (!request) return;
152
+ log.verbose('network', `${request.url} data received`);
147
153
  request.onDataReceived(data);
148
154
  }
149
155
 
@@ -154,6 +160,7 @@ class NetworkRecorder extends EventEmitter {
154
160
  const data = event.params;
155
161
  const request = this._findRealRequestAndSetSession(data.requestId, event.sessionId);
156
162
  if (!request) return;
163
+ log.verbose('network', `${request.url} loading finished`);
157
164
  request.onLoadingFinished(data);
158
165
  this.onRequestFinished(request);
159
166
  }
@@ -165,6 +172,7 @@ class NetworkRecorder extends EventEmitter {
165
172
  const data = event.params;
166
173
  const request = this._findRealRequestAndSetSession(data.requestId, event.sessionId);
167
174
  if (!request) return;
175
+ log.verbose('network', `${request.url} loading failed`);
168
176
  request.onLoadingFailed(data);
169
177
  this.onRequestFinished(request);
170
178
  }
@@ -16,10 +16,10 @@ const fs = require('fs');
16
16
  */
17
17
 
18
18
  /**
19
- * Transform an LHR into a proto-friendly, mostly-compatible LHR.
20
- * @param {LH.Result} lhr
21
- * @return {LH.Result}
22
- */
19
+ * Transform an LHR into a proto-friendly, mostly-compatible LHR.
20
+ * @param {LH.Result} lhr
21
+ * @return {LH.Result}
22
+ */
23
23
  function processForProto(lhr) {
24
24
  /** @type {LH.Result} */
25
25
  const reportJson = JSON.parse(JSON.stringify(lhr));
@@ -68,13 +68,8 @@ function processForProto(lhr) {
68
68
  });
69
69
  }
70
70
 
71
- // Drop the i18n icuMessagePaths. Painful in proto, and low priority to expose currently.
72
- if (reportJson.i18n && reportJson.i18n.icuMessagePaths) {
73
- delete reportJson.i18n.icuMessagePaths;
74
- }
75
-
76
- // Remove any found empty strings, as they are dropped after round-tripping anyway
77
71
  /**
72
+ * Remove any found empty strings, as they are dropped after round-tripping anyway
78
73
  * @param {any} obj
79
74
  */
80
75
  function removeStrings(obj) {
@@ -132,9 +132,7 @@ class Runner {
132
132
 
133
133
  /** @type {LH.RawIcu<LH.Result>} */
134
134
  const i18nLhr = {
135
- // TODO(FR-COMPAT): Make GatherContext a true base artifact.
136
- // If GatherContext is not collected, then we are in a non-default pass of the legacy runner.
137
- gatherMode: artifacts.GatherContext ? artifacts.GatherContext.gatherMode : 'navigation',
135
+ gatherMode: artifacts.GatherContext.gatherMode,
138
136
  userAgent: artifacts.HostUserAgent,
139
137
  environment: {
140
138
  networkUserAgent: artifacts.NetworkUserAgent,