lighthouse 9.5.0-dev.20220326 → 9.5.0-dev.20220329
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/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js +0 -1
- package/lighthouse-core/audits/byte-efficiency/duplicated-javascript.js +1 -1
- package/lighthouse-core/audits/byte-efficiency/legacy-javascript.js +15 -16
- package/lighthouse-core/audits/byte-efficiency/render-blocking-resources.js +1 -1
- package/lighthouse-core/fraggle-rock/gather/base-artifacts.js +8 -1
- package/lighthouse-core/fraggle-rock/gather/navigation-runner.js +32 -22
- package/lighthouse-core/fraggle-rock/gather/snapshot-runner.js +6 -2
- package/lighthouse-core/fraggle-rock/gather/timespan-runner.js +8 -4
- package/lighthouse-core/gather/driver/navigation.js +9 -9
- package/lighthouse-core/gather/driver/network-monitor.js +16 -3
- package/lighthouse-core/gather/driver.js +5 -0
- package/lighthouse-core/gather/gather-runner.js +13 -7
- package/package.json +1 -1
- package/types/artifacts.d.ts +24 -2
- package/types/gatherer.d.ts +1 -0
|
@@ -158,7 +158,6 @@ class UnusedBytes extends Audit {
|
|
|
158
158
|
|
|
159
159
|
const simulationBeforeChanges = simulator.simulate(graph, {label: beforeLabel});
|
|
160
160
|
|
|
161
|
-
// TODO: change this to wastedBytesByScriptId
|
|
162
161
|
const wastedBytesByUrl = options.providedWastedBytesByUrl || new Map();
|
|
163
162
|
if (!options.providedWastedBytesByUrl) {
|
|
164
163
|
for (const {url, wastedBytes} of results) {
|
|
@@ -164,7 +164,7 @@ class DuplicatedJavascript extends ByteEfficiencyAudit {
|
|
|
164
164
|
let transferRatio = transferRatioByUrl.get(url);
|
|
165
165
|
if (transferRatio === undefined) {
|
|
166
166
|
if (!script || script.length === undefined) {
|
|
167
|
-
// This should never happen because we found the wasted bytes from bundles, which required contents in a
|
|
167
|
+
// This should never happen because we found the wasted bytes from bundles, which required contents in a Script.
|
|
168
168
|
continue;
|
|
169
169
|
}
|
|
170
170
|
|
|
@@ -277,21 +277,21 @@ class LegacyJavascript extends ByteEfficiencyAudit {
|
|
|
277
277
|
* @param {LH.Artifacts['Scripts']} scripts
|
|
278
278
|
* @param {LH.Artifacts.NetworkRequest[]} networkRecords
|
|
279
279
|
* @param {LH.Artifacts.Bundle[]} bundles
|
|
280
|
-
* @return {Map<
|
|
280
|
+
* @return {Map<LH.Artifacts.Script, PatternMatchResult[]>}
|
|
281
281
|
*/
|
|
282
282
|
static detectAcrossScripts(matcher, scripts, networkRecords, bundles) {
|
|
283
|
-
/** @type {Map<
|
|
284
|
-
const
|
|
283
|
+
/** @type {Map<LH.Artifacts.Script, PatternMatchResult[]>} */
|
|
284
|
+
const scriptToMatchResults = new Map();
|
|
285
285
|
const polyfillData = this.getPolyfillData();
|
|
286
286
|
|
|
287
|
-
for (const
|
|
288
|
-
if (!content) continue;
|
|
287
|
+
for (const script of Object.values(scripts)) {
|
|
288
|
+
if (!script.content) continue;
|
|
289
289
|
|
|
290
290
|
// Start with pattern matching against the downloaded script.
|
|
291
|
-
const matches = matcher.match(content);
|
|
291
|
+
const matches = matcher.match(script.content);
|
|
292
292
|
|
|
293
293
|
// If it's a bundle with source maps, add in the polyfill modules by name too.
|
|
294
|
-
const bundle = bundles.find(b => b.script.scriptId === scriptId);
|
|
294
|
+
const bundle = bundles.find(b => b.script.scriptId === script.scriptId);
|
|
295
295
|
if (bundle) {
|
|
296
296
|
for (const {coreJs2Module, coreJs3Module, name} of polyfillData) {
|
|
297
297
|
// Skip if the pattern matching found a match for this polyfill.
|
|
@@ -311,11 +311,10 @@ class LegacyJavascript extends ByteEfficiencyAudit {
|
|
|
311
311
|
}
|
|
312
312
|
|
|
313
313
|
if (!matches.length) continue;
|
|
314
|
-
|
|
315
|
-
urlToMatchResults.set(url, matches);
|
|
314
|
+
scriptToMatchResults.set(script, matches);
|
|
316
315
|
}
|
|
317
316
|
|
|
318
|
-
return
|
|
317
|
+
return scriptToMatchResults;
|
|
319
318
|
}
|
|
320
319
|
|
|
321
320
|
/**
|
|
@@ -410,15 +409,15 @@ class LegacyJavascript extends ByteEfficiencyAudit {
|
|
|
410
409
|
/** @type {Map<string, number>} */
|
|
411
410
|
const transferRatioByUrl = new Map();
|
|
412
411
|
|
|
413
|
-
const
|
|
412
|
+
const scriptToMatchResults =
|
|
414
413
|
this.detectAcrossScripts(matcher, artifacts.Scripts, networkRecords, bundles);
|
|
415
|
-
for (const [
|
|
414
|
+
for (const [script, matches] of scriptToMatchResults.entries()) {
|
|
416
415
|
const transferRatio = await this.estimateTransferRatioForScript(
|
|
417
|
-
transferRatioByUrl, url, artifacts, networkRecords);
|
|
416
|
+
transferRatioByUrl, script.url, artifacts, networkRecords);
|
|
418
417
|
const wastedBytes = Math.round(this.estimateWastedBytes(matches) * transferRatio);
|
|
419
418
|
/** @type {typeof items[number]} */
|
|
420
419
|
const item = {
|
|
421
|
-
url,
|
|
420
|
+
url: script.url,
|
|
422
421
|
wastedBytes,
|
|
423
422
|
subItems: {
|
|
424
423
|
type: 'subitems',
|
|
@@ -428,13 +427,13 @@ class LegacyJavascript extends ByteEfficiencyAudit {
|
|
|
428
427
|
totalBytes: 0,
|
|
429
428
|
};
|
|
430
429
|
|
|
431
|
-
const bundle = bundles.find(bundle => bundle.script.
|
|
430
|
+
const bundle = bundles.find(bundle => bundle.script.scriptId === script.scriptId);
|
|
432
431
|
for (const match of matches) {
|
|
433
432
|
const {name, line, column} = match;
|
|
434
433
|
/** @type {SubItem} */
|
|
435
434
|
const subItem = {
|
|
436
435
|
signal: name,
|
|
437
|
-
location: ByteEfficiencyAudit.makeSourceLocation(url, line, column, bundle),
|
|
436
|
+
location: ByteEfficiencyAudit.makeSourceLocation(script.url, line, column, bundle),
|
|
438
437
|
};
|
|
439
438
|
item.subItems.items.push(subItem);
|
|
440
439
|
}
|
|
@@ -35,7 +35,12 @@ async function getBaseArtifacts(config, driver, context) {
|
|
|
35
35
|
HostFormFactor: userAgent.includes('Android') || userAgent.includes('Mobile') ?
|
|
36
36
|
'mobile' : 'desktop',
|
|
37
37
|
// Contextual artifacts whose collection changes based on gather mode.
|
|
38
|
-
|
|
38
|
+
// TODO: Make `requestedUrl` optional in timespan and snapshot modes.
|
|
39
|
+
URL: {
|
|
40
|
+
initialUrl: '',
|
|
41
|
+
requestedUrl: '',
|
|
42
|
+
finalUrl: '',
|
|
43
|
+
},
|
|
39
44
|
PageLoadError: null,
|
|
40
45
|
GatherContext: context,
|
|
41
46
|
// Artifacts that have been replaced by regular gatherers in Fraggle Rock.
|
|
@@ -87,6 +92,8 @@ function finalizeArtifacts(baseArtifacts, gathererArtifacts) {
|
|
|
87
92
|
}
|
|
88
93
|
|
|
89
94
|
// Check that the runner remembered to mutate the special-case URL artifact.
|
|
95
|
+
// TODO: Make `requestedUrl` optional.
|
|
96
|
+
if (!artifacts.URL.initialUrl) throw new Error('Runner did not set initialUrl');
|
|
90
97
|
if (!artifacts.URL.requestedUrl) throw new Error('Runner did not set requestedUrl');
|
|
91
98
|
if (!artifacts.URL.finalUrl) throw new Error('Runner did not set finalUrl');
|
|
92
99
|
|
|
@@ -44,17 +44,16 @@ const NetworkRecords = require('../../computed/network-records.js');
|
|
|
44
44
|
/** @typedef {Omit<Parameters<typeof collectPhaseArtifacts>[0], 'phase'>} PhaseState */
|
|
45
45
|
|
|
46
46
|
/**
|
|
47
|
-
* @param {{driver: Driver, config: LH.Config.FRConfig,
|
|
47
|
+
* @param {{driver: Driver, config: LH.Config.FRConfig, options?: InternalOptions}} args
|
|
48
48
|
* @return {Promise<{baseArtifacts: LH.FRBaseArtifacts}>}
|
|
49
49
|
*/
|
|
50
|
-
async function _setup({driver, config,
|
|
50
|
+
async function _setup({driver, config, options}) {
|
|
51
51
|
await driver.connect();
|
|
52
52
|
if (!options?.skipAboutBlank) {
|
|
53
53
|
await gotoURL(driver, defaultNavigationConfig.blankPage, {waitUntil: ['navigated']});
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
const baseArtifacts = await getBaseArtifacts(config, driver, {gatherMode: 'navigation'});
|
|
57
|
-
if (typeof requestor === 'string') baseArtifacts.URL.requestedUrl = requestor;
|
|
58
57
|
|
|
59
58
|
await prepare.prepareTargetForNavigationMode(driver, config.settings);
|
|
60
59
|
|
|
@@ -90,25 +89,32 @@ async function _cleanupNavigation({driver}) {
|
|
|
90
89
|
|
|
91
90
|
/**
|
|
92
91
|
* @param {NavigationContext} navigationContext
|
|
93
|
-
* @return {Promise<{requestedUrl: string,
|
|
92
|
+
* @return {Promise<{requestedUrl: string, mainDocumentUrl: string, navigationError: LH.LighthouseError | undefined, warnings: Array<LH.IcuMessage>}>}
|
|
94
93
|
*/
|
|
95
94
|
async function _navigate(navigationContext) {
|
|
96
95
|
const {driver, config, requestor} = navigationContext;
|
|
97
96
|
|
|
98
97
|
try {
|
|
99
|
-
const {requestedUrl,
|
|
98
|
+
const {requestedUrl, mainDocumentUrl, warnings} = await gotoURL(driver, requestor, {
|
|
100
99
|
...navigationContext.navigation,
|
|
101
100
|
debugNavigation: config.settings.debugNavigation,
|
|
102
101
|
maxWaitForFcp: config.settings.maxWaitForFcp,
|
|
103
102
|
maxWaitForLoad: config.settings.maxWaitForLoad,
|
|
104
103
|
waitUntil: navigationContext.navigation.pauseAfterFcpMs ? ['fcp', 'load'] : ['load'],
|
|
105
104
|
});
|
|
106
|
-
return {requestedUrl,
|
|
105
|
+
return {requestedUrl, mainDocumentUrl, navigationError: undefined, warnings};
|
|
107
106
|
} catch (err) {
|
|
108
107
|
if (!(err instanceof LighthouseError)) throw err;
|
|
109
108
|
if (err.code !== 'NO_FCP' && err.code !== 'PAGE_HUNG') throw err;
|
|
110
109
|
if (typeof requestor !== 'string') throw err;
|
|
111
|
-
|
|
110
|
+
|
|
111
|
+
// TODO: Make the urls optional here so we don't need to throw an error with a callback requestor.
|
|
112
|
+
return {
|
|
113
|
+
requestedUrl: requestor,
|
|
114
|
+
mainDocumentUrl: requestor,
|
|
115
|
+
navigationError: err,
|
|
116
|
+
warnings: [],
|
|
117
|
+
};
|
|
112
118
|
}
|
|
113
119
|
}
|
|
114
120
|
|
|
@@ -152,7 +158,7 @@ async function _collectDebugData(navigationContext, phaseState) {
|
|
|
152
158
|
* @param {PhaseState} phaseState
|
|
153
159
|
* @param {Awaited<ReturnType<typeof _setupNavigation>>} setupResult
|
|
154
160
|
* @param {Awaited<ReturnType<typeof _navigate>>} navigateResult
|
|
155
|
-
* @return {Promise<{
|
|
161
|
+
* @return {Promise<{artifacts: Partial<LH.GathererArtifacts>, warnings: Array<LH.IcuMessage>, pageLoadError: LH.LighthouseError | undefined}>}
|
|
156
162
|
*/
|
|
157
163
|
async function _computeNavigationResult(
|
|
158
164
|
navigationContext,
|
|
@@ -160,12 +166,12 @@ async function _computeNavigationResult(
|
|
|
160
166
|
setupResult,
|
|
161
167
|
navigateResult
|
|
162
168
|
) {
|
|
163
|
-
const {navigationError,
|
|
169
|
+
const {navigationError, mainDocumentUrl} = navigateResult;
|
|
164
170
|
const warnings = [...setupResult.warnings, ...navigateResult.warnings];
|
|
165
171
|
const debugData = await _collectDebugData(navigationContext, phaseState);
|
|
166
172
|
const pageLoadError = debugData.records
|
|
167
173
|
? getPageLoadError(navigationError, {
|
|
168
|
-
url:
|
|
174
|
+
url: mainDocumentUrl,
|
|
169
175
|
loadFailureMode: navigationContext.navigation.loadFailureMode,
|
|
170
176
|
networkRecords: debugData.records,
|
|
171
177
|
})
|
|
@@ -183,8 +189,6 @@ async function _computeNavigationResult(
|
|
|
183
189
|
if (debugData.trace) artifacts.traces = {[pageLoadErrorId]: debugData.trace};
|
|
184
190
|
|
|
185
191
|
return {
|
|
186
|
-
requestedUrl: navigateResult.requestedUrl,
|
|
187
|
-
finalUrl,
|
|
188
192
|
pageLoadError,
|
|
189
193
|
artifacts,
|
|
190
194
|
warnings: [...warnings, pageLoadError.friendlyMessage],
|
|
@@ -194,8 +198,6 @@ async function _computeNavigationResult(
|
|
|
194
198
|
|
|
195
199
|
const artifacts = await awaitArtifacts(phaseState.artifactState);
|
|
196
200
|
return {
|
|
197
|
-
requestedUrl: navigateResult.requestedUrl,
|
|
198
|
-
finalUrl,
|
|
199
201
|
artifacts,
|
|
200
202
|
warnings,
|
|
201
203
|
pageLoadError: undefined,
|
|
@@ -209,8 +211,9 @@ async function _computeNavigationResult(
|
|
|
209
211
|
*/
|
|
210
212
|
async function _navigation(navigationContext) {
|
|
211
213
|
const artifactState = getEmptyArtifactState();
|
|
214
|
+
const initialUrl = await navigationContext.driver.url();
|
|
212
215
|
const phaseState = {
|
|
213
|
-
url:
|
|
216
|
+
url: initialUrl,
|
|
214
217
|
gatherMode: /** @type {const} */ ('navigation'),
|
|
215
218
|
driver: navigationContext.driver,
|
|
216
219
|
computedCache: navigationContext.computedCache,
|
|
@@ -224,8 +227,20 @@ async function _navigation(navigationContext) {
|
|
|
224
227
|
await collectPhaseArtifacts({phase: 'startInstrumentation', ...phaseState});
|
|
225
228
|
await collectPhaseArtifacts({phase: 'startSensitiveInstrumentation', ...phaseState});
|
|
226
229
|
const navigateResult = await _navigate(navigationContext);
|
|
227
|
-
|
|
228
|
-
|
|
230
|
+
|
|
231
|
+
// Every required url is initialized to an empty string in `getBaseArtifacts`.
|
|
232
|
+
// If we haven't set the required urls yet, set them here.
|
|
233
|
+
const {URL} = phaseState.baseArtifacts;
|
|
234
|
+
if (!URL.finalUrl || !URL.initialUrl) {
|
|
235
|
+
phaseState.baseArtifacts.URL = {
|
|
236
|
+
initialUrl,
|
|
237
|
+
requestedUrl: navigateResult.requestedUrl,
|
|
238
|
+
mainDocumentUrl: navigateResult.mainDocumentUrl,
|
|
239
|
+
finalUrl: navigateResult.mainDocumentUrl,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
phaseState.url = navigateResult.mainDocumentUrl;
|
|
243
|
+
|
|
229
244
|
await collectPhaseArtifacts({phase: 'stopSensitiveInstrumentation', ...phaseState});
|
|
230
245
|
await collectPhaseArtifacts({phase: 'stopInstrumentation', ...phaseState});
|
|
231
246
|
await _cleanupNavigation(navigationContext);
|
|
@@ -263,11 +278,6 @@ async function _navigations({driver, config, requestor, baseArtifacts, computedC
|
|
|
263
278
|
artifacts.PageLoadError = navigationResult.pageLoadError;
|
|
264
279
|
shouldHaltNavigations = true;
|
|
265
280
|
}
|
|
266
|
-
|
|
267
|
-
artifacts.URL = {
|
|
268
|
-
requestedUrl: navigationResult.requestedUrl,
|
|
269
|
-
finalUrl: navigationResult.finalUrl,
|
|
270
|
-
};
|
|
271
281
|
}
|
|
272
282
|
|
|
273
283
|
LighthouseRunWarnings.push(...navigationResult.warnings);
|
|
@@ -36,8 +36,12 @@ async function snapshotGather(options) {
|
|
|
36
36
|
const artifacts = await Runner.gather(
|
|
37
37
|
async () => {
|
|
38
38
|
const baseArtifacts = await getBaseArtifacts(config, driver, {gatherMode: 'snapshot'});
|
|
39
|
-
baseArtifacts.URL
|
|
40
|
-
|
|
39
|
+
baseArtifacts.URL = {
|
|
40
|
+
initialUrl: url,
|
|
41
|
+
// TODO: Remove `requestedUrl` from snapshot mode.
|
|
42
|
+
requestedUrl: url,
|
|
43
|
+
finalUrl: url,
|
|
44
|
+
};
|
|
41
45
|
|
|
42
46
|
const artifactDefinitions = config.artifacts || [];
|
|
43
47
|
const artifactState = getEmptyArtifactState();
|
|
@@ -32,12 +32,12 @@ async function startTimespanGather(options) {
|
|
|
32
32
|
/** @type {Map<string, LH.ArbitraryEqualityMap>} */
|
|
33
33
|
const computedCache = new Map();
|
|
34
34
|
const artifactDefinitions = config.artifacts || [];
|
|
35
|
-
const
|
|
35
|
+
const initialUrl = await driver.url();
|
|
36
36
|
const baseArtifacts = await getBaseArtifacts(config, driver, {gatherMode: 'timespan'});
|
|
37
37
|
const artifactState = getEmptyArtifactState();
|
|
38
38
|
/** @type {Omit<import('./runner-helpers.js').CollectPhaseArtifactOptions, 'phase'>} */
|
|
39
39
|
const phaseOptions = {
|
|
40
|
-
url:
|
|
40
|
+
url: initialUrl,
|
|
41
41
|
driver,
|
|
42
42
|
artifactDefinitions,
|
|
43
43
|
artifactState,
|
|
@@ -59,8 +59,12 @@ async function startTimespanGather(options) {
|
|
|
59
59
|
const runnerOptions = {config, computedCache};
|
|
60
60
|
const artifacts = await Runner.gather(
|
|
61
61
|
async () => {
|
|
62
|
-
baseArtifacts.URL
|
|
63
|
-
|
|
62
|
+
baseArtifacts.URL = {
|
|
63
|
+
initialUrl,
|
|
64
|
+
// TODO: Remove requestedUrl from timespan mode
|
|
65
|
+
requestedUrl: initialUrl,
|
|
66
|
+
finalUrl,
|
|
67
|
+
};
|
|
64
68
|
|
|
65
69
|
await collectPhaseArtifacts({phase: 'stopSensitiveInstrumentation', ...phaseOptions});
|
|
66
70
|
await collectPhaseArtifacts({phase: 'stopInstrumentation', ...phaseOptions});
|
|
@@ -80,7 +80,7 @@ function resolveWaitForFullyLoadedOptions(options) {
|
|
|
80
80
|
* @param {LH.Gatherer.FRTransitionalDriver} driver
|
|
81
81
|
* @param {LH.NavigationRequestor} requestor
|
|
82
82
|
* @param {NavigationOptions} options
|
|
83
|
-
* @return {Promise<{requestedUrl: string,
|
|
83
|
+
* @return {Promise<{requestedUrl: string, mainDocumentUrl: string, warnings: Array<LH.IcuMessage>}>}
|
|
84
84
|
*/
|
|
85
85
|
async function gotoURL(driver, requestor, options) {
|
|
86
86
|
const status = typeof requestor === 'string' ?
|
|
@@ -130,14 +130,14 @@ async function gotoURL(driver, requestor, options) {
|
|
|
130
130
|
|
|
131
131
|
let requestedUrl = navigationUrls.requestedUrl;
|
|
132
132
|
if (typeof requestor === 'string') {
|
|
133
|
-
if (requestor
|
|
133
|
+
if (requestedUrl && !URL.equalWithExcludedFragments(requestor, requestedUrl)) {
|
|
134
134
|
log.error('Navigation', 'Provided URL did not match initial navigation URL');
|
|
135
135
|
}
|
|
136
136
|
requestedUrl = requestor;
|
|
137
137
|
}
|
|
138
138
|
if (!requestedUrl) throw Error('No navigations detected when running user defined requestor.');
|
|
139
139
|
|
|
140
|
-
const
|
|
140
|
+
const mainDocumentUrl = navigationUrls.mainDocumentUrl || requestedUrl;
|
|
141
141
|
|
|
142
142
|
// Bring `Page.navigate` errors back into the promise chain. See https://github.com/GoogleChrome/lighthouse/pull/6739.
|
|
143
143
|
await waitForNavigationTriggered;
|
|
@@ -150,26 +150,26 @@ async function gotoURL(driver, requestor, options) {
|
|
|
150
150
|
log.timeEnd(status);
|
|
151
151
|
return {
|
|
152
152
|
requestedUrl,
|
|
153
|
-
|
|
154
|
-
warnings: getNavigationWarnings({timedOut,
|
|
153
|
+
mainDocumentUrl,
|
|
154
|
+
warnings: getNavigationWarnings({timedOut, mainDocumentUrl, requestedUrl}),
|
|
155
155
|
};
|
|
156
156
|
}
|
|
157
157
|
|
|
158
158
|
/**
|
|
159
|
-
* @param {{timedOut: boolean, requestedUrl: string,
|
|
159
|
+
* @param {{timedOut: boolean, requestedUrl: string, mainDocumentUrl: string; }} navigation
|
|
160
160
|
* @return {Array<LH.IcuMessage>}
|
|
161
161
|
*/
|
|
162
162
|
function getNavigationWarnings(navigation) {
|
|
163
|
-
const {requestedUrl,
|
|
163
|
+
const {requestedUrl, mainDocumentUrl} = navigation;
|
|
164
164
|
/** @type {Array<LH.IcuMessage>} */
|
|
165
165
|
const warnings = [];
|
|
166
166
|
|
|
167
167
|
if (navigation.timedOut) warnings.push(str_(UIStrings.warningTimeout));
|
|
168
168
|
|
|
169
|
-
if (!URL.equalWithExcludedFragments(requestedUrl,
|
|
169
|
+
if (!URL.equalWithExcludedFragments(requestedUrl, mainDocumentUrl)) {
|
|
170
170
|
warnings.push(str_(UIStrings.warningRedirected, {
|
|
171
171
|
requested: requestedUrl,
|
|
172
|
-
final:
|
|
172
|
+
final: mainDocumentUrl,
|
|
173
173
|
}));
|
|
174
174
|
}
|
|
175
175
|
|
|
@@ -133,7 +133,7 @@ class NetworkMonitor {
|
|
|
133
133
|
this._sessions = new Map();
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
-
/** @return {Promise<{requestedUrl?: string,
|
|
136
|
+
/** @return {Promise<{requestedUrl?: string, mainDocumentUrl?: string}>} */
|
|
137
137
|
async getNavigationUrls() {
|
|
138
138
|
const frameNavigations = this._frameNavigations;
|
|
139
139
|
if (!frameNavigations.length) return {};
|
|
@@ -143,9 +143,22 @@ class NetworkMonitor {
|
|
|
143
143
|
const mainFrameNavigations = frameNavigations.filter(frame => frame.id === mainFrameId);
|
|
144
144
|
if (!mainFrameNavigations.length) log.warn('NetworkMonitor', 'No detected navigations');
|
|
145
145
|
|
|
146
|
+
// The requested URL is the initiator request for the first frame navigation.
|
|
147
|
+
/** @type {string|undefined} */
|
|
148
|
+
let requestedUrl = mainFrameNavigations[0]?.url;
|
|
149
|
+
if (this._networkRecorder) {
|
|
150
|
+
const records = this._networkRecorder.getRawRecords();
|
|
151
|
+
|
|
152
|
+
let initialUrlRequest = records.find(record => record.url === requestedUrl);
|
|
153
|
+
while (initialUrlRequest?.redirectSource) {
|
|
154
|
+
initialUrlRequest = initialUrlRequest.redirectSource;
|
|
155
|
+
requestedUrl = initialUrlRequest.url;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
146
159
|
return {
|
|
147
|
-
requestedUrl
|
|
148
|
-
|
|
160
|
+
requestedUrl,
|
|
161
|
+
mainDocumentUrl: mainFrameNavigations[mainFrameNavigations.length - 1]?.url,
|
|
149
162
|
};
|
|
150
163
|
}
|
|
151
164
|
|
|
@@ -470,6 +470,11 @@ class Driver {
|
|
|
470
470
|
this._devtoolsLog.endRecording();
|
|
471
471
|
return this._devtoolsLog.messages;
|
|
472
472
|
}
|
|
473
|
+
|
|
474
|
+
async url() {
|
|
475
|
+
const {frameTree} = await this.sendCommand('Page.getFrameTree');
|
|
476
|
+
return `${frameTree.frame.url}${frameTree.frame.urlFragment || ''}`;
|
|
477
|
+
}
|
|
473
478
|
}
|
|
474
479
|
|
|
475
480
|
module.exports = Driver;
|
|
@@ -71,7 +71,7 @@ class GatherRunner {
|
|
|
71
71
|
log.time(status);
|
|
72
72
|
try {
|
|
73
73
|
const requestedUrl = passContext.url;
|
|
74
|
-
const {
|
|
74
|
+
const {mainDocumentUrl, warnings} = await navigation.gotoURL(driver, requestedUrl, {
|
|
75
75
|
waitUntil: passContext.passConfig.recordTrace ?
|
|
76
76
|
['load', 'fcp'] : ['load'],
|
|
77
77
|
debugNavigation: passContext.settings.debugNavigation,
|
|
@@ -79,8 +79,12 @@ class GatherRunner {
|
|
|
79
79
|
maxWaitForLoad: passContext.settings.maxWaitForLoad,
|
|
80
80
|
...passContext.passConfig,
|
|
81
81
|
});
|
|
82
|
-
passContext.url =
|
|
83
|
-
|
|
82
|
+
passContext.url = mainDocumentUrl;
|
|
83
|
+
const {URL} = passContext.baseArtifacts;
|
|
84
|
+
if (!URL.finalUrl || !URL.mainDocumentUrl) {
|
|
85
|
+
URL.finalUrl = mainDocumentUrl;
|
|
86
|
+
URL.mainDocumentUrl = mainDocumentUrl;
|
|
87
|
+
}
|
|
84
88
|
if (passContext.passConfig.loadFailureMode === 'fatal') {
|
|
85
89
|
passContext.LighthouseRunWarnings.push(...warnings);
|
|
86
90
|
}
|
|
@@ -408,7 +412,12 @@ class GatherRunner {
|
|
|
408
412
|
devtoolsLogs: {},
|
|
409
413
|
settings: options.settings,
|
|
410
414
|
GatherContext: {gatherMode: 'navigation'},
|
|
411
|
-
URL: {
|
|
415
|
+
URL: {
|
|
416
|
+
initialUrl: await options.driver.url(),
|
|
417
|
+
requestedUrl: options.requestedUrl,
|
|
418
|
+
mainDocumentUrl: '',
|
|
419
|
+
finalUrl: '',
|
|
420
|
+
},
|
|
412
421
|
Timing: [],
|
|
413
422
|
PageLoadError: null,
|
|
414
423
|
};
|
|
@@ -426,9 +435,6 @@ class GatherRunner {
|
|
|
426
435
|
|
|
427
436
|
const baseArtifacts = passContext.baseArtifacts;
|
|
428
437
|
|
|
429
|
-
// Copy redirected URL to artifact.
|
|
430
|
-
baseArtifacts.URL.finalUrl = passContext.url;
|
|
431
|
-
|
|
432
438
|
// Fetch the manifest, if it exists.
|
|
433
439
|
try {
|
|
434
440
|
baseArtifacts.WebAppManifest = await WebAppManifest.getWebAppManifest(
|
package/package.json
CHANGED
package/types/artifacts.d.ts
CHANGED
|
@@ -66,7 +66,7 @@ interface UniversalBaseArtifacts {
|
|
|
66
66
|
*/
|
|
67
67
|
interface ContextualBaseArtifacts {
|
|
68
68
|
/** The URL initially requested and the post-redirects URL that was actually loaded. */
|
|
69
|
-
URL:
|
|
69
|
+
URL: Artifacts.URL;
|
|
70
70
|
/** If loading the page failed, value is the error that caused it. Otherwise null. */
|
|
71
71
|
PageLoadError: LighthouseError | null;
|
|
72
72
|
}
|
|
@@ -187,6 +187,28 @@ declare module Artifacts {
|
|
|
187
187
|
type TaskNode = _TaskNode;
|
|
188
188
|
type MetaElement = Artifacts['MetaElements'][0];
|
|
189
189
|
|
|
190
|
+
interface URL {
|
|
191
|
+
/** URL of the main frame before Lighthouse starts. */
|
|
192
|
+
initialUrl: string;
|
|
193
|
+
/**
|
|
194
|
+
* URL of the first document request during a Lighthouse navigation.
|
|
195
|
+
* Will be the same as `initialUrl` in timespan/snapshot.
|
|
196
|
+
* TODO: Make this property `undefined` in timespan/snapshot.
|
|
197
|
+
*/
|
|
198
|
+
requestedUrl: string;
|
|
199
|
+
/**
|
|
200
|
+
* URL of the last document request during a Lighthouse navigation.
|
|
201
|
+
* Will be `undefined` in timespan/snapshot.
|
|
202
|
+
*/
|
|
203
|
+
mainDocumentUrl?: string;
|
|
204
|
+
/**
|
|
205
|
+
* Will be the same as `mainDocumentUrl` in navigation mode.
|
|
206
|
+
* Wil be the URL of the main frame after Lighthouse finishes in timespan/snapshot.
|
|
207
|
+
* TODO: Use the main frame URL in navigation mode as well.
|
|
208
|
+
*/
|
|
209
|
+
finalUrl: string;
|
|
210
|
+
}
|
|
211
|
+
|
|
190
212
|
interface NodeDetails {
|
|
191
213
|
lhId: string,
|
|
192
214
|
devtoolsNodePath: string,
|
|
@@ -299,7 +321,7 @@ declare module Artifacts {
|
|
|
299
321
|
url: string;
|
|
300
322
|
content?: string;
|
|
301
323
|
}
|
|
302
|
-
|
|
324
|
+
|
|
303
325
|
interface ScriptElement {
|
|
304
326
|
type: string | null
|
|
305
327
|
src: string | null
|
package/types/gatherer.d.ts
CHANGED