lighthouse 9.5.0-dev.20220509 → 9.6.1
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 +70 -0
- package/dist/report/bundle.esm.js +400 -808
- package/dist/report/flow.js +22 -27
- package/dist/report/standalone.js +18 -23
- package/flow-report/src/summary/category.tsx +1 -1
- package/jest.config.js +1 -1
- package/lighthouse-cli/.eslintrc.cjs +4 -3
- package/lighthouse-cli/bin.js +1 -0
- package/lighthouse-cli/cli-flags.js +4 -3
- package/lighthouse-cli/commands/commands.js +1 -0
- package/lighthouse-cli/commands/list-audits.js +1 -0
- package/lighthouse-cli/commands/list-locales.js +1 -0
- package/lighthouse-cli/commands/list-trace-categories.js +1 -0
- package/lighthouse-cli/index.js +1 -0
- package/lighthouse-cli/printer.js +1 -0
- package/lighthouse-cli/run.js +24 -9
- package/lighthouse-cli/sentry-prompt.js +1 -0
- package/lighthouse-cli/test/smokehouse/core-tests.js +1 -0
- package/lighthouse-cli/test/smokehouse/frontends/back-compat-util.js +1 -0
- package/lighthouse-cli/test/smokehouse/frontends/lib.js +1 -0
- package/lighthouse-cli/test/smokehouse/frontends/node.js +1 -0
- package/lighthouse-cli/test/smokehouse/frontends/smokehouse-bin.js +1 -0
- package/lighthouse-cli/test/smokehouse/lib/child-process-error.js +1 -0
- package/lighthouse-cli/test/smokehouse/lib/concurrent-mapper.js +1 -0
- package/lighthouse-cli/test/smokehouse/lib/local-console.js +1 -0
- package/lighthouse-cli/test/smokehouse/lighthouse-runners/bundle.js +23 -109
- package/lighthouse-cli/test/smokehouse/lighthouse-runners/cli.js +3 -2
- package/lighthouse-cli/test/smokehouse/lighthouse-runners/devtools.js +19 -28
- package/lighthouse-cli/test/smokehouse/report-assert.js +4 -3
- package/lighthouse-cli/test/smokehouse/smokehouse.js +1 -1
- package/lighthouse-cli/test/smokehouse/version-check-test.js +3 -0
- package/lighthouse-cli/test/smokehouse/version-check.js +1 -0
- package/lighthouse-core/audits/accessibility/accesskeys.js +1 -1
- package/lighthouse-core/audits/accessibility/aria-allowed-attr.js +1 -1
- package/lighthouse-core/audits/accessibility/aria-command-name.js +1 -1
- package/lighthouse-core/audits/accessibility/aria-hidden-body.js +1 -1
- package/lighthouse-core/audits/accessibility/aria-hidden-focus.js +1 -1
- package/lighthouse-core/audits/accessibility/aria-input-field-name.js +1 -1
- package/lighthouse-core/audits/accessibility/aria-meter-name.js +1 -1
- package/lighthouse-core/audits/accessibility/aria-progressbar-name.js +1 -1
- package/lighthouse-core/audits/accessibility/aria-required-attr.js +1 -1
- package/lighthouse-core/audits/accessibility/aria-required-children.js +1 -1
- package/lighthouse-core/audits/accessibility/aria-required-parent.js +1 -1
- package/lighthouse-core/audits/accessibility/aria-roles.js +1 -1
- package/lighthouse-core/audits/accessibility/aria-toggle-field-name.js +1 -1
- package/lighthouse-core/audits/accessibility/aria-tooltip-name.js +1 -1
- package/lighthouse-core/audits/accessibility/aria-treeitem-name.js +1 -1
- package/lighthouse-core/audits/accessibility/aria-valid-attr-value.js +1 -1
- package/lighthouse-core/audits/accessibility/aria-valid-attr.js +1 -1
- package/lighthouse-core/audits/accessibility/button-name.js +1 -1
- package/lighthouse-core/audits/accessibility/bypass.js +1 -1
- package/lighthouse-core/audits/accessibility/color-contrast.js +1 -1
- package/lighthouse-core/audits/accessibility/definition-list.js +1 -1
- package/lighthouse-core/audits/accessibility/dlitem.js +1 -1
- package/lighthouse-core/audits/accessibility/document-title.js +1 -1
- package/lighthouse-core/audits/accessibility/duplicate-id-active.js +1 -1
- package/lighthouse-core/audits/accessibility/duplicate-id-aria.js +1 -1
- package/lighthouse-core/audits/accessibility/form-field-multiple-labels.js +1 -1
- package/lighthouse-core/audits/accessibility/frame-title.js +1 -1
- package/lighthouse-core/audits/accessibility/heading-order.js +1 -1
- package/lighthouse-core/audits/accessibility/html-has-lang.js +1 -1
- package/lighthouse-core/audits/accessibility/html-lang-valid.js +1 -1
- package/lighthouse-core/audits/accessibility/image-alt.js +1 -1
- package/lighthouse-core/audits/accessibility/input-image-alt.js +1 -1
- package/lighthouse-core/audits/accessibility/label.js +1 -1
- package/lighthouse-core/audits/accessibility/link-name.js +1 -1
- package/lighthouse-core/audits/accessibility/list.js +1 -1
- package/lighthouse-core/audits/accessibility/listitem.js +5 -5
- package/lighthouse-core/audits/accessibility/meta-refresh.js +1 -1
- package/lighthouse-core/audits/accessibility/meta-viewport.js +1 -1
- package/lighthouse-core/audits/accessibility/object-alt.js +1 -1
- package/lighthouse-core/audits/accessibility/tabindex.js +1 -1
- package/lighthouse-core/audits/accessibility/td-headers-attr.js +1 -1
- package/lighthouse-core/audits/accessibility/th-has-data-cells.js +1 -1
- package/lighthouse-core/audits/accessibility/valid-lang.js +1 -1
- package/lighthouse-core/audits/accessibility/video-caption.js +1 -1
- package/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js +1 -2
- package/lighthouse-core/audits/byte-efficiency/duplicated-javascript.js +12 -11
- package/lighthouse-core/audits/byte-efficiency/efficient-animated-content.js +1 -1
- package/lighthouse-core/audits/byte-efficiency/legacy-javascript.js +24 -22
- package/lighthouse-core/audits/byte-efficiency/offscreen-images.js +2 -3
- package/lighthouse-core/audits/byte-efficiency/render-blocking-resources.js +3 -2
- package/lighthouse-core/audits/byte-efficiency/unminified-css.js +5 -3
- package/lighthouse-core/audits/byte-efficiency/unminified-javascript.js +11 -10
- package/lighthouse-core/audits/byte-efficiency/unused-css-rules.js +1 -0
- package/lighthouse-core/audits/byte-efficiency/unused-javascript.js +8 -11
- package/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js +1 -1
- package/lighthouse-core/audits/byte-efficiency/uses-text-compression.js +1 -1
- package/lighthouse-core/audits/deprecations.js +598 -8
- package/lighthouse-core/audits/diagnostics.js +3 -4
- package/lighthouse-core/audits/dobetterweb/doctype.js +9 -20
- package/lighthouse-core/audits/dobetterweb/geolocation-on-start.js +1 -1
- package/lighthouse-core/audits/dobetterweb/no-document-write.js +1 -1
- package/lighthouse-core/audits/dobetterweb/notification-on-start.js +1 -1
- package/lighthouse-core/audits/dobetterweb/uses-http2.js +1 -2
- package/lighthouse-core/audits/dobetterweb/uses-passive-event-listeners.js +1 -1
- package/lighthouse-core/audits/errors-in-console.js +2 -2
- package/lighthouse-core/audits/long-tasks.js +2 -3
- package/lighthouse-core/audits/metrics/experimental-interaction-to-next-paint.js +3 -1
- package/lighthouse-core/audits/metrics/first-contentful-paint-3g.js +2 -2
- package/lighthouse-core/audits/metrics/first-contentful-paint.js +2 -3
- package/lighthouse-core/audits/metrics/first-meaningful-paint.js +2 -3
- package/lighthouse-core/audits/metrics/interactive.js +2 -3
- package/lighthouse-core/audits/metrics/largest-contentful-paint.js +2 -3
- package/lighthouse-core/audits/metrics/max-potential-fid.js +2 -3
- package/lighthouse-core/audits/metrics/speed-index.js +2 -3
- package/lighthouse-core/audits/metrics/total-blocking-time.js +2 -3
- package/lighthouse-core/audits/metrics.js +2 -3
- package/lighthouse-core/audits/no-unload-listeners.js +15 -7
- package/lighthouse-core/audits/predictive-perf.js +1 -2
- package/lighthouse-core/audits/preload-lcp-image.js +3 -3
- package/lighthouse-core/audits/redirects.js +1 -1
- package/lighthouse-core/audits/script-treemap-data.js +45 -45
- package/lighthouse-core/audits/seo/http-status-code.js +4 -2
- package/lighthouse-core/audits/seo/link-text.js +0 -2
- package/lighthouse-core/audits/server-response-time.js +17 -2
- package/lighthouse-core/audits/service-worker.js +2 -7
- package/lighthouse-core/audits/timing-budget.js +2 -3
- package/lighthouse-core/audits/uses-rel-preconnect.js +1 -1
- package/lighthouse-core/audits/uses-rel-preload.js +1 -1
- package/lighthouse-core/audits/valid-source-maps.js +14 -12
- package/lighthouse-core/audits/violation-audit.js +1 -1
- package/lighthouse-core/audits/work-during-interaction.js +280 -0
- package/lighthouse-core/computed/critical-request-chains.js +9 -5
- package/lighthouse-core/computed/js-bundles.js +11 -11
- package/lighthouse-core/computed/main-resource.js +2 -3
- package/lighthouse-core/computed/metrics/first-contentful-paint-all-frames.js +1 -1
- package/lighthouse-core/computed/metrics/first-contentful-paint.js +1 -1
- package/lighthouse-core/computed/metrics/first-meaningful-paint.js +1 -1
- package/lighthouse-core/computed/metrics/interactive.js +1 -1
- package/lighthouse-core/computed/metrics/lantern-first-contentful-paint.js +1 -1
- package/lighthouse-core/computed/metrics/lantern-first-meaningful-paint.js +1 -1
- package/lighthouse-core/computed/metrics/lantern-interactive.js +1 -1
- package/lighthouse-core/computed/metrics/lantern-largest-contentful-paint.js +1 -1
- package/lighthouse-core/computed/metrics/lantern-max-potential-fid.js +1 -1
- package/lighthouse-core/computed/metrics/lantern-metric.js +5 -3
- package/lighthouse-core/computed/metrics/lantern-speed-index.js +1 -1
- package/lighthouse-core/computed/metrics/lantern-total-blocking-time.js +1 -1
- package/lighthouse-core/computed/metrics/largest-contentful-paint-all-frames.js +1 -1
- package/lighthouse-core/computed/metrics/largest-contentful-paint.js +1 -1
- package/lighthouse-core/computed/metrics/max-potential-fid.js +1 -1
- package/lighthouse-core/computed/metrics/metric.js +0 -1
- package/lighthouse-core/computed/metrics/responsiveness.js +12 -9
- package/lighthouse-core/computed/metrics/speed-index.js +1 -1
- package/lighthouse-core/computed/metrics/timing-summary.js +6 -13
- package/lighthouse-core/computed/metrics/total-blocking-time.js +1 -1
- package/lighthouse-core/computed/module-duplication.js +5 -6
- package/lighthouse-core/computed/page-dependency-graph.js +28 -55
- package/lighthouse-core/computed/resource-summary.js +5 -5
- package/lighthouse-core/computed/unused-css.js +7 -6
- package/lighthouse-core/computed/unused-javascript-summary.js +36 -16
- package/lighthouse-core/config/budget.js +2 -2
- package/lighthouse-core/config/default-config.js +1 -2
- package/lighthouse-core/config/metrics-to-audits.js +5 -0
- package/lighthouse-core/fraggle-rock/api.js +1 -1
- package/lighthouse-core/fraggle-rock/config/default-config.js +8 -5
- package/lighthouse-core/fraggle-rock/gather/base-artifacts.js +3 -6
- package/lighthouse-core/fraggle-rock/gather/driver.js +2 -2
- package/lighthouse-core/fraggle-rock/gather/navigation-runner.js +26 -51
- package/lighthouse-core/fraggle-rock/gather/runner-helpers.js +3 -0
- package/lighthouse-core/fraggle-rock/gather/session.js +2 -2
- package/lighthouse-core/fraggle-rock/gather/snapshot-runner.js +4 -5
- package/lighthouse-core/fraggle-rock/gather/timespan-runner.js +6 -6
- package/lighthouse-core/gather/connections/cri.js +0 -1
- package/lighthouse-core/gather/driver/execution-context.js +2 -4
- package/lighthouse-core/gather/driver/navigation.js +9 -9
- package/lighthouse-core/gather/driver/network-monitor.js +3 -16
- package/lighthouse-core/gather/driver.js +1 -6
- package/lighthouse-core/gather/fetcher.js +210 -34
- package/lighthouse-core/gather/gather-runner.js +17 -13
- package/lighthouse-core/gather/gatherers/accessibility.js +4 -16
- package/lighthouse-core/gather/gatherers/console-messages.js +2 -4
- package/lighthouse-core/gather/gatherers/dobetterweb/doctype.js +2 -4
- package/lighthouse-core/gather/gatherers/full-page-screenshot.js +2 -3
- package/lighthouse-core/gather/gatherers/js-usage.js +87 -12
- package/lighthouse-core/gather/gatherers/link-elements.js +14 -12
- package/lighthouse-core/gather/gatherers/main-document-content.js +8 -8
- package/lighthouse-core/gather/gatherers/script-elements.js +56 -7
- package/lighthouse-core/gather/gatherers/seo/robots-txt.js +30 -2
- package/lighthouse-core/gather/gatherers/source-maps.js +1 -3
- package/lighthouse-core/gather/gatherers/trace-elements.js +21 -2
- package/lighthouse-core/gather/gatherers/web-app-manifest.js +2 -2
- package/lighthouse-core/index.js +2 -26
- package/lighthouse-core/lib/cdt/generated/SourceMap.js +1 -1
- package/lighthouse-core/lib/dependency-graph/simulator/network-analyzer.js +29 -8
- package/lighthouse-core/lib/navigation-error.js +4 -13
- package/lighthouse-core/lib/script-helpers.js +6 -11
- package/lighthouse-core/lib/url-shim.js +2 -2
- package/lighthouse-core/runner.js +5 -0
- package/lighthouse-core/scripts/manual-chrome-launcher.js +1 -0
- package/lighthouse-core/util-commonjs.js +24 -28
- package/package.json +12 -12
- package/readme.md +1 -1
- package/report/.eslintrc.cjs +1 -3
- package/report/assets/styles.css +10 -12
- package/report/assets/templates.html +9 -10
- package/report/clients/bundle.js +1 -0
- package/report/clients/standalone.js +1 -0
- package/report/generator/report-generator.js +21 -46
- package/report/renderer/api.js +1 -0
- package/report/renderer/category-renderer.js +1 -0
- package/report/renderer/components.js +304 -303
- package/report/renderer/crc-details-renderer.js +1 -0
- package/report/renderer/details-renderer.js +10 -10
- package/report/renderer/dom.js +1 -0
- package/report/renderer/drop-down-menu.js +1 -0
- package/report/renderer/element-screenshot-renderer.js +4 -0
- package/report/renderer/features-util.js +1 -0
- package/report/renderer/i18n.js +62 -118
- package/report/renderer/logger.js +1 -0
- package/report/renderer/open-tab.js +3 -1
- package/report/renderer/performance-category-renderer.js +1 -0
- package/report/renderer/pwa-category-renderer.js +1 -0
- package/report/renderer/report-renderer.js +1 -0
- package/report/renderer/report-ui-features.js +1 -0
- package/report/renderer/snippet-renderer.js +1 -0
- package/report/renderer/swap-locale-feature.js +1 -0
- package/report/renderer/text-encoding.js +1 -0
- package/report/renderer/topbar-features.js +1 -0
- package/report/renderer/util.js +24 -27
- package/report/test/clients/bundle-test.js +4 -0
- package/report/test/generator/file-namer-test.js +1 -0
- package/report/test/generator/report-generator-test.js +20 -25
- package/report/test/renderer/category-renderer-test.js +3 -0
- package/report/test/renderer/components-test.js +3 -0
- package/report/test/renderer/crc-details-renderer-test.js +3 -0
- package/report/test/renderer/details-renderer-test.js +12 -58
- package/report/test/renderer/dom-test.js +3 -0
- package/report/test/renderer/element-screenshot-renderer-test.js +6 -3
- package/report/test/renderer/i18n-test.js +25 -70
- package/report/test/renderer/performance-category-renderer-test.js +4 -11
- package/report/test/renderer/pwa-category-renderer-test.js +3 -0
- package/report/test/renderer/report-renderer-axe-test.js +3 -0
- package/report/test/renderer/report-renderer-test.js +3 -0
- package/report/test/renderer/report-ui-features-test.js +3 -0
- package/report/test/renderer/snippet-renderer-test.js +3 -0
- package/report/test/renderer/text-encoding-test.js +3 -0
- package/report/test/renderer/util-test.js +5 -2
- package/report/test-assets/faux-psi.js +1 -2
- package/shared/localization/locales/en-US.json +260 -53
- package/shared/localization/locales/en-XL.json +260 -53
- package/shared/test/localization/format-test.js +2 -0
- package/shared/test/localization/locales-test.js +2 -0
- package/shared/test/localization/swap-locale-test.js +2 -0
- package/third-party/chromium-synchronization/inspector-issueAdded-types-test.js +2 -0
- package/third-party/chromium-synchronization/installability-errors-test.js +2 -0
- package/tsconfig.json +0 -1
- package/types/artifacts.d.ts +9 -48
- package/types/config.d.ts +0 -2
- package/types/externs.d.ts +2 -2
- package/types/gatherer.d.ts +2 -1
- package/types/global-lh.d.ts +0 -3
- package/types/lhr/audit-details.d.ts +3 -1
- package/types/lhr/lhr.d.ts +2 -2
- package/types/lhr/treemap.d.ts +1 -0
- package/lighthouse-core/audits/script-elements-test-audit.js +0 -30
- package/lighthouse-core/gather/gatherers/scripts.js +0 -154
- package/report/test/.eslintrc.cjs +0 -11
- package/shared/test/localization/.eslintrc.cjs +0 -11
|
@@ -6,55 +6,124 @@
|
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
* @fileoverview Fetcher is a utility for making requests
|
|
10
|
-
*
|
|
9
|
+
* @fileoverview Fetcher is a utility for making requests within the context of the page.
|
|
10
|
+
* Requests can circumvent CORS, and so are good for fetching source maps that may be hosted
|
|
11
|
+
* on a different origin.
|
|
11
12
|
*/
|
|
12
13
|
|
|
13
|
-
/* global
|
|
14
|
+
/* global document */
|
|
14
15
|
|
|
15
16
|
/** @typedef {{content: string|null, status: number|null}} FetchResponse */
|
|
16
17
|
|
|
18
|
+
const log = require('lighthouse-logger');
|
|
19
|
+
const {getBrowserVersion} = require('./driver/environment.js');
|
|
20
|
+
|
|
17
21
|
class Fetcher {
|
|
18
22
|
/**
|
|
19
23
|
* @param {LH.Gatherer.FRProtocolSession} session
|
|
24
|
+
* @param {import('./driver/execution-context.js')} executionContext
|
|
20
25
|
*/
|
|
21
|
-
constructor(session) {
|
|
26
|
+
constructor(session, executionContext) {
|
|
22
27
|
this.session = session;
|
|
28
|
+
this.executionContext = executionContext;
|
|
29
|
+
/** @type {Map<string, (event: LH.Crdp.Fetch.RequestPausedEvent) => void>} */
|
|
30
|
+
this._onRequestPausedHandlers = new Map();
|
|
31
|
+
this._onRequestPaused = this._onRequestPaused.bind(this);
|
|
32
|
+
this._enabled = false;
|
|
23
33
|
}
|
|
24
34
|
|
|
25
35
|
/**
|
|
26
|
-
*
|
|
36
|
+
* Chrome M92 and above:
|
|
37
|
+
* We use `Network.loadNetworkResource` to fetch each resource.
|
|
38
|
+
*
|
|
39
|
+
* Chrome <M92:
|
|
40
|
+
* The Fetch domain accepts patterns for controlling what requests are intercepted, but we
|
|
41
|
+
* enable the domain for all patterns and filter events at a lower level to support multiple
|
|
42
|
+
* concurrent usages. Reasons for this:
|
|
43
|
+
*
|
|
44
|
+
* 1) only one set of patterns may be applied for the entire domain.
|
|
45
|
+
* 2) every request that matches the patterns are paused and only resumes when certain Fetch
|
|
46
|
+
* commands are sent. So a listener of the `Fetch.requestPaused` event must either handle
|
|
47
|
+
* the requests it cares about, or explicitly allow them to continue.
|
|
48
|
+
* 3) if multiple commands to continue the same request are sent, protocol errors occur.
|
|
27
49
|
*
|
|
50
|
+
* So instead we have one global `Fetch.enable` / `Fetch.requestPaused` pair, and allow specific
|
|
51
|
+
* urls to be intercepted via `fetcher._setOnRequestPausedHandler`.
|
|
52
|
+
*/
|
|
53
|
+
async enable() {
|
|
54
|
+
if (this._enabled) return;
|
|
55
|
+
|
|
56
|
+
this._enabled = true;
|
|
57
|
+
await this.session.sendCommand('Fetch.enable', {
|
|
58
|
+
patterns: [{requestStage: 'Request'}, {requestStage: 'Response'}],
|
|
59
|
+
});
|
|
60
|
+
await this.session.on('Fetch.requestPaused', this._onRequestPaused);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async disable() {
|
|
64
|
+
if (!this._enabled) return;
|
|
65
|
+
|
|
66
|
+
this._enabled = false;
|
|
67
|
+
await this.session.off('Fetch.requestPaused', this._onRequestPaused);
|
|
68
|
+
await this.session.sendCommand('Fetch.disable');
|
|
69
|
+
this._onRequestPausedHandlers.clear();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
28
73
|
* @param {string} url
|
|
29
|
-
* @param {
|
|
30
|
-
* @return {Promise<FetchResponse>}
|
|
74
|
+
* @param {(event: LH.Crdp.Fetch.RequestPausedEvent) => void} handler
|
|
31
75
|
*/
|
|
32
|
-
async
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
76
|
+
async _setOnRequestPausedHandler(url, handler) {
|
|
77
|
+
this._onRequestPausedHandlers.set(url, handler);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* @param {LH.Crdp.Fetch.RequestPausedEvent} event
|
|
82
|
+
*/
|
|
83
|
+
_onRequestPaused(event) {
|
|
84
|
+
const handler = this._onRequestPausedHandlers.get(event.request.url);
|
|
85
|
+
if (handler) {
|
|
86
|
+
handler(event);
|
|
87
|
+
} else {
|
|
88
|
+
// Nothing cares about this URL, so continue.
|
|
89
|
+
this.session.sendCommand('Fetch.continueRequest', {requestId: event.requestId}).catch(err => {
|
|
90
|
+
log.error('Fetcher', `Failed to continueRequest: ${err.message}`);
|
|
91
|
+
});
|
|
37
92
|
}
|
|
93
|
+
}
|
|
38
94
|
|
|
39
|
-
|
|
95
|
+
/**
|
|
96
|
+
* `Network.loadNetworkResource` was introduced in M88.
|
|
97
|
+
* The long timeout bug with `IO.read` was fixed in M92:
|
|
98
|
+
* https://bugs.chromium.org/p/chromium/issues/detail?id=1191757
|
|
99
|
+
* Lightrider has a bug forcing us to use the old version for now:
|
|
100
|
+
* https://docs.google.com/document/d/1V-DxgsOFMPxUuFrdGPQpyiCqSljvgNlOqXCtqDtd0b8/edit?usp=sharing&resourcekey=0-aIaIqcHFKG-0dX4MAudBEw
|
|
101
|
+
* @return {Promise<boolean>}
|
|
102
|
+
*/
|
|
103
|
+
async shouldUseLegacyFetcher() {
|
|
104
|
+
const {milestone} = await getBrowserVersion(this.session);
|
|
105
|
+
return milestone < 92 || Boolean(global.isLightrider);
|
|
40
106
|
}
|
|
41
107
|
|
|
42
108
|
/**
|
|
109
|
+
* Requires that `fetcher.enable` has been called.
|
|
110
|
+
*
|
|
111
|
+
* Fetches any resource in a way that circumvents CORS.
|
|
112
|
+
*
|
|
43
113
|
* @param {string} url
|
|
114
|
+
* @param {{timeout: number}=} options timeout is in ms
|
|
44
115
|
* @return {Promise<FetchResponse>}
|
|
45
116
|
*/
|
|
46
|
-
async
|
|
47
|
-
|
|
117
|
+
async fetchResource(url, options = {timeout: 2_000}) {
|
|
118
|
+
if (!this._enabled) {
|
|
119
|
+
throw new Error('Must call `enable` before using fetchResource');
|
|
120
|
+
}
|
|
48
121
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
} catch {}
|
|
122
|
+
if (await this.shouldUseLegacyFetcher()) {
|
|
123
|
+
return this._fetchResourceIframe(url, options);
|
|
124
|
+
}
|
|
53
125
|
|
|
54
|
-
return
|
|
55
|
-
content,
|
|
56
|
-
status: response.status,
|
|
57
|
-
};
|
|
126
|
+
return this._fetchResourceOverProtocol(url, options);
|
|
58
127
|
}
|
|
59
128
|
|
|
60
129
|
/**
|
|
@@ -103,6 +172,18 @@ class Fetcher {
|
|
|
103
172
|
};
|
|
104
173
|
}
|
|
105
174
|
|
|
175
|
+
/**
|
|
176
|
+
* @param {string} requestId
|
|
177
|
+
* @return {Promise<string>}
|
|
178
|
+
*/
|
|
179
|
+
async _resolveResponseBody(requestId) {
|
|
180
|
+
const responseBody = await this.session.sendCommand('Fetch.getResponseBody', {requestId});
|
|
181
|
+
if (responseBody.base64Encoded) {
|
|
182
|
+
return Buffer.from(responseBody.body, 'base64').toString();
|
|
183
|
+
}
|
|
184
|
+
return responseBody.body;
|
|
185
|
+
}
|
|
186
|
+
|
|
106
187
|
/**
|
|
107
188
|
* @param {string} url
|
|
108
189
|
* @param {{timeout: number}} options timeout is in ms
|
|
@@ -110,7 +191,18 @@ class Fetcher {
|
|
|
110
191
|
*/
|
|
111
192
|
async _fetchResourceOverProtocol(url, options) {
|
|
112
193
|
const startTime = Date.now();
|
|
113
|
-
|
|
194
|
+
|
|
195
|
+
/** @type {NodeJS.Timeout} */
|
|
196
|
+
let timeoutHandle;
|
|
197
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
198
|
+
timeoutHandle = setTimeout(reject, options.timeout, new Error('Timed out fetching resource'));
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
const responsePromise = this._loadNetworkResource(url);
|
|
202
|
+
|
|
203
|
+
/** @type {{stream: LH.Crdp.IO.StreamHandle|null, status: number|null}} */
|
|
204
|
+
const response = await Promise.race([responsePromise, timeoutPromise])
|
|
205
|
+
.finally(() => clearTimeout(timeoutHandle));
|
|
114
206
|
|
|
115
207
|
const isOk = response.status && response.status >= 200 && response.status <= 299;
|
|
116
208
|
if (!response.stream || !isOk) return {status: response.status, content: null};
|
|
@@ -121,21 +213,105 @@ class Fetcher {
|
|
|
121
213
|
}
|
|
122
214
|
|
|
123
215
|
/**
|
|
124
|
-
*
|
|
125
|
-
* @param {
|
|
126
|
-
* @param {number} ms
|
|
216
|
+
* Fetches resource by injecting an iframe into the page.
|
|
217
|
+
* @param {string} url
|
|
218
|
+
* @param {{timeout: number}} options timeout is in ms
|
|
219
|
+
* @return {Promise<FetchResponse>}
|
|
127
220
|
*/
|
|
128
|
-
async
|
|
221
|
+
async _fetchResourceIframe(url, options) {
|
|
222
|
+
/** @type {Promise<FetchResponse>} */
|
|
223
|
+
const requestInterceptionPromise = new Promise((resolve, reject) => {
|
|
224
|
+
/** @param {LH.Crdp.Fetch.RequestPausedEvent} event */
|
|
225
|
+
const handlerAsync = async event => {
|
|
226
|
+
const {requestId, responseStatusCode} = event;
|
|
227
|
+
|
|
228
|
+
// The first requestPaused event is for the request stage. Continue it.
|
|
229
|
+
if (!responseStatusCode) {
|
|
230
|
+
// Remove cookies so we aren't buying stuff on Amazon.
|
|
231
|
+
const headers = Object.entries(event.request.headers)
|
|
232
|
+
.filter(([name]) => name !== 'Cookie')
|
|
233
|
+
.map(([name, value]) => {
|
|
234
|
+
return {name, value};
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
await this.session.sendCommand('Fetch.continueRequest', {
|
|
238
|
+
requestId,
|
|
239
|
+
headers,
|
|
240
|
+
});
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (responseStatusCode >= 200 && responseStatusCode <= 299) {
|
|
245
|
+
resolve({
|
|
246
|
+
status: responseStatusCode,
|
|
247
|
+
content: await this._resolveResponseBody(requestId),
|
|
248
|
+
});
|
|
249
|
+
} else {
|
|
250
|
+
resolve({status: responseStatusCode, content: null});
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Fail the request (from the page's perspective) so that the iframe never loads.
|
|
254
|
+
await this.session.sendCommand('Fetch.failRequest', {requestId, errorReason: 'Aborted'});
|
|
255
|
+
};
|
|
256
|
+
this._setOnRequestPausedHandler(url, event => handlerAsync(event).catch(reject));
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* @param {string} src
|
|
261
|
+
*/
|
|
262
|
+
/* c8 ignore start */
|
|
263
|
+
function injectIframe(src) {
|
|
264
|
+
const iframe = document.createElement('iframe');
|
|
265
|
+
// Try really hard not to affect the page.
|
|
266
|
+
iframe.style.display = 'none';
|
|
267
|
+
iframe.style.visibility = 'hidden';
|
|
268
|
+
iframe.style.position = 'absolute';
|
|
269
|
+
iframe.style.top = '-1000px';
|
|
270
|
+
iframe.style.left = '-1000px';
|
|
271
|
+
iframe.style.width = '1px';
|
|
272
|
+
iframe.style.height = '1px';
|
|
273
|
+
iframe.src = src;
|
|
274
|
+
iframe.onload = iframe.onerror = () => {
|
|
275
|
+
iframe.remove();
|
|
276
|
+
iframe.onload = null;
|
|
277
|
+
iframe.onerror = null;
|
|
278
|
+
};
|
|
279
|
+
document.body.appendChild(iframe);
|
|
280
|
+
}
|
|
281
|
+
/* c8 ignore stop */
|
|
282
|
+
|
|
129
283
|
/** @type {NodeJS.Timeout} */
|
|
130
|
-
let
|
|
284
|
+
let asyncTimeout;
|
|
285
|
+
/** @type {Promise<never>} */
|
|
131
286
|
const timeoutPromise = new Promise((_, reject) => {
|
|
132
|
-
|
|
287
|
+
asyncTimeout = setTimeout(reject, options.timeout, new Error('Timed out fetching resource.'));
|
|
133
288
|
});
|
|
134
289
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
290
|
+
const racePromise = Promise.race([
|
|
291
|
+
timeoutPromise,
|
|
292
|
+
requestInterceptionPromise,
|
|
293
|
+
]).finally(() => clearTimeout(asyncTimeout));
|
|
294
|
+
|
|
295
|
+
// Temporarily disable auto-attaching for this iframe.
|
|
296
|
+
await this.session.sendCommand('Target.setAutoAttach', {
|
|
297
|
+
autoAttach: false,
|
|
298
|
+
waitForDebuggerOnStart: false,
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
const injectionPromise = this.executionContext.evaluate(injectIframe, {
|
|
302
|
+
args: [url],
|
|
303
|
+
useIsolation: true,
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
const [fetchResult] = await Promise.all([racePromise, injectionPromise]);
|
|
307
|
+
|
|
308
|
+
await this.session.sendCommand('Target.setAutoAttach', {
|
|
309
|
+
flatten: true,
|
|
310
|
+
autoAttach: true,
|
|
311
|
+
waitForDebuggerOnStart: true,
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
return fetchResult;
|
|
139
315
|
}
|
|
140
316
|
}
|
|
141
317
|
|
|
@@ -71,7 +71,7 @@ class GatherRunner {
|
|
|
71
71
|
log.time(status);
|
|
72
72
|
try {
|
|
73
73
|
const requestedUrl = passContext.url;
|
|
74
|
-
const {
|
|
74
|
+
const {finalUrl, warnings} = await navigation.gotoURL(driver, requestedUrl, {
|
|
75
75
|
waitUntil: passContext.passConfig.recordTrace ?
|
|
76
76
|
['load', 'fcp'] : ['load'],
|
|
77
77
|
debugNavigation: passContext.settings.debugNavigation,
|
|
@@ -79,12 +79,7 @@ class GatherRunner {
|
|
|
79
79
|
maxWaitForLoad: passContext.settings.maxWaitForLoad,
|
|
80
80
|
...passContext.passConfig,
|
|
81
81
|
});
|
|
82
|
-
passContext.url =
|
|
83
|
-
const {URL} = passContext.baseArtifacts;
|
|
84
|
-
if (!URL.finalUrl || !URL.mainDocumentUrl) {
|
|
85
|
-
URL.finalUrl = mainDocumentUrl;
|
|
86
|
-
URL.mainDocumentUrl = mainDocumentUrl;
|
|
87
|
-
}
|
|
82
|
+
passContext.url = finalUrl;
|
|
88
83
|
if (passContext.passConfig.loadFailureMode === 'fatal') {
|
|
89
84
|
passContext.LighthouseRunWarnings.push(...warnings);
|
|
90
85
|
}
|
|
@@ -183,6 +178,11 @@ class GatherRunner {
|
|
|
183
178
|
const resetStorage = !options.settings.disableStorageReset;
|
|
184
179
|
if (resetStorage) await storage.clearDataForOrigin(session, options.requestedUrl);
|
|
185
180
|
|
|
181
|
+
// Disable fetcher, in case a gatherer enabled it.
|
|
182
|
+
// This cleanup should be removed once the only usage of
|
|
183
|
+
// fetcher (fetching arbitrary URLs) is replaced by new protocol support.
|
|
184
|
+
await driver.fetcher.disable();
|
|
185
|
+
|
|
186
186
|
await driver.disconnect();
|
|
187
187
|
} catch (err) {
|
|
188
188
|
// Ignore disconnecting error if browser was already closed.
|
|
@@ -407,12 +407,7 @@ class GatherRunner {
|
|
|
407
407
|
devtoolsLogs: {},
|
|
408
408
|
settings: options.settings,
|
|
409
409
|
GatherContext: {gatherMode: 'navigation'},
|
|
410
|
-
URL: {
|
|
411
|
-
initialUrl: await options.driver.url(),
|
|
412
|
-
requestedUrl: options.requestedUrl,
|
|
413
|
-
mainDocumentUrl: '',
|
|
414
|
-
finalUrl: '',
|
|
415
|
-
},
|
|
410
|
+
URL: {requestedUrl: options.requestedUrl, finalUrl: options.requestedUrl},
|
|
416
411
|
Timing: [],
|
|
417
412
|
PageLoadError: null,
|
|
418
413
|
};
|
|
@@ -430,6 +425,9 @@ class GatherRunner {
|
|
|
430
425
|
|
|
431
426
|
const baseArtifacts = passContext.baseArtifacts;
|
|
432
427
|
|
|
428
|
+
// Copy redirected URL to artifact.
|
|
429
|
+
baseArtifacts.URL.finalUrl = passContext.url;
|
|
430
|
+
|
|
433
431
|
// Fetch the manifest, if it exists.
|
|
434
432
|
try {
|
|
435
433
|
baseArtifacts.WebAppManifest = await WebAppManifest.getWebAppManifest(
|
|
@@ -519,6 +517,12 @@ class GatherRunner {
|
|
|
519
517
|
await GatherRunner.populateBaseArtifacts(passContext);
|
|
520
518
|
isFirstPass = false;
|
|
521
519
|
}
|
|
520
|
+
|
|
521
|
+
// Disable fetcher for every pass, in case a gatherer enabled it.
|
|
522
|
+
// Noop if fetcher was never enabled.
|
|
523
|
+
// This cleanup should be removed once the only usage of
|
|
524
|
+
// fetcher (fetching arbitrary URLs) is replaced by new protocol support.
|
|
525
|
+
await driver.fetcher.disable();
|
|
522
526
|
}
|
|
523
527
|
|
|
524
528
|
await GatherRunner.disposeDriver(driver, options);
|
|
@@ -12,6 +12,9 @@ const axeLibSource = require('../../lib/axe.js').source;
|
|
|
12
12
|
const pageFunctions = require('../../lib/page-functions.js');
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
+
* This is run in the page, not Lighthouse itself.
|
|
16
|
+
* axe.run returns a promise which fulfills with a results object
|
|
17
|
+
* containing any violations.
|
|
15
18
|
* @return {Promise<LH.Artifacts.Accessibility>}
|
|
16
19
|
*/
|
|
17
20
|
/* c8 ignore start */
|
|
@@ -35,11 +38,8 @@ async function runA11yChecks() {
|
|
|
35
38
|
'wcag2aa',
|
|
36
39
|
],
|
|
37
40
|
},
|
|
38
|
-
// resultTypes doesn't limit the output of the axeResults object. Instead, if it's defined,
|
|
39
|
-
// some expensive element identification is done only for the respective types. https://github.com/dequelabs/axe-core/blob/f62f0cf18f7b69b247b0b6362cf1ae71ffbf3a1b/lib/core/reporters/helpers/process-aggregate.js#L61-L97
|
|
40
41
|
resultTypes: ['violations', 'inapplicable'],
|
|
41
42
|
rules: {
|
|
42
|
-
// Consider http://go/prcpg for expert review of the aXe rules.
|
|
43
43
|
'tabindex': {enabled: true},
|
|
44
44
|
'accesskeys': {enabled: true},
|
|
45
45
|
'heading-order': {enabled: true},
|
|
@@ -60,12 +60,6 @@ async function runA11yChecks() {
|
|
|
60
60
|
// https://github.com/dequelabs/axe-core/issues/2958
|
|
61
61
|
'nested-interactive': {enabled: false},
|
|
62
62
|
'frame-focusable-content': {enabled: false},
|
|
63
|
-
'aria-roledescription': {enabled: false},
|
|
64
|
-
'scrollable-region-focusable': {enabled: false},
|
|
65
|
-
// TODO(paulirish): create audits and enable these 3.
|
|
66
|
-
'input-button-name': {enabled: false},
|
|
67
|
-
'role-img-alt': {enabled: false},
|
|
68
|
-
'select-name': {enabled: false},
|
|
69
63
|
},
|
|
70
64
|
});
|
|
71
65
|
|
|
@@ -76,8 +70,7 @@ async function runA11yChecks() {
|
|
|
76
70
|
return {
|
|
77
71
|
violations: axeResults.violations.map(createAxeRuleResultArtifact),
|
|
78
72
|
incomplete: axeResults.incomplete.map(createAxeRuleResultArtifact),
|
|
79
|
-
notApplicable: axeResults.inapplicable.map(result => ({id: result.id})),
|
|
80
|
-
passes: axeResults.passes.map(result => ({id: result.id})),
|
|
73
|
+
notApplicable: axeResults.inapplicable.map(result => ({id: result.id})),
|
|
81
74
|
version: axeResults.testEngine.version,
|
|
82
75
|
};
|
|
83
76
|
}
|
|
@@ -157,11 +150,6 @@ class Accessibility extends FRGatherer {
|
|
|
157
150
|
supportedModes: ['snapshot', 'navigation'],
|
|
158
151
|
};
|
|
159
152
|
|
|
160
|
-
static pageFns = {
|
|
161
|
-
runA11yChecks,
|
|
162
|
-
createAxeRuleResultArtifact,
|
|
163
|
-
};
|
|
164
|
-
|
|
165
153
|
/**
|
|
166
154
|
* @param {LH.Gatherer.FRTransitionalContext} passContext
|
|
167
155
|
* @return {Promise<LH.Artifacts.Accessibility>}
|
|
@@ -101,7 +101,6 @@ class ConsoleMessages extends FRGatherer {
|
|
|
101
101
|
stackTrace: event.exceptionDetails.stackTrace,
|
|
102
102
|
timestamp: event.timestamp,
|
|
103
103
|
url: event.exceptionDetails.url,
|
|
104
|
-
scriptId: event.exceptionDetails.scriptId,
|
|
105
104
|
lineNumber: event.exceptionDetails.lineNumber,
|
|
106
105
|
columnNumber: event.exceptionDetails.columnNumber,
|
|
107
106
|
};
|
|
@@ -118,7 +117,7 @@ class ConsoleMessages extends FRGatherer {
|
|
|
118
117
|
|
|
119
118
|
// JS events have a stack trace, which we use to get the column.
|
|
120
119
|
// CSS/HTML events only expose a line number.
|
|
121
|
-
const
|
|
120
|
+
const {columnNumber} = event.entry.stackTrace?.callFrames[0] || {};
|
|
122
121
|
|
|
123
122
|
this._logEntries.push({
|
|
124
123
|
eventType: 'protocolLog',
|
|
@@ -128,9 +127,8 @@ class ConsoleMessages extends FRGatherer {
|
|
|
128
127
|
stackTrace,
|
|
129
128
|
timestamp,
|
|
130
129
|
url,
|
|
131
|
-
scriptId: firstStackFrame?.scriptId,
|
|
132
130
|
lineNumber,
|
|
133
|
-
columnNumber
|
|
131
|
+
columnNumber,
|
|
134
132
|
});
|
|
135
133
|
}
|
|
136
134
|
|
|
@@ -12,8 +12,7 @@ const FRGatherer = require('../../../fraggle-rock/gather/base-gatherer.js');
|
|
|
12
12
|
/**
|
|
13
13
|
* Get and return `name`, `publicId`, `systemId` from
|
|
14
14
|
* `document.doctype`
|
|
15
|
-
*
|
|
16
|
-
* @return {{name: string, publicId: string, systemId: string, documentCompatMode: string} | null}
|
|
15
|
+
* @return {{name: string, publicId: string, systemId: string} | null}
|
|
17
16
|
*/
|
|
18
17
|
function getDoctype() {
|
|
19
18
|
// An example of this is warnerbros.com/archive/spacejam/movie/jam.htm
|
|
@@ -21,9 +20,8 @@ function getDoctype() {
|
|
|
21
20
|
return null;
|
|
22
21
|
}
|
|
23
22
|
|
|
24
|
-
const documentCompatMode = document.compatMode;
|
|
25
23
|
const {name, publicId, systemId} = document.doctype;
|
|
26
|
-
return {name, publicId, systemId
|
|
24
|
+
return {name, publicId, systemId};
|
|
27
25
|
}
|
|
28
26
|
|
|
29
27
|
class Doctype extends FRGatherer {
|
|
@@ -15,7 +15,6 @@ const {waitForNetworkIdle} = require('../driver/wait-for-condition.js');
|
|
|
15
15
|
|
|
16
16
|
// JPEG quality setting
|
|
17
17
|
// Exploration and examples of reports using different quality settings: https://docs.google.com/document/d/1ZSffucIca9XDW2eEwfoevrk-OTl7WQFeMf0CgeJAA8M/edit#
|
|
18
|
-
// Note: this analysis was done for JPEG, but now we use WEBP.
|
|
19
18
|
const FULL_PAGE_SCREENSHOT_QUALITY = 30;
|
|
20
19
|
|
|
21
20
|
/**
|
|
@@ -119,10 +118,10 @@ class FullPageScreenshot extends FRGatherer {
|
|
|
119
118
|
await context.driver.executionContext.evaluate(waitForDoubleRaf, {args: []});
|
|
120
119
|
|
|
121
120
|
const result = await session.sendCommand('Page.captureScreenshot', {
|
|
122
|
-
format: '
|
|
121
|
+
format: 'jpeg',
|
|
123
122
|
quality: FULL_PAGE_SCREENSHOT_QUALITY,
|
|
124
123
|
});
|
|
125
|
-
const data = 'data:image/
|
|
124
|
+
const data = 'data:image/jpeg;base64,' + result.data;
|
|
126
125
|
|
|
127
126
|
return {
|
|
128
127
|
data,
|
|
@@ -18,8 +18,20 @@ class JsUsage extends FRGatherer {
|
|
|
18
18
|
|
|
19
19
|
constructor() {
|
|
20
20
|
super();
|
|
21
|
+
/** @type {LH.Crdp.Debugger.ScriptParsedEvent[]} */
|
|
22
|
+
this._scriptParsedEvents = [];
|
|
21
23
|
/** @type {LH.Crdp.Profiler.ScriptCoverage[]} */
|
|
22
24
|
this._scriptUsages = [];
|
|
25
|
+
this.onScriptParsed = this.onScriptParsed.bind(this);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @param {LH.Crdp.Debugger.ScriptParsedEvent} event
|
|
30
|
+
*/
|
|
31
|
+
onScriptParsed(event) {
|
|
32
|
+
if (event.embedderName) {
|
|
33
|
+
this._scriptParsedEvents.push(event);
|
|
34
|
+
}
|
|
23
35
|
}
|
|
24
36
|
|
|
25
37
|
/**
|
|
@@ -43,27 +55,90 @@ class JsUsage extends FRGatherer {
|
|
|
43
55
|
}
|
|
44
56
|
|
|
45
57
|
/**
|
|
58
|
+
* @param {LH.Gatherer.FRTransitionalContext} context
|
|
59
|
+
*/
|
|
60
|
+
async startSensitiveInstrumentation(context) {
|
|
61
|
+
const session = context.driver.defaultSession;
|
|
62
|
+
session.on('Debugger.scriptParsed', this.onScriptParsed);
|
|
63
|
+
await session.sendCommand('Debugger.enable');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @param {LH.Gatherer.FRTransitionalContext} context
|
|
68
|
+
*/
|
|
69
|
+
async stopSensitiveInstrumentation(context) {
|
|
70
|
+
const session = context.driver.defaultSession;
|
|
71
|
+
await session.sendCommand('Debugger.disable');
|
|
72
|
+
session.off('Debugger.scriptParsed', this.onScriptParsed);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Usages alone do not always generate an exhaustive list of scripts in timespan and snapshot.
|
|
77
|
+
* For audits which use this for url/scriptId mappings, we can include an empty usage object.
|
|
78
|
+
*
|
|
79
|
+
* @param {Record<string, Array<LH.Crdp.Profiler.ScriptCoverage>>} usageByUrl
|
|
80
|
+
*/
|
|
81
|
+
_addMissingScriptIds(usageByUrl) {
|
|
82
|
+
for (const scriptParsedEvent of this._scriptParsedEvents) {
|
|
83
|
+
const url = scriptParsedEvent.embedderName;
|
|
84
|
+
if (!url) continue;
|
|
85
|
+
|
|
86
|
+
const scripts = usageByUrl[url] || [];
|
|
87
|
+
if (!scripts.find(s => s.scriptId === scriptParsedEvent.scriptId)) {
|
|
88
|
+
scripts.push({
|
|
89
|
+
url,
|
|
90
|
+
scriptId: scriptParsedEvent.scriptId,
|
|
91
|
+
functions: [],
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
usageByUrl[url] = scripts;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* @param {LH.Gatherer.FRTransitionalContext} context
|
|
46
100
|
* @return {Promise<LH.Artifacts['JsUsage']>}
|
|
47
101
|
*/
|
|
48
|
-
async getArtifact() {
|
|
49
|
-
/** @type {Record<string, LH.Crdp.Profiler.ScriptCoverage
|
|
50
|
-
const
|
|
102
|
+
async getArtifact(context) {
|
|
103
|
+
/** @type {Record<string, Array<LH.Crdp.Profiler.ScriptCoverage>>} */
|
|
104
|
+
const usageByUrl = {};
|
|
105
|
+
|
|
106
|
+
// Force `Debugger.scriptParsed` events for url to scriptId mappings in snapshot mode.
|
|
107
|
+
if (context.gatherMode === 'snapshot') {
|
|
108
|
+
await this.startSensitiveInstrumentation(context);
|
|
109
|
+
await this.stopSensitiveInstrumentation(context);
|
|
110
|
+
}
|
|
51
111
|
|
|
52
112
|
for (const scriptUsage of this._scriptUsages) {
|
|
53
|
-
//
|
|
54
|
-
//
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
113
|
+
// `ScriptCoverage.url` can be overridden by a magic sourceURL comment.
|
|
114
|
+
// Get the associated ScriptParsedEvent and use embedderName, which is the original url.
|
|
115
|
+
// See https://chromium-review.googlesource.com/c/v8/v8/+/2317310
|
|
116
|
+
let url = scriptUsage.url;
|
|
117
|
+
const scriptParsedEvent =
|
|
118
|
+
this._scriptParsedEvents.find(e => e.scriptId === scriptUsage.scriptId);
|
|
119
|
+
if (scriptParsedEvent?.embedderName) {
|
|
120
|
+
url = scriptParsedEvent.embedderName;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// If `url` is blank, that means the script was anonymous (eval, new Function, onload, ...).
|
|
124
|
+
// Or, it's because it was code Lighthouse over the protocol via `Runtime.evaluate`.
|
|
125
|
+
// We currently don't consider coverage of anonymous scripts, and we definitely don't want
|
|
126
|
+
// coverage of code Lighthouse ran to inspect the page, so we ignore this ScriptCoverage if
|
|
127
|
+
// url is blank.
|
|
128
|
+
if (scriptUsage.url === '' || (scriptParsedEvent && scriptParsedEvent.embedderName === '')) {
|
|
60
129
|
continue;
|
|
61
130
|
}
|
|
62
131
|
|
|
63
|
-
|
|
132
|
+
const scripts = usageByUrl[url] || [];
|
|
133
|
+
scripts.push(scriptUsage);
|
|
134
|
+
usageByUrl[url] = scripts;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (context.gatherMode !== 'navigation') {
|
|
138
|
+
this._addMissingScriptIds(usageByUrl);
|
|
64
139
|
}
|
|
65
140
|
|
|
66
|
-
return
|
|
141
|
+
return usageByUrl;
|
|
67
142
|
}
|
|
68
143
|
}
|
|
69
144
|
|