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.
- package/changelog.md +79 -0
- package/flow-report/assets/styles.css +85 -61
- package/flow-report/src/app.tsx +6 -2
- package/flow-report/src/common.tsx +0 -41
- package/flow-report/src/icons.tsx +11 -0
- package/flow-report/src/summary/summary.tsx +32 -25
- package/flow-report/src/topbar.tsx +60 -0
- package/flow-report/src/util.ts +0 -1
- package/flow-report/src/wrappers/{gauge.tsx → category-score.tsx} +11 -5
- package/flow-report/test/summary/summary-test.tsx +3 -3
- package/flow-report/tsconfig.json +6 -22
- package/{lighthouse-core/gather/gatherers/gather-context.js → lighthouse-cli/.eslintrc.cjs} +20 -18
- package/lighthouse-cli/bin.js +31 -21
- package/lighthouse-cli/cli-flags.js +12 -7
- package/lighthouse-cli/commands/commands.js +2 -7
- package/lighthouse-cli/commands/list-audits.js +2 -2
- package/lighthouse-cli/commands/list-trace-categories.js +2 -2
- package/lighthouse-cli/index.js +3 -1
- package/lighthouse-cli/package.json +4 -0
- package/lighthouse-cli/printer.js +4 -3
- package/lighthouse-cli/run.js +14 -15
- package/lighthouse-cli/sentry-prompt.js +5 -6
- package/lighthouse-cli/test/smokehouse/frontends/back-compat-util.js +1 -1
- package/lighthouse-cli/test/smokehouse/frontends/lib.js +5 -4
- package/lighthouse-cli/test/smokehouse/frontends/node.js +1 -1
- package/lighthouse-cli/test/smokehouse/frontends/smokehouse-bin.js +93 -14
- package/lighthouse-cli/test/smokehouse/lib/child-process-error.js +1 -1
- package/lighthouse-cli/test/smokehouse/lib/concurrent-mapper.js +1 -1
- package/lighthouse-cli/test/smokehouse/lib/local-console.js +1 -1
- package/lighthouse-cli/test/smokehouse/lighthouse-runners/bundle.js +20 -16
- package/lighthouse-cli/test/smokehouse/lighthouse-runners/cli.js +15 -12
- package/lighthouse-cli/test/smokehouse/report-assert.js +23 -13
- package/lighthouse-cli/test/smokehouse/smokehouse.js +32 -11
- package/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js +0 -2
- package/lighthouse-core/audits/byte-efficiency/uses-responsive-images-snapshot.js +6 -1
- package/lighthouse-core/audits/dobetterweb/js-libraries.js +5 -0
- package/lighthouse-core/audits/dobetterweb/uses-http2.js +1 -0
- package/lighthouse-core/audits/lcp-lazy-loaded.js +1 -0
- package/lighthouse-core/audits/oopif-iframe-test-audit.js +30 -0
- package/lighthouse-core/computed/metrics/metric.js +2 -1
- package/lighthouse-core/config/config-helpers.js +47 -3
- package/lighthouse-core/config/config.js +3 -46
- package/lighthouse-core/config/default-config.js +0 -1
- package/lighthouse-core/fraggle-rock/config/config.js +22 -4
- package/lighthouse-core/fraggle-rock/config/default-config.js +0 -9
- package/lighthouse-core/fraggle-rock/config/filters.js +3 -0
- package/lighthouse-core/fraggle-rock/config/validation.js +19 -1
- package/lighthouse-core/fraggle-rock/gather/base-artifacts.js +8 -3
- package/lighthouse-core/fraggle-rock/gather/driver.js +10 -0
- package/lighthouse-core/fraggle-rock/gather/navigation-runner.js +40 -19
- package/lighthouse-core/fraggle-rock/gather/runner-helpers.js +3 -0
- package/lighthouse-core/fraggle-rock/gather/session.js +44 -1
- package/lighthouse-core/fraggle-rock/gather/snapshot-runner.js +4 -1
- package/lighthouse-core/fraggle-rock/gather/timespan-runner.js +5 -1
- package/lighthouse-core/gather/devtools-log.js +9 -3
- package/lighthouse-core/gather/driver/network-monitor.js +57 -13
- package/lighthouse-core/gather/driver/prepare.js +43 -17
- package/lighthouse-core/gather/driver/target-manager.js +125 -0
- package/lighthouse-core/gather/driver.js +20 -0
- package/lighthouse-core/gather/fetcher.js +2 -2
- package/lighthouse-core/gather/gather-runner.js +1 -1
- package/lighthouse-core/gather/gatherers/css-usage.js +0 -2
- package/lighthouse-core/gather/gatherers/devtools-log.js +11 -9
- package/lighthouse-core/gather/gatherers/js-usage.js +0 -1
- package/lighthouse-core/gather/gatherers/script-elements.js +4 -5
- package/lighthouse-core/gather/gatherers/seo/robots-txt.js +3 -3
- package/lighthouse-core/gather/gatherers/trace.js +4 -5
- package/lighthouse-core/index.js +4 -0
- package/lighthouse-core/lib/emulation.js +0 -1
- package/lighthouse-core/lib/i18n/locales/en-US.json +6 -0
- package/lighthouse-core/lib/i18n/locales/en-XL.json +6 -0
- package/lighthouse-core/lib/network-recorder.js +8 -0
- package/lighthouse-core/lib/proto-preprocessor.js +5 -10
- package/lighthouse-core/runner.js +1 -3
- package/package.json +12 -9
- package/readme.md +2 -0
- package/report/assets/styles.css +120 -12
- package/report/assets/templates.html +13 -0
- package/report/clients/psi.js +5 -1
- package/report/generator/tsconfig.json +5 -16
- package/report/renderer/category-renderer.js +58 -5
- package/report/renderer/components.js +25 -5
- package/report/renderer/performance-category-renderer.js +5 -5
- package/report/renderer/pwa-category-renderer.js +10 -0
- package/report/renderer/report-renderer.js +12 -3
- package/report/renderer/report-ui-features.js +1 -4
- package/report/test/renderer/category-renderer-test.js +19 -0
- package/report/tsconfig.json +6 -21
- package/report/types/report-result.d.ts +1 -1
- package/root.js +3 -1
- package/third-party/snyk/snapshot.json +2 -2
- package/tsconfig-all.json +15 -0
- package/tsconfig-base.json +24 -0
- package/tsconfig.json +8 -18
- package/types/artifacts.d.ts +7 -7
- package/types/config.d.ts +1 -1
- package/types/gatherer.d.ts +7 -1
- package/types/lhr/flow.d.ts +0 -1
- package/types/lhr/lhr.d.ts +5 -1
- package/types/lhr/tsconfig.json +4 -14
- package/types/protocol.d.ts +2 -0
- package/types/smokehouse.d.ts +2 -1
- package/dist/report/flow.js +0 -149
- package/dist/report/standalone.js +0 -228
- package/flow-report/test/common-test.tsx +0 -131
- package/lighthouse-core/gather/gatherers/host-form-factor.js +0 -31
- 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
|
|
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 {
|
|
97
|
+
* @param {{disableThrottling: boolean, blockedUrlPatterns?: string[]}} options
|
|
98
98
|
*/
|
|
99
|
-
async function
|
|
100
|
-
const status = {msg: 'Preparing network conditions', id: `lh:gather:
|
|
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 (
|
|
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
|
|
108
|
-
// neccessary at the beginning of the next
|
|
109
|
-
const blockedUrls = (
|
|
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
|
|
122
|
-
*
|
|
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
|
|
130
|
-
// Enable network domain here so future calls to `emulate()` don't clear cache (
|
|
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
|
|
196
|
+
await prepareThrottlingAndNetwork(session, settings, navigation);
|
|
172
197
|
|
|
173
198
|
return {warnings};
|
|
174
199
|
}
|
|
175
200
|
|
|
176
201
|
module.exports = {
|
|
177
|
-
|
|
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:
|
|
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:
|
|
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
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
+
this._networkMonitor = new NetworkMonitor(driver.defaultSession);
|
|
47
|
+
this._networkMonitor.on('protocolmessage', this._onProtocolMessage);
|
|
48
|
+
this._networkMonitor.enable();
|
|
46
49
|
}
|
|
47
50
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
*/
|
|
51
|
-
async stopSensitiveInstrumentation({driver}) {
|
|
51
|
+
async stopSensitiveInstrumentation() {
|
|
52
|
+
if (!this._networkMonitor) return;
|
|
52
53
|
this._messageLog.endRecording();
|
|
53
|
-
|
|
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'
|
|
69
|
+
/** @type {LH.Gatherer.GathererMeta<'DevtoolsLog'>} */
|
|
71
70
|
meta = {
|
|
72
71
|
supportedModes: ['timespan', 'navigation'],
|
|
73
|
-
dependencies: {DevtoolsLog: DevtoolsLog.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'
|
|
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.
|
|
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(
|
|
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
|
-
|
|
107
|
-
|
|
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:
|
|
109
|
+
categories: traceCategories.join(','),
|
|
111
110
|
options: 'sampling-frequency=10000', // 1000 is default and too slow.
|
|
112
111
|
});
|
|
113
112
|
|
package/lighthouse-core/index.js
CHANGED
|
@@ -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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
|
|
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,
|