lighthouse 9.5.0-dev.20230131 → 9.5.0-dev.20230202
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/core/audits/dobetterweb/{password-inputs-can-be-pasted-into.d.ts → paste-preventing-inputs.d.ts} +3 -3
- package/core/audits/dobetterweb/{password-inputs-can-be-pasted-into.js → paste-preventing-inputs.js} +17 -15
- package/core/config/default-config.js +2 -4
- package/core/gather/driver/prepare.d.ts +6 -0
- package/core/gather/driver/prepare.js +26 -12
- package/core/gather/gatherers/bf-cache-failures.js +4 -1
- package/core/gather/gatherers/inputs.js +6 -0
- package/core/gather/navigation-runner.js +10 -0
- package/core/gather/timespan-runner.js +10 -1
- package/core/legacy/config/legacy-default-config.js +0 -1
- package/core/legacy/gather/driver.d.ts +4 -3
- package/core/legacy/gather/driver.js +6 -3
- package/core/legacy/gather/gather-runner.js +1 -1
- package/core/runner.d.ts +1 -6
- package/core/runner.js +9 -20
- package/dist/report/bundle.esm.js +7 -9
- package/dist/report/flow.js +2 -2
- package/dist/report/standalone.js +2 -2
- package/package.json +1 -1
- package/report/renderer/report-ui-features.js +1 -1
- package/report/renderer/report-utils.d.ts +2 -2
- package/report/renderer/report-utils.js +6 -8
- package/report/test/renderer/report-ui-features-test.js +20 -31
- package/shared/localization/locales/ar-XB.json +0 -9
- package/shared/localization/locales/ar.json +0 -9
- package/shared/localization/locales/bg.json +0 -9
- package/shared/localization/locales/ca.json +0 -9
- package/shared/localization/locales/cs.json +0 -9
- package/shared/localization/locales/da.json +0 -9
- package/shared/localization/locales/de.json +0 -9
- package/shared/localization/locales/el.json +0 -9
- package/shared/localization/locales/en-GB.json +0 -9
- package/shared/localization/locales/en-US.json +6 -6
- package/shared/localization/locales/en-XA.json +0 -9
- package/shared/localization/locales/en-XL.json +6 -6
- package/shared/localization/locales/es-419.json +0 -9
- package/shared/localization/locales/es.json +0 -9
- package/shared/localization/locales/fi.json +0 -9
- package/shared/localization/locales/fil.json +0 -9
- package/shared/localization/locales/fr.json +0 -9
- package/shared/localization/locales/he.json +0 -9
- package/shared/localization/locales/hi.json +0 -9
- package/shared/localization/locales/hr.json +0 -9
- package/shared/localization/locales/hu.json +0 -9
- package/shared/localization/locales/id.json +0 -9
- package/shared/localization/locales/it.json +0 -9
- package/shared/localization/locales/ja.json +0 -9
- package/shared/localization/locales/ko.json +0 -9
- package/shared/localization/locales/lt.json +0 -9
- package/shared/localization/locales/lv.json +0 -9
- package/shared/localization/locales/nl.json +0 -9
- package/shared/localization/locales/no.json +0 -9
- package/shared/localization/locales/pl.json +0 -9
- package/shared/localization/locales/pt-PT.json +0 -9
- package/shared/localization/locales/pt.json +0 -9
- package/shared/localization/locales/ro.json +0 -9
- package/shared/localization/locales/ru.json +0 -9
- package/shared/localization/locales/sk.json +0 -9
- package/shared/localization/locales/sl.json +0 -9
- package/shared/localization/locales/sr-Latn.json +0 -9
- package/shared/localization/locales/sr.json +0 -9
- package/shared/localization/locales/sv.json +0 -9
- package/shared/localization/locales/ta.json +0 -9
- package/shared/localization/locales/te.json +0 -9
- package/shared/localization/locales/th.json +0 -9
- package/shared/localization/locales/tr.json +0 -9
- package/shared/localization/locales/uk.json +0 -9
- package/shared/localization/locales/vi.json +0 -9
- package/shared/localization/locales/zh-HK.json +0 -9
- package/shared/localization/locales/zh-TW.json +0 -9
- package/shared/localization/locales/zh.json +0 -9
- package/tsconfig.json +0 -1
- package/types/artifacts.d.ts +2 -5
- package/types/lhr/lhr.d.ts +3 -11
- package/core/gather/gatherers/dobetterweb/password-inputs-with-prevented-paste.d.ts +0 -10
- package/core/gather/gatherers/dobetterweb/password-inputs-with-prevented-paste.js +0 -49
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export default
|
|
2
|
-
declare class
|
|
1
|
+
export default PastePreventingInputsAudit;
|
|
2
|
+
declare class PastePreventingInputsAudit extends Audit {
|
|
3
3
|
/**
|
|
4
4
|
* @param {LH.Artifacts} artifacts
|
|
5
5
|
* @return {LH.Audit.Product}
|
|
@@ -12,4 +12,4 @@ export namespace UIStrings {
|
|
|
12
12
|
const description: string;
|
|
13
13
|
}
|
|
14
14
|
import { Audit } from "../audit.js";
|
|
15
|
-
//# sourceMappingURL=
|
|
15
|
+
//# sourceMappingURL=paste-preventing-inputs.d.ts.map
|
package/core/audits/dobetterweb/{password-inputs-can-be-pasted-into.js → paste-preventing-inputs.js}
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @license Copyright
|
|
2
|
+
* @license Copyright 2023 The Lighthouse Authors. All Rights Reserved.
|
|
3
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
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
5
|
*/
|
|
@@ -8,28 +8,29 @@ import {Audit} from '../audit.js';
|
|
|
8
8
|
import * as i18n from '../../lib/i18n/i18n.js';
|
|
9
9
|
|
|
10
10
|
const UIStrings = {
|
|
11
|
-
/** Title of a Lighthouse audit that provides detail on the ability to paste into
|
|
12
|
-
title: 'Allows users to paste into
|
|
13
|
-
/** Title of a Lighthouse audit that provides detail on the ability to paste into
|
|
14
|
-
failureTitle: 'Prevents users
|
|
15
|
-
/** Description of a Lighthouse audit that tells the user why they should allow pasting of content into
|
|
16
|
-
description: 'Preventing
|
|
17
|
-
'
|
|
11
|
+
/** Title of a Lighthouse audit that provides detail on the ability to paste into input fields. This descriptive title is shown to users when the page allows pasting of content into input fields. */
|
|
12
|
+
title: 'Allows users to paste into input fields',
|
|
13
|
+
/** Title of a Lighthouse audit that provides detail on the ability to paste into input fields. This descriptive title is shown to users when the page does not allow pasting of content into input fields. */
|
|
14
|
+
failureTitle: 'Prevents users from pasting into input fields',
|
|
15
|
+
/** Description of a Lighthouse audit that tells the user why they should allow pasting of content into input fields. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation. */
|
|
16
|
+
description: 'Preventing input pasting is a UX anti-pattern, ' +
|
|
17
|
+
'and undermines good security policy. ' +
|
|
18
|
+
'[Learn more about user-friendly input fields](https://developer.chrome.com/docs/lighthouse/best-practices/paste-preventing-inputs/).',
|
|
18
19
|
};
|
|
19
20
|
|
|
20
21
|
const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
|
|
21
22
|
|
|
22
|
-
class
|
|
23
|
+
class PastePreventingInputsAudit extends Audit {
|
|
23
24
|
/**
|
|
24
25
|
* @return {LH.Audit.Meta}
|
|
25
26
|
*/
|
|
26
27
|
static get meta() {
|
|
27
28
|
return {
|
|
28
|
-
id: '
|
|
29
|
+
id: 'paste-preventing-inputs',
|
|
29
30
|
title: str_(UIStrings.title),
|
|
30
31
|
failureTitle: str_(UIStrings.failureTitle),
|
|
31
32
|
description: str_(UIStrings.description),
|
|
32
|
-
requiredArtifacts: ['
|
|
33
|
+
requiredArtifacts: ['Inputs'],
|
|
33
34
|
};
|
|
34
35
|
}
|
|
35
36
|
|
|
@@ -38,14 +39,15 @@ class PasswordInputsCanBePastedIntoAudit extends Audit {
|
|
|
38
39
|
* @return {LH.Audit.Product}
|
|
39
40
|
*/
|
|
40
41
|
static audit(artifacts) {
|
|
41
|
-
const
|
|
42
|
+
const inputsWithPreventsPaste = artifacts.Inputs.inputs.filter(input => input.preventsPaste);
|
|
42
43
|
|
|
43
44
|
/** @type {LH.Audit.Details.Table['items']} */
|
|
44
45
|
const items = [];
|
|
45
46
|
|
|
46
|
-
|
|
47
|
+
inputsWithPreventsPaste.forEach(input => {
|
|
47
48
|
items.push({
|
|
48
49
|
node: Audit.makeNodeItem(input.node),
|
|
50
|
+
type: input.type,
|
|
49
51
|
});
|
|
50
52
|
});
|
|
51
53
|
|
|
@@ -55,11 +57,11 @@ class PasswordInputsCanBePastedIntoAudit extends Audit {
|
|
|
55
57
|
];
|
|
56
58
|
|
|
57
59
|
return {
|
|
58
|
-
score: Number(
|
|
60
|
+
score: Number(inputsWithPreventsPaste.length === 0),
|
|
59
61
|
details: Audit.makeTableDetails(headings, items),
|
|
60
62
|
};
|
|
61
63
|
}
|
|
62
64
|
}
|
|
63
65
|
|
|
64
|
-
export default
|
|
66
|
+
export default PastePreventingInputsAudit;
|
|
65
67
|
export {UIStrings};
|
|
@@ -150,7 +150,6 @@ const artifacts = {
|
|
|
150
150
|
MetaElements: '',
|
|
151
151
|
NetworkUserAgent: '',
|
|
152
152
|
OptimizedImages: '',
|
|
153
|
-
PasswordInputsWithPreventedPaste: '',
|
|
154
153
|
ResponseCompression: '',
|
|
155
154
|
RobotsTxt: '',
|
|
156
155
|
ServiceWorker: '',
|
|
@@ -200,7 +199,6 @@ const defaultConfig = {
|
|
|
200
199
|
{id: artifacts.MetaElements, gatherer: 'meta-elements'},
|
|
201
200
|
{id: artifacts.NetworkUserAgent, gatherer: 'network-user-agent'},
|
|
202
201
|
{id: artifacts.OptimizedImages, gatherer: 'dobetterweb/optimized-images'},
|
|
203
|
-
{id: artifacts.PasswordInputsWithPreventedPaste, gatherer: 'dobetterweb/password-inputs-with-prevented-paste'},
|
|
204
202
|
{id: artifacts.ResponseCompression, gatherer: 'dobetterweb/response-compression'},
|
|
205
203
|
{id: artifacts.RobotsTxt, gatherer: 'seo/robots-txt'},
|
|
206
204
|
{id: artifacts.ServiceWorker, gatherer: 'service-worker'},
|
|
@@ -361,7 +359,7 @@ const defaultConfig = {
|
|
|
361
359
|
'dobetterweb/no-document-write',
|
|
362
360
|
'dobetterweb/js-libraries',
|
|
363
361
|
'dobetterweb/notification-on-start',
|
|
364
|
-
'dobetterweb/
|
|
362
|
+
'dobetterweb/paste-preventing-inputs',
|
|
365
363
|
'dobetterweb/uses-http2',
|
|
366
364
|
'dobetterweb/uses-passive-event-listeners',
|
|
367
365
|
'seo/meta-description',
|
|
@@ -614,7 +612,7 @@ const defaultConfig = {
|
|
|
614
612
|
{id: 'notification-on-start', weight: 1, group: 'best-practices-trust-safety'},
|
|
615
613
|
{id: 'csp-xss', weight: 0, group: 'best-practices-trust-safety'},
|
|
616
614
|
// User Experience
|
|
617
|
-
{id: '
|
|
615
|
+
{id: 'paste-preventing-inputs', weight: 1, group: 'best-practices-ux'},
|
|
618
616
|
{id: 'image-aspect-ratio', weight: 1, group: 'best-practices-ux'},
|
|
619
617
|
{id: 'image-size-responsive', weight: 1, group: 'best-practices-ux'},
|
|
620
618
|
{id: 'preload-fonts', weight: 1, group: 'best-practices-ux'},
|
|
@@ -43,4 +43,10 @@ export function prepareTargetForIndividualNavigation(session: LH.Gatherer.FRProt
|
|
|
43
43
|
}): Promise<{
|
|
44
44
|
warnings: Array<LH.IcuMessage>;
|
|
45
45
|
}>;
|
|
46
|
+
/**
|
|
47
|
+
* Enables `Debugger` domain to receive async stacktrace information on network request initiators.
|
|
48
|
+
* This is critical for tracking attribution of tasks and performance simulation accuracy.
|
|
49
|
+
* @param {LH.Gatherer.FRProtocolSession} session
|
|
50
|
+
*/
|
|
51
|
+
export function enableAsyncStacks(session: LH.Gatherer.FRProtocolSession): Promise<() => Promise<void>>;
|
|
46
52
|
//# sourceMappingURL=prepare.d.ts.map
|
|
@@ -22,17 +22,33 @@ async function enableAsyncStacks(session) {
|
|
|
22
22
|
await session.sendCommand('Debugger.setAsyncCallStackDepth', {maxDepth: 8});
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
/**
|
|
26
|
+
* Resume any pauses that make it through `setSkipAllPauses`
|
|
27
|
+
*/
|
|
28
|
+
function onDebuggerPaused() {
|
|
29
|
+
session.sendCommand('Debugger.resume');
|
|
30
|
+
}
|
|
27
31
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
32
|
+
/**
|
|
33
|
+
* `Debugger.setSkipAllPauses` is reset after every navigation, so retrigger it on main frame navigations.
|
|
34
|
+
* See https://bugs.chromium.org/p/chromium/issues/detail?id=990945&q=setSkipAllPauses&can=2
|
|
35
|
+
* @param {LH.Crdp.Page.FrameNavigatedEvent} event
|
|
36
|
+
*/
|
|
37
|
+
function onFrameNavigated(event) {
|
|
31
38
|
if (event.frame.parentId) return;
|
|
32
39
|
enable().catch(err => log.error('Driver', err));
|
|
33
|
-
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
session.on('Debugger.paused', onDebuggerPaused);
|
|
43
|
+
session.on('Page.frameNavigated', onFrameNavigated);
|
|
34
44
|
|
|
35
45
|
await enable();
|
|
46
|
+
|
|
47
|
+
return async () => {
|
|
48
|
+
await session.sendCommand('Debugger.disable');
|
|
49
|
+
session.off('Debugger.paused', onDebuggerPaused);
|
|
50
|
+
session.off('Page.frameNavigated', onFrameNavigated);
|
|
51
|
+
};
|
|
36
52
|
}
|
|
37
53
|
|
|
38
54
|
/**
|
|
@@ -128,15 +144,12 @@ async function prepareThrottlingAndNetwork(session, settings, options) {
|
|
|
128
144
|
* @param {LH.Gatherer.FRTransitionalDriver} driver
|
|
129
145
|
* @param {LH.Config.Settings} settings
|
|
130
146
|
*/
|
|
131
|
-
async function
|
|
147
|
+
async function prepareDeviceEmulation(driver, settings) {
|
|
132
148
|
// Enable network domain here so future calls to `emulate()` don't clear cache (https://github.com/GoogleChrome/lighthouse/issues/12631)
|
|
133
149
|
await driver.defaultSession.sendCommand('Network.enable');
|
|
134
150
|
|
|
135
151
|
// Emulate our target device screen and user agent.
|
|
136
152
|
await emulation.emulate(driver.defaultSession, settings);
|
|
137
|
-
|
|
138
|
-
// Enable better stacks on network requests.
|
|
139
|
-
await enableAsyncStacks(driver.defaultSession);
|
|
140
153
|
}
|
|
141
154
|
|
|
142
155
|
/**
|
|
@@ -149,7 +162,7 @@ async function prepareTargetForTimespanMode(driver, settings) {
|
|
|
149
162
|
const status = {msg: 'Preparing target for timespan mode', id: 'lh:prepare:timespanMode'};
|
|
150
163
|
log.time(status);
|
|
151
164
|
|
|
152
|
-
await
|
|
165
|
+
await prepareDeviceEmulation(driver, settings);
|
|
153
166
|
await prepareThrottlingAndNetwork(driver.defaultSession, settings, {
|
|
154
167
|
disableThrottling: false,
|
|
155
168
|
blockedUrlPatterns: undefined,
|
|
@@ -171,7 +184,7 @@ async function prepareTargetForNavigationMode(driver, settings) {
|
|
|
171
184
|
const status = {msg: 'Preparing target for navigation mode', id: 'lh:prepare:navigationMode'};
|
|
172
185
|
log.time(status);
|
|
173
186
|
|
|
174
|
-
await
|
|
187
|
+
await prepareDeviceEmulation(driver, settings);
|
|
175
188
|
|
|
176
189
|
// Automatically handle any JavaScript dialogs to prevent a hung renderer.
|
|
177
190
|
await dismissJavaScriptDialogs(driver.defaultSession);
|
|
@@ -227,4 +240,5 @@ export {
|
|
|
227
240
|
prepareTargetForTimespanMode,
|
|
228
241
|
prepareTargetForNavigationMode,
|
|
229
242
|
prepareTargetForIndividualNavigation,
|
|
243
|
+
enableAsyncStacks,
|
|
230
244
|
};
|
|
@@ -9,6 +9,7 @@ import {waitForFrameNavigated, waitForLoadEvent} from '../driver/wait-for-condit
|
|
|
9
9
|
import DevtoolsLog from './devtools-log.js';
|
|
10
10
|
|
|
11
11
|
const FAILURE_EVENT_TIMEOUT = 100;
|
|
12
|
+
const TEMP_PAGE_PAUSE_TIMEOUT = 100;
|
|
12
13
|
|
|
13
14
|
class BFCacheFailures extends FRGatherer {
|
|
14
15
|
/** @type {LH.Gatherer.GathererMeta<'DevtoolsLog'>} */
|
|
@@ -109,7 +110,9 @@ class BFCacheFailures extends FRGatherer {
|
|
|
109
110
|
// https://github.com/GoogleChrome/lighthouse/issues/14665
|
|
110
111
|
await Promise.all([
|
|
111
112
|
session.sendCommand('Page.navigate', {url: 'chrome://terms'}),
|
|
112
|
-
|
|
113
|
+
// DevTools e2e tests can sometimes fail on the next command if we progress too fast.
|
|
114
|
+
// The only reliable way to prevent this is to wait for an arbitrary period of time after load.
|
|
115
|
+
waitForLoadEvent(session, TEMP_PAGE_PAUSE_TIMEOUT).promise,
|
|
113
116
|
]);
|
|
114
117
|
|
|
115
118
|
const [, frameNavigatedEvent] = await Promise.all([
|
|
@@ -61,6 +61,11 @@ function collectElements() {
|
|
|
61
61
|
return [...labelElToArtifact.keys()].indexOf(labelEl);
|
|
62
62
|
});
|
|
63
63
|
|
|
64
|
+
let preventsPaste;
|
|
65
|
+
if (!inputEl.readOnly) {
|
|
66
|
+
preventsPaste = !inputEl.dispatchEvent(new ClipboardEvent('paste', {cancelable: true}));
|
|
67
|
+
}
|
|
68
|
+
|
|
64
69
|
inputArtifacts.push({
|
|
65
70
|
parentFormIndex,
|
|
66
71
|
labelIndices,
|
|
@@ -74,6 +79,7 @@ function collectElements() {
|
|
|
74
79
|
// Requires `--enable-features=AutofillShowTypePredictions`.
|
|
75
80
|
prediction: inputEl.getAttribute('autofill-prediction'),
|
|
76
81
|
},
|
|
82
|
+
preventsPaste,
|
|
77
83
|
// @ts-expect-error - getNodeDetails put into scope via stringification
|
|
78
84
|
node: getNodeDetails(inputEl),
|
|
79
85
|
});
|
|
@@ -227,6 +227,10 @@ async function _navigation(navigationContext) {
|
|
|
227
227
|
};
|
|
228
228
|
|
|
229
229
|
const setupResult = await _setupNavigation(navigationContext);
|
|
230
|
+
|
|
231
|
+
const disableAsyncStacks =
|
|
232
|
+
await prepare.enableAsyncStacks(navigationContext.driver.defaultSession);
|
|
233
|
+
|
|
230
234
|
await collectPhaseArtifacts({phase: 'startInstrumentation', ...phaseState});
|
|
231
235
|
await collectPhaseArtifacts({phase: 'startSensitiveInstrumentation', ...phaseState});
|
|
232
236
|
const navigateResult = await _navigate(navigationContext);
|
|
@@ -244,6 +248,12 @@ async function _navigation(navigationContext) {
|
|
|
244
248
|
|
|
245
249
|
await collectPhaseArtifacts({phase: 'stopSensitiveInstrumentation', ...phaseState});
|
|
246
250
|
await collectPhaseArtifacts({phase: 'stopInstrumentation', ...phaseState});
|
|
251
|
+
|
|
252
|
+
// bf-cache-failures can emit `Page.frameNavigated` at the end of the run.
|
|
253
|
+
// This can cause us to issue protocol commands after the target closes.
|
|
254
|
+
// We should disable our `Page.frameNavigated` handlers before that.
|
|
255
|
+
await disableAsyncStacks();
|
|
256
|
+
|
|
247
257
|
await _cleanupNavigation(navigationContext);
|
|
248
258
|
|
|
249
259
|
return _computeNavigationResult(navigationContext, phaseState, setupResult, navigateResult);
|
|
@@ -9,7 +9,7 @@ import log from 'lighthouse-logger';
|
|
|
9
9
|
import {Driver} from './driver.js';
|
|
10
10
|
import {Runner} from '../runner.js';
|
|
11
11
|
import {getEmptyArtifactState, collectPhaseArtifacts, awaitArtifacts} from './runner-helpers.js';
|
|
12
|
-
import {prepareTargetForTimespanMode} from './driver/prepare.js';
|
|
12
|
+
import {enableAsyncStacks, prepareTargetForTimespanMode} from './driver/prepare.js';
|
|
13
13
|
import {initializeConfig} from '../config/config.js';
|
|
14
14
|
import {getBaseArtifacts, finalizeArtifacts} from './base-artifacts.js';
|
|
15
15
|
|
|
@@ -44,6 +44,9 @@ async function startTimespanGather(page, options = {}) {
|
|
|
44
44
|
};
|
|
45
45
|
|
|
46
46
|
await prepareTargetForTimespanMode(driver, resolvedConfig.settings);
|
|
47
|
+
|
|
48
|
+
const disableAsyncStacks = await enableAsyncStacks(driver.defaultSession);
|
|
49
|
+
|
|
47
50
|
await collectPhaseArtifacts({phase: 'startInstrumentation', ...phaseOptions});
|
|
48
51
|
await collectPhaseArtifacts({phase: 'startSensitiveInstrumentation', ...phaseOptions});
|
|
49
52
|
|
|
@@ -58,6 +61,12 @@ async function startTimespanGather(page, options = {}) {
|
|
|
58
61
|
|
|
59
62
|
await collectPhaseArtifacts({phase: 'stopSensitiveInstrumentation', ...phaseOptions});
|
|
60
63
|
await collectPhaseArtifacts({phase: 'stopInstrumentation', ...phaseOptions});
|
|
64
|
+
|
|
65
|
+
// bf-cache-failures can emit `Page.frameNavigated` at the end of the run.
|
|
66
|
+
// This can cause us to issue protocol commands after the target closes.
|
|
67
|
+
// We should disable our `Page.frameNavigated` handlers before that.
|
|
68
|
+
await disableAsyncStacks();
|
|
69
|
+
|
|
61
70
|
await collectPhaseArtifacts({phase: 'getArtifact', ...phaseOptions});
|
|
62
71
|
await driver.disconnect();
|
|
63
72
|
|
|
@@ -59,7 +59,6 @@ legacyDefaultConfig.passes = [{
|
|
|
59
59
|
'dobetterweb/doctype',
|
|
60
60
|
'dobetterweb/domstats',
|
|
61
61
|
'dobetterweb/optimized-images',
|
|
62
|
-
'dobetterweb/password-inputs-with-prevented-paste',
|
|
63
62
|
'dobetterweb/response-compression',
|
|
64
63
|
'dobetterweb/tags-blocking-first-paint',
|
|
65
64
|
'seo/font-size',
|
|
@@ -200,12 +200,13 @@ export class Driver implements LH.Gatherer.FRTransitionalDriver {
|
|
|
200
200
|
/**
|
|
201
201
|
* Begin recording devtools protocol messages.
|
|
202
202
|
*/
|
|
203
|
-
beginDevtoolsLog(): void
|
|
203
|
+
beginDevtoolsLog(): Promise<void>;
|
|
204
|
+
_disableAsyncStacks: (() => Promise<void>) | undefined;
|
|
204
205
|
/**
|
|
205
206
|
* Stop recording to devtoolsLog and return log contents.
|
|
206
|
-
* @return {LH.DevtoolsLog}
|
|
207
|
+
* @return {Promise<LH.DevtoolsLog>}
|
|
207
208
|
*/
|
|
208
|
-
endDevtoolsLog(): LH.DevtoolsLog
|
|
209
|
+
endDevtoolsLog(): Promise<LH.DevtoolsLog>;
|
|
209
210
|
url(): Promise<string>;
|
|
210
211
|
}
|
|
211
212
|
import * as LH from "../../../types/lh.js";
|
|
@@ -16,6 +16,7 @@ import {fetchResponseBodyFromCache} from '../../gather/driver/network.js';
|
|
|
16
16
|
import {DevtoolsMessageLog} from '../../gather/gatherers/devtools-log.js';
|
|
17
17
|
import TraceGatherer from '../../gather/gatherers/trace.js';
|
|
18
18
|
import {getBrowserVersion} from '../../gather/driver/environment.js';
|
|
19
|
+
import {enableAsyncStacks} from '../../gather/driver/prepare.js';
|
|
19
20
|
|
|
20
21
|
// Controls how long to wait for a response after sending a DevTools protocol command.
|
|
21
22
|
const DEFAULT_PROTOCOL_TIMEOUT = 30000;
|
|
@@ -443,17 +444,19 @@ class Driver {
|
|
|
443
444
|
/**
|
|
444
445
|
* Begin recording devtools protocol messages.
|
|
445
446
|
*/
|
|
446
|
-
beginDevtoolsLog() {
|
|
447
|
+
async beginDevtoolsLog() {
|
|
448
|
+
this._disableAsyncStacks = await enableAsyncStacks(this);
|
|
447
449
|
this._devtoolsLog.reset();
|
|
448
450
|
this._devtoolsLog.beginRecording();
|
|
449
451
|
}
|
|
450
452
|
|
|
451
453
|
/**
|
|
452
454
|
* Stop recording to devtoolsLog and return log contents.
|
|
453
|
-
* @return {LH.DevtoolsLog}
|
|
455
|
+
* @return {Promise<LH.DevtoolsLog>}
|
|
454
456
|
*/
|
|
455
|
-
endDevtoolsLog() {
|
|
457
|
+
async endDevtoolsLog() {
|
|
456
458
|
this._devtoolsLog.endRecording();
|
|
459
|
+
await this._disableAsyncStacks?.();
|
|
457
460
|
return this._devtoolsLog.messages;
|
|
458
461
|
}
|
|
459
462
|
|
|
@@ -235,7 +235,7 @@ class GatherRunner {
|
|
|
235
235
|
id: `lh:gather:getDevtoolsLog`,
|
|
236
236
|
};
|
|
237
237
|
log.time(status);
|
|
238
|
-
const devtoolsLog = driver.endDevtoolsLog();
|
|
238
|
+
const devtoolsLog = await driver.endDevtoolsLog();
|
|
239
239
|
const networkRecords = await NetworkRecords.request(devtoolsLog, passContext);
|
|
240
240
|
log.timeEnd(status);
|
|
241
241
|
|
package/core/runner.d.ts
CHANGED
|
@@ -20,12 +20,7 @@ export class Runner {
|
|
|
20
20
|
* @param {LH.Artifacts} artifacts
|
|
21
21
|
* @param {LH.Artifacts.ComputedContext} context
|
|
22
22
|
*/
|
|
23
|
-
static getEntityClassification(artifacts: LH.Artifacts, context: LH.Artifacts.ComputedContext): Promise<
|
|
24
|
-
list: import("../types/lhr/lhr.js").default.LhrEntity[];
|
|
25
|
-
firstParty: string | undefined;
|
|
26
|
-
entityIndexByOrigin: Record<string, number>;
|
|
27
|
-
entityIndexByName: Record<string, number>;
|
|
28
|
-
} | undefined>;
|
|
23
|
+
static getEntityClassification(artifacts: LH.Artifacts, context: LH.Artifacts.ComputedContext): Promise<import("../types/lhr/lhr.js").default.LhrEntity[] | undefined>;
|
|
29
24
|
/**
|
|
30
25
|
* User can run -G solo, -A solo, or -GA together
|
|
31
26
|
* -G and -A will run partial lighthouse pipelines,
|
package/core/runner.js
CHANGED
|
@@ -154,37 +154,26 @@ class Runner {
|
|
|
154
154
|
|
|
155
155
|
/** @type {Array<LH.Result.LhrEntity>} */
|
|
156
156
|
const entities = [];
|
|
157
|
-
/** @type {Record<string, number>} */
|
|
158
|
-
const entityIndexByOrigin = {};
|
|
159
|
-
/** @type {Record<string, number>} */
|
|
160
|
-
const entityIndexByName = {};
|
|
161
|
-
|
|
162
157
|
for (const [entity, entityUrls] of classifiedEntities.urlsByEntity) {
|
|
158
|
+
const uniqueOrigins = new Set();
|
|
159
|
+
for (const url of entityUrls) {
|
|
160
|
+
const origin = UrlUtils.getOrigin(url);
|
|
161
|
+
if (origin) uniqueOrigins.add(origin);
|
|
162
|
+
}
|
|
163
|
+
|
|
163
164
|
/** @type {LH.Result.LhrEntity} */
|
|
164
165
|
const shortEntity = {
|
|
165
166
|
name: entity.name,
|
|
166
167
|
homepage: entity.homepage,
|
|
168
|
+
origins: [...uniqueOrigins],
|
|
167
169
|
};
|
|
168
|
-
|
|
169
170
|
// Reduce payload size in LHR JSON by omitting whats falsy.
|
|
170
171
|
if (entity === classifiedEntities.firstParty) shortEntity.isFirstParty = true;
|
|
171
172
|
if (entity.isUnrecognized) shortEntity.isUnrecognized = true;
|
|
172
|
-
|
|
173
|
-
const id = entities.push(shortEntity) - 1;
|
|
174
|
-
for (const url of entityUrls) {
|
|
175
|
-
const origin = UrlUtils.getOrigin(url);
|
|
176
|
-
if (!origin) continue;
|
|
177
|
-
entityIndexByOrigin[origin] = id;
|
|
178
|
-
}
|
|
179
|
-
entityIndexByName[shortEntity.name] = id;
|
|
173
|
+
entities.push(shortEntity);
|
|
180
174
|
}
|
|
181
175
|
|
|
182
|
-
return
|
|
183
|
-
list: entities,
|
|
184
|
-
firstParty: classifiedEntities.firstParty?.name,
|
|
185
|
-
entityIndexByOrigin,
|
|
186
|
-
entityIndexByName,
|
|
187
|
-
};
|
|
176
|
+
return entities;
|
|
188
177
|
}
|
|
189
178
|
|
|
190
179
|
/**
|
|
@@ -1626,11 +1626,11 @@ class ReportUtils {
|
|
|
1626
1626
|
|
|
1627
1627
|
/**
|
|
1628
1628
|
* Mark TableItems/OpportunityItems with entity names.
|
|
1629
|
-
* @param {LH.Result.Entities|undefined}
|
|
1629
|
+
* @param {LH.Result.Entities|undefined} entities
|
|
1630
1630
|
* @param {import('../../types/lhr/audit-result').Result} audit
|
|
1631
1631
|
*/
|
|
1632
|
-
static classifyEntities(
|
|
1633
|
-
if (!
|
|
1632
|
+
static classifyEntities(entities, audit) {
|
|
1633
|
+
if (!entities) return;
|
|
1634
1634
|
if (audit.details?.type !== 'opportunity' && audit.details?.type !== 'table') {
|
|
1635
1635
|
return;
|
|
1636
1636
|
}
|
|
@@ -1647,17 +1647,15 @@ class ReportUtils {
|
|
|
1647
1647
|
const url = urlLocatorFn(item);
|
|
1648
1648
|
if (!url) continue;
|
|
1649
1649
|
|
|
1650
|
-
let origin;
|
|
1650
|
+
let origin = '';
|
|
1651
1651
|
try {
|
|
1652
1652
|
// Non-URLs can appear in valueType: url columns, like 'Unattributable'
|
|
1653
1653
|
origin = Util.parseURL(url).origin;
|
|
1654
1654
|
} catch {}
|
|
1655
1655
|
if (!origin) continue;
|
|
1656
1656
|
|
|
1657
|
-
const
|
|
1658
|
-
if (
|
|
1659
|
-
const entity = entityClassification.list[entityIndex];
|
|
1660
|
-
item.entity = entity.name;
|
|
1657
|
+
const entity = entities.find(e => e.origins.includes(origin));
|
|
1658
|
+
if (entity) item.entity = entity.name;
|
|
1661
1659
|
}
|
|
1662
1660
|
}
|
|
1663
1661
|
|
|
@@ -5832,7 +5830,7 @@ class ReportUIFeatures {
|
|
|
5832
5830
|
*/
|
|
5833
5831
|
_getThirdPartyRows(rowEls, finalDisplayedUrl) {
|
|
5834
5832
|
const finalDisplayedUrlRootDomain = Util.getRootDomain(finalDisplayedUrl);
|
|
5835
|
-
const firstPartyEntityName = this.json.entities?.
|
|
5833
|
+
const firstPartyEntityName = this.json.entities?.find(e => e.isFirstParty === true)?.name;
|
|
5836
5834
|
|
|
5837
5835
|
/** @type {Array<HTMLElement>} */
|
|
5838
5836
|
const thirdPartyRowEls = [];
|