lighthouse 11.7.0 → 12.0.0-dev.20240422
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/CONTRIBUTING.md +2 -2
- package/cli/bin.js +0 -7
- package/cli/cli-flags.d.ts +1 -5
- package/cli/cli-flags.js +4 -8
- package/cli/test/smokehouse/__snapshots__/report-assert-test.js.snap +14 -14
- package/cli/test/smokehouse/config/exclusions.js +3 -0
- package/cli/test/smokehouse/core-tests.js +6 -20
- package/cli/test/smokehouse/frontends/smokehouse-bin.js +2 -2
- package/core/audits/accessibility/accesskeys.js +1 -1
- package/core/audits/accessibility/aria-allowed-attr.js +1 -1
- package/core/audits/accessibility/aria-allowed-role.js +1 -1
- package/core/audits/accessibility/aria-command-name.js +1 -1
- package/core/audits/accessibility/aria-conditional-attr.d.ts +10 -0
- package/core/audits/accessibility/aria-conditional-attr.js +43 -0
- package/core/audits/accessibility/{duplicate-id-active.d.ts → aria-deprecated-role.d.ts} +3 -3
- package/core/audits/accessibility/aria-deprecated-role.js +43 -0
- package/core/audits/accessibility/aria-dialog-name.js +1 -1
- package/core/audits/accessibility/aria-hidden-body.js +1 -1
- package/core/audits/accessibility/aria-hidden-focus.js +1 -1
- package/core/audits/accessibility/aria-input-field-name.js +1 -1
- package/core/audits/accessibility/aria-meter-name.js +1 -1
- package/core/audits/accessibility/aria-progressbar-name.js +1 -1
- package/core/audits/accessibility/aria-prohibited-attr.d.ts +10 -0
- package/core/audits/accessibility/aria-prohibited-attr.js +44 -0
- package/core/audits/accessibility/aria-required-attr.js +1 -1
- package/core/audits/accessibility/aria-required-children.js +1 -1
- package/core/audits/accessibility/aria-required-parent.js +1 -1
- package/core/audits/accessibility/aria-roles.js +1 -1
- package/core/audits/accessibility/aria-text.js +1 -1
- package/core/audits/accessibility/aria-toggle-field-name.js +1 -1
- package/core/audits/accessibility/aria-tooltip-name.js +1 -1
- package/core/audits/accessibility/aria-treeitem-name.js +1 -1
- package/core/audits/accessibility/aria-valid-attr-value.js +1 -1
- package/core/audits/accessibility/aria-valid-attr.js +1 -1
- package/core/audits/accessibility/button-name.js +1 -1
- package/core/audits/accessibility/bypass.js +1 -1
- package/core/audits/accessibility/color-contrast.js +1 -1
- package/core/audits/accessibility/definition-list.js +1 -1
- package/core/audits/accessibility/dlitem.js +1 -1
- package/core/audits/accessibility/document-title.js +1 -1
- package/core/audits/accessibility/duplicate-id-aria.js +1 -1
- package/core/audits/accessibility/empty-heading.js +1 -1
- package/core/audits/accessibility/form-field-multiple-labels.js +1 -1
- package/core/audits/accessibility/frame-title.js +1 -1
- package/core/audits/accessibility/heading-order.js +1 -1
- package/core/audits/accessibility/html-has-lang.js +1 -1
- package/core/audits/accessibility/html-lang-valid.js +1 -1
- package/core/audits/accessibility/html-xml-lang-mismatch.js +1 -1
- package/core/audits/accessibility/identical-links-same-purpose.js +1 -1
- package/core/audits/accessibility/image-alt.js +1 -1
- package/core/audits/accessibility/image-redundant-alt.js +1 -1
- package/core/audits/accessibility/input-button-name.js +1 -1
- package/core/audits/accessibility/input-image-alt.js +1 -1
- package/core/audits/accessibility/label-content-name-mismatch.js +1 -1
- package/core/audits/accessibility/label.js +1 -1
- package/core/audits/accessibility/landmark-one-main.js +1 -1
- package/core/audits/accessibility/link-in-text-block.js +1 -1
- package/core/audits/accessibility/link-name.js +1 -1
- package/core/audits/accessibility/list.js +1 -1
- package/core/audits/accessibility/listitem.js +1 -1
- package/core/audits/accessibility/meta-refresh.js +1 -1
- package/core/audits/accessibility/meta-viewport.js +1 -1
- package/core/audits/accessibility/object-alt.js +1 -1
- package/core/audits/accessibility/select-name.js +1 -1
- package/core/audits/accessibility/skip-link.js +1 -1
- package/core/audits/accessibility/tabindex.js +1 -1
- package/core/audits/accessibility/table-duplicate-name.js +1 -1
- package/core/audits/accessibility/table-fake-caption.js +1 -1
- package/core/audits/accessibility/target-size.js +1 -2
- package/core/audits/accessibility/td-has-header.js +1 -1
- package/core/audits/accessibility/td-headers-attr.js +1 -1
- package/core/audits/accessibility/th-has-data-cells.js +1 -1
- package/core/audits/accessibility/valid-lang.js +1 -1
- package/core/audits/accessibility/video-caption.js +1 -1
- package/core/audits/byte-efficiency/byte-efficiency-audit.d.ts +0 -16
- package/core/audits/byte-efficiency/byte-efficiency-audit.js +1 -38
- package/core/audits/byte-efficiency/offscreen-images.d.ts +0 -11
- package/core/audits/byte-efficiency/offscreen-images.js +0 -15
- package/core/audits/byte-efficiency/render-blocking-resources.js +27 -26
- package/core/audits/byte-efficiency/unminified-css.js +2 -2
- package/core/audits/byte-efficiency/unused-css-rules.js +3 -1
- package/core/audits/dobetterweb/uses-http2.d.ts +0 -13
- package/core/audits/dobetterweb/uses-http2.js +4 -38
- package/core/audits/font-display.js +2 -2
- package/core/audits/layout-shifts.js +2 -2
- package/core/audits/preload-fonts.d.ts +1 -5
- package/core/audits/preload-fonts.js +1 -10
- package/core/audits/prioritize-lcp-image.js +2 -2
- package/core/audits/redirects-http.d.ts +19 -0
- package/core/audits/redirects-http.js +75 -0
- package/core/audits/resource-summary.d.ts +2 -0
- package/core/audits/resource-summary.js +5 -4
- package/core/audits/seo/is-crawlable.d.ts +2 -1
- package/core/audits/seo/link-text.js +12 -0
- package/core/audits/uses-rel-preload.d.ts +1 -5
- package/core/audits/uses-rel-preload.js +5 -15
- package/core/audits/work-during-interaction.d.ts +2 -2
- package/core/audits/work-during-interaction.js +2 -2
- package/core/computed/js-bundles.d.ts +1 -1
- package/core/computed/metrics/cumulative-layout-shift.js +2 -2
- package/core/computed/metrics/lantern-first-contentful-paint.d.ts +13 -82
- package/core/computed/metrics/lantern-first-contentful-paint.js +16 -178
- package/core/computed/metrics/lantern-first-meaningful-paint.d.ts +13 -12
- package/core/computed/metrics/lantern-first-meaningful-paint.js +11 -49
- package/core/computed/metrics/lantern-interactive.d.ts +13 -21
- package/core/computed/metrics/lantern-interactive.js +12 -84
- package/core/computed/metrics/lantern-largest-contentful-paint.d.ts +13 -25
- package/core/computed/metrics/lantern-largest-contentful-paint.js +13 -80
- package/core/computed/metrics/lantern-max-potential-fid.d.ts +13 -24
- package/core/computed/metrics/lantern-max-potential-fid.js +12 -64
- package/core/computed/metrics/lantern-metric.d.ts +14 -18
- package/core/computed/metrics/lantern-metric.js +34 -30
- package/core/computed/metrics/lantern-speed-index.d.ts +13 -32
- package/core/computed/metrics/lantern-speed-index.js +13 -116
- package/core/computed/metrics/lantern-total-blocking-time.d.ts +13 -25
- package/core/computed/metrics/lantern-total-blocking-time.js +10 -94
- package/core/computed/metrics/metric.js +1 -3
- package/core/computed/module-duplication.d.ts +1 -1
- package/core/computed/navigation-insights.d.ts +36 -0
- package/core/computed/navigation-insights.js +35 -0
- package/core/computed/page-dependency-graph.js +3 -7
- package/core/computed/resource-summary.d.ts +10 -12
- package/core/computed/resource-summary.js +12 -19
- package/core/computed/trace-engine-result.d.ts +16 -3
- package/core/computed/trace-engine-result.js +19 -14
- package/core/computed/unused-css.d.ts +3 -1
- package/core/computed/unused-css.js +6 -5
- package/core/config/config-helpers.js +0 -4
- package/core/config/constants.js +0 -1
- package/core/config/default-config.js +63 -142
- package/core/config/experimental-config.js +11 -0
- package/core/config/filters.js +0 -1
- package/core/gather/driver/navigation.js +4 -1
- package/core/gather/driver/prepare.js +1 -1
- package/core/gather/driver/target-manager.js +4 -3
- package/core/gather/driver/wait-for-condition.js +2 -3
- package/core/gather/driver.d.ts +12 -0
- package/core/gather/driver.js +27 -0
- package/core/gather/gatherers/accessibility.js +4 -6
- package/core/gather/gatherers/css-usage.d.ts +0 -21
- package/core/gather/gatherers/css-usage.js +9 -119
- package/core/gather/gatherers/root-causes.d.ts +2 -2
- package/core/gather/gatherers/root-causes.js +6 -5
- package/core/gather/gatherers/stylesheets.d.ts +26 -0
- package/core/gather/gatherers/stylesheets.js +138 -0
- package/core/gather/gatherers/trace-elements.d.ts +4 -13
- package/core/gather/gatherers/trace-elements.js +42 -31
- package/core/gather/navigation-runner.js +3 -1
- package/core/gather/session.d.ts +8 -0
- package/core/gather/session.js +14 -1
- package/core/lib/emulation.js +2 -2
- package/core/lib/i18n/i18n.d.ts +0 -1
- package/core/lib/i18n/i18n.js +0 -2
- package/core/lib/lantern/cpu-node.d.ts +7 -1
- package/core/lib/lantern/cpu-node.js +12 -2
- package/core/lib/lantern/lantern-error.d.ts +8 -0
- package/core/lib/lantern/lantern-error.js +9 -0
- package/core/lib/lantern/metric.js +2 -6
- package/core/lib/lantern/metrics/first-contentful-paint.d.ts +79 -0
- package/core/lib/lantern/metrics/first-contentful-paint.js +200 -0
- package/core/lib/lantern/metrics/first-meaningful-paint.d.ts +6 -0
- package/core/lib/lantern/metrics/first-meaningful-paint.js +64 -0
- package/core/lib/lantern/metrics/interactive.d.ts +26 -0
- package/core/lib/lantern/metrics/interactive.js +112 -0
- package/core/lib/lantern/metrics/largest-contentful-paint.d.ts +19 -0
- package/core/lib/lantern/metrics/largest-contentful-paint.js +108 -0
- package/core/lib/lantern/metrics/max-potential-fid.d.ts +30 -0
- package/core/lib/lantern/metrics/max-potential-fid.js +92 -0
- package/core/lib/lantern/metrics/speed-index.d.ts +38 -0
- package/core/lib/lantern/metrics/speed-index.js +144 -0
- package/core/lib/lantern/metrics/total-blocking-time.d.ts +31 -0
- package/core/lib/lantern/metrics/total-blocking-time.js +128 -0
- package/core/lib/lantern/network-node.js +28 -4
- package/core/lib/lantern/page-dependency-graph.d.ts +4 -4
- package/core/lib/lantern/page-dependency-graph.js +19 -7
- package/core/lib/lantern/simulator/connection-pool.d.ts +2 -12
- package/core/lib/lantern/simulator/connection-pool.js +3 -22
- package/core/lib/lantern/simulator/network-analyzer.js +1 -12
- package/core/lib/lantern/simulator/simulator.d.ts +2 -4
- package/core/lib/lantern/simulator/simulator.js +7 -14
- package/core/lib/lantern/types/lantern.d.ts +5 -3
- package/core/lib/lantern-trace-saver.js +0 -1
- package/core/lib/lh-error.d.ts +9 -0
- package/core/lib/lh-error.js +13 -3
- package/core/lib/lighthouse-compatibility.js +53 -10
- package/core/lib/navigation-error.js +3 -0
- package/core/lib/network-request.d.ts +1 -0
- package/core/lib/network-request.js +29 -1
- package/core/lib/proto-preprocessor.js +24 -8
- package/core/lib/tracehouse/trace-processor.js +15 -2
- package/core/runner.js +18 -25
- package/dist/report/bundle.esm.js +11 -112
- package/dist/report/flow.js +16 -117
- package/dist/report/standalone.js +12 -113
- package/flow-report/src/help-dialog.tsx +0 -1
- package/flow-report/src/i18n/i18n.d.ts +0 -4
- package/flow-report/src/i18n/ui-strings.d.ts +0 -1
- package/flow-report/src/i18n/ui-strings.js +0 -2
- package/flow-report/src/summary/category.tsx +10 -12
- package/package.json +7 -7
- package/readme.md +4 -4
- package/report/assets/styles.css +1 -60
- package/report/assets/templates.html +0 -95
- package/report/renderer/category-renderer.js +1 -1
- package/report/renderer/components.d.ts +2 -2
- package/report/renderer/components.js +2 -103
- package/report/renderer/dom.d.ts +1 -1
- package/report/renderer/dom.js +1 -1
- package/report/renderer/performance-category-renderer.d.ts +0 -7
- package/report/renderer/performance-category-renderer.js +1 -32
- package/report/renderer/report-renderer.js +3 -13
- package/report/renderer/report-ui-features.js +1 -1
- package/report/renderer/report-utils.d.ts +0 -1
- package/report/renderer/report-utils.js +13 -13
- package/shared/localization/locales/ar-XB.json +62 -329
- package/shared/localization/locales/ar.json +62 -329
- package/shared/localization/locales/bg.json +62 -329
- package/shared/localization/locales/ca.json +62 -329
- package/shared/localization/locales/cs.json +62 -329
- package/shared/localization/locales/da.json +62 -329
- package/shared/localization/locales/de.json +62 -329
- package/shared/localization/locales/el.json +62 -329
- package/shared/localization/locales/en-GB.json +62 -329
- package/shared/localization/locales/en-US.json +101 -332
- package/shared/localization/locales/en-XA.json +62 -329
- package/shared/localization/locales/en-XL.json +101 -332
- package/shared/localization/locales/es-419.json +62 -329
- package/shared/localization/locales/es.json +62 -329
- package/shared/localization/locales/fi.json +62 -329
- package/shared/localization/locales/fil.json +61 -328
- package/shared/localization/locales/fr.json +62 -329
- package/shared/localization/locales/he.json +62 -329
- package/shared/localization/locales/hi.json +62 -329
- package/shared/localization/locales/hr.json +63 -330
- package/shared/localization/locales/hu.json +62 -329
- package/shared/localization/locales/id.json +62 -329
- package/shared/localization/locales/it.json +62 -329
- package/shared/localization/locales/ja.json +62 -329
- package/shared/localization/locales/ko.json +62 -329
- package/shared/localization/locales/lt.json +62 -329
- package/shared/localization/locales/lv.json +62 -329
- package/shared/localization/locales/nl.json +62 -329
- package/shared/localization/locales/no.json +62 -329
- package/shared/localization/locales/pl.json +61 -328
- package/shared/localization/locales/pt-PT.json +62 -329
- package/shared/localization/locales/pt.json +64 -331
- package/shared/localization/locales/ro.json +62 -329
- package/shared/localization/locales/ru.json +62 -329
- package/shared/localization/locales/sk.json +62 -329
- package/shared/localization/locales/sl.json +62 -329
- package/shared/localization/locales/sr-Latn.json +62 -329
- package/shared/localization/locales/sr.json +62 -329
- package/shared/localization/locales/sv.json +62 -329
- package/shared/localization/locales/ta.json +61 -328
- package/shared/localization/locales/te.json +62 -329
- package/shared/localization/locales/th.json +62 -329
- package/shared/localization/locales/tr.json +62 -329
- package/shared/localization/locales/uk.json +62 -329
- package/shared/localization/locales/vi.json +61 -328
- package/shared/localization/locales/zh-HK.json +62 -329
- package/shared/localization/locales/zh-TW.json +61 -328
- package/shared/localization/locales/zh.json +62 -329
- package/tsconfig.json +1 -3
- package/types/artifacts.d.ts +21 -110
- package/types/audit.d.ts +0 -10
- package/types/config.d.ts +0 -1
- package/types/externs.d.ts +0 -2
- package/types/gatherer.d.ts +3 -0
- package/types/internal/smokehouse.d.ts +1 -1
- package/types/lh.d.ts +0 -2
- package/types/lhr/audit-details.d.ts +9 -4
- package/types/lhr/lhr.d.ts +0 -4
- package/types/lhr/settings.d.ts +0 -4
- package/core/audits/accessibility/duplicate-id-active.js +0 -42
- package/core/audits/content-width.d.ts +0 -17
- package/core/audits/content-width.js +0 -76
- package/core/audits/installable-manifest.d.ts +0 -111
- package/core/audits/installable-manifest.js +0 -248
- package/core/audits/layout-shift-elements.d.ts +0 -16
- package/core/audits/layout-shift-elements.js +0 -101
- package/core/audits/manual/pwa-cross-browser.d.ts +0 -12
- package/core/audits/manual/pwa-cross-browser.js +0 -40
- package/core/audits/manual/pwa-each-page-has-url.d.ts +0 -12
- package/core/audits/manual/pwa-each-page-has-url.js +0 -38
- package/core/audits/manual/pwa-page-transitions.d.ts +0 -12
- package/core/audits/manual/pwa-page-transitions.js +0 -38
- package/core/audits/maskable-icon.d.ts +0 -25
- package/core/audits/maskable-icon.js +0 -70
- package/core/audits/multi-check-audit.d.ts +0 -28
- package/core/audits/multi-check-audit.js +0 -82
- package/core/audits/no-unload-listeners.d.ts +0 -16
- package/core/audits/no-unload-listeners.js +0 -86
- package/core/audits/performance-budget.d.ts +0 -39
- package/core/audits/performance-budget.js +0 -154
- package/core/audits/script-elements-test-audit.d.ts +0 -14
- package/core/audits/script-elements-test-audit.js +0 -29
- package/core/audits/seo/plugins.d.ts +0 -15
- package/core/audits/seo/plugins.js +0 -150
- package/core/audits/seo/tap-targets.d.ts +0 -50
- package/core/audits/seo/tap-targets.js +0 -352
- package/core/audits/splash-screen.d.ts +0 -37
- package/core/audits/splash-screen.js +0 -98
- package/core/audits/themed-omnibox.d.ts +0 -42
- package/core/audits/themed-omnibox.js +0 -102
- package/core/audits/timing-budget.d.ts +0 -43
- package/core/audits/timing-budget.js +0 -175
- package/core/computed/manifest-values.d.ts +0 -24
- package/core/computed/manifest-values.js +0 -136
- package/core/config/budget.d.ts +0 -74
- package/core/config/budget.js +0 -340
- package/core/config/metrics-to-audits.d.ts +0 -19
- package/core/config/metrics-to-audits.js +0 -62
- package/core/gather/driver/service-workers.d.ts +0 -16
- package/core/gather/driver/service-workers.js +0 -52
- package/core/gather/gatherers/dobetterweb/tags-blocking-first-paint.d.ts +0 -47
- package/core/gather/gatherers/dobetterweb/tags-blocking-first-paint.js +0 -233
- package/core/gather/gatherers/global-listeners.d.ts +0 -27
- package/core/gather/gatherers/global-listeners.js +0 -108
- package/core/gather/gatherers/installability-errors.d.ts +0 -17
- package/core/gather/gatherers/installability-errors.js +0 -56
- package/core/gather/gatherers/script-elements.d.ts +0 -21
- package/core/gather/gatherers/script-elements.js +0 -100
- package/core/gather/gatherers/seo/embedded-content.d.ts +0 -10
- package/core/gather/gatherers/seo/embedded-content.js +0 -63
- package/core/gather/gatherers/seo/tap-targets.d.ts +0 -21
- package/core/gather/gatherers/seo/tap-targets.js +0 -389
- package/core/gather/gatherers/service-worker.d.ts +0 -10
- package/core/gather/gatherers/service-worker.js +0 -32
- package/core/gather/gatherers/web-app-manifest.d.ts +0 -32
- package/core/gather/gatherers/web-app-manifest.js +0 -106
- package/report/renderer/pwa-category-renderer.d.ts +0 -55
- package/report/renderer/pwa-category-renderer.js +0 -184
- package/third-party/chromium-synchronization/installability-errors-test.js +0 -79
- package/types/lhr/budget.d.ts +0 -60
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2024 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as Lantern from '../types/lantern.js';
|
|
8
|
+
import {Metric} from '../metric.js';
|
|
9
|
+
import {BaseNode} from '../base-node.js';
|
|
10
|
+
import {BLOCKING_TIME_THRESHOLD, calculateSumOfBlockingTime} from '../../../computed/metrics/tbt-utils.js';
|
|
11
|
+
|
|
12
|
+
/** @typedef {import('../base-node.js').Node} Node */
|
|
13
|
+
|
|
14
|
+
class TotalBlockingTime extends Metric {
|
|
15
|
+
/**
|
|
16
|
+
* @return {LH.Gatherer.Simulation.MetricCoefficients}
|
|
17
|
+
*/
|
|
18
|
+
static get COEFFICIENTS() {
|
|
19
|
+
return {
|
|
20
|
+
intercept: 0,
|
|
21
|
+
optimistic: 0.5,
|
|
22
|
+
pessimistic: 0.5,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @param {Node} dependencyGraph
|
|
28
|
+
* @return {Node}
|
|
29
|
+
*/
|
|
30
|
+
static getOptimisticGraph(dependencyGraph) {
|
|
31
|
+
return dependencyGraph;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @param {Node} dependencyGraph
|
|
36
|
+
* @return {Node}
|
|
37
|
+
*/
|
|
38
|
+
static getPessimisticGraph(dependencyGraph) {
|
|
39
|
+
return dependencyGraph;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @param {LH.Gatherer.Simulation.Result} simulation
|
|
44
|
+
* @param {import('../metric.js').Extras} extras
|
|
45
|
+
* @return {LH.Gatherer.Simulation.Result}
|
|
46
|
+
*/
|
|
47
|
+
static getEstimateFromSimulation(simulation, extras) {
|
|
48
|
+
if (!extras.fcpResult) throw new Error('missing fcpResult');
|
|
49
|
+
if (!extras.interactiveResult) throw new Error('missing interactiveResult');
|
|
50
|
+
|
|
51
|
+
// Intentionally use the opposite FCP estimate. A pessimistic FCP is higher than equal to an
|
|
52
|
+
// optimistic FCP, which means potentially more tasks are excluded from the Total Blocking Time
|
|
53
|
+
// computation. So a more pessimistic FCP gives a more optimistic Total Blocking Time for the
|
|
54
|
+
// same work.
|
|
55
|
+
const fcpTimeInMs = extras.optimistic
|
|
56
|
+
? extras.fcpResult.pessimisticEstimate.timeInMs
|
|
57
|
+
: extras.fcpResult.optimisticEstimate.timeInMs;
|
|
58
|
+
|
|
59
|
+
// Similarly, we always have pessimistic TTI >= optimistic TTI. Therefore, picking optimistic
|
|
60
|
+
// TTI means our window of interest is smaller and thus potentially more tasks are excluded from
|
|
61
|
+
// Total Blocking Time computation, yielding a lower (more optimistic) Total Blocking Time value
|
|
62
|
+
// for the same work.
|
|
63
|
+
const interactiveTimeMs = extras.optimistic
|
|
64
|
+
? extras.interactiveResult.optimisticEstimate.timeInMs
|
|
65
|
+
: extras.interactiveResult.pessimisticEstimate.timeInMs;
|
|
66
|
+
|
|
67
|
+
const minDurationMs = BLOCKING_TIME_THRESHOLD;
|
|
68
|
+
|
|
69
|
+
const events = TotalBlockingTime.getTopLevelEvents(
|
|
70
|
+
simulation.nodeTimings,
|
|
71
|
+
minDurationMs
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
timeInMs: calculateSumOfBlockingTime(
|
|
76
|
+
events,
|
|
77
|
+
fcpTimeInMs,
|
|
78
|
+
interactiveTimeMs
|
|
79
|
+
),
|
|
80
|
+
nodeTimings: simulation.nodeTimings,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* @param {Lantern.Simulation.MetricComputationDataInput} data
|
|
86
|
+
* @param {Omit<import('../metric.js').Extras, 'optimistic'>=} extras
|
|
87
|
+
* @return {Promise<LH.Artifacts.LanternMetric>}
|
|
88
|
+
*/
|
|
89
|
+
static async compute(data, extras) {
|
|
90
|
+
const fcpResult = extras?.fcpResult;
|
|
91
|
+
if (!fcpResult) {
|
|
92
|
+
throw new Error('FCP is required to calculate the TBT metric');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const interactiveResult = extras?.fcpResult;
|
|
96
|
+
if (!interactiveResult) {
|
|
97
|
+
throw new Error('Interactive is required to calculate the TBT metric');
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return super.compute(data, extras);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* @param {LH.Gatherer.Simulation.Result['nodeTimings']} nodeTimings
|
|
105
|
+
* @param {number} minDurationMs
|
|
106
|
+
*/
|
|
107
|
+
static getTopLevelEvents(nodeTimings, minDurationMs) {
|
|
108
|
+
/** @type {Array<{start: number, end: number, duration: number}>}
|
|
109
|
+
*/
|
|
110
|
+
const events = [];
|
|
111
|
+
|
|
112
|
+
for (const [node, timing] of nodeTimings.entries()) {
|
|
113
|
+
if (node.type !== BaseNode.TYPES.CPU) continue;
|
|
114
|
+
// Filtering out events below minimum duration.
|
|
115
|
+
if (timing.duration < minDurationMs) continue;
|
|
116
|
+
|
|
117
|
+
events.push({
|
|
118
|
+
start: timing.startTime,
|
|
119
|
+
end: timing.endTime,
|
|
120
|
+
duration: timing.duration,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return events;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export {TotalBlockingTime};
|
|
@@ -7,8 +7,32 @@
|
|
|
7
7
|
import * as Lantern from './types/lantern.js';
|
|
8
8
|
import {NetworkRequestTypes} from './lantern.js';
|
|
9
9
|
import {BaseNode} from './base-node.js';
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
|
|
11
|
+
const NON_NETWORK_SCHEMES = [
|
|
12
|
+
'blob', // @see https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL
|
|
13
|
+
'data', // @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs
|
|
14
|
+
'intent', // @see https://developer.chrome.com/docs/multidevice/android/intents/
|
|
15
|
+
'file', // @see https://en.wikipedia.org/wiki/File_URI_scheme
|
|
16
|
+
'filesystem', // @see https://developer.mozilla.org/en-US/docs/Web/API/FileSystem
|
|
17
|
+
'chrome-extension',
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Use `NetworkRequest.isNonNetworkRequest(req)` if working with a request.
|
|
22
|
+
* Note: the `protocol` field from CDP can be 'h2', 'http', (not 'https'!) or it'll be url's scheme.
|
|
23
|
+
* https://source.chromium.org/chromium/chromium/src/+/main:content/browser/devtools/protocol/network_handler.cc;l=598-611;drc=56d4a9a9deb30be73adcee8737c73bcb2a5ab64f
|
|
24
|
+
* However, a `new URL(href).protocol` has a colon suffix.
|
|
25
|
+
* https://url.spec.whatwg.org/#dom-url-protocol
|
|
26
|
+
* A URL's `scheme` is specced as the `protocol` sans-colon, but isn't exposed on a URL object.
|
|
27
|
+
* This method can take all 3 of these string types as a parameter.
|
|
28
|
+
* @param {string} protocol Either a networkRequest's `protocol` per CDP or a `new URL(href).protocol`
|
|
29
|
+
* @return {boolean}
|
|
30
|
+
*/
|
|
31
|
+
function isNonNetworkProtocol(protocol) {
|
|
32
|
+
// Strip off any colon
|
|
33
|
+
const urlScheme = protocol.includes(':') ? protocol.slice(0, protocol.indexOf(':')) : protocol;
|
|
34
|
+
return NON_NETWORK_SCHEMES.includes(urlScheme);
|
|
35
|
+
}
|
|
12
36
|
|
|
13
37
|
/**
|
|
14
38
|
* @template [T=any]
|
|
@@ -75,9 +99,9 @@ class NetworkNode extends BaseNode {
|
|
|
75
99
|
*/
|
|
76
100
|
get isNonNetworkProtocol() {
|
|
77
101
|
// The 'protocol' field in devtools a string more like a `scheme`
|
|
78
|
-
return
|
|
102
|
+
return isNonNetworkProtocol(this.request.protocol) ||
|
|
79
103
|
// But `protocol` can fail to be populated if the request fails, so fallback to scheme.
|
|
80
|
-
|
|
104
|
+
isNonNetworkProtocol(this.request.parsedURL.scheme);
|
|
81
105
|
}
|
|
82
106
|
|
|
83
107
|
/**
|
|
@@ -18,10 +18,10 @@ export class PageDependencyGraph {
|
|
|
18
18
|
*/
|
|
19
19
|
static getNetworkNodeOutput(networkRecords: Array<Lantern.NetworkRequest>): NetworkNodeOutput;
|
|
20
20
|
/**
|
|
21
|
-
* @param {LH.
|
|
21
|
+
* @param {LH.TraceEvent[]} mainThreadEvents
|
|
22
22
|
* @return {Array<CPUNode>}
|
|
23
23
|
*/
|
|
24
|
-
static getCPUNodes(
|
|
24
|
+
static getCPUNodes(mainThreadEvents: LH.TraceEvent[]): Array<CPUNode>;
|
|
25
25
|
/**
|
|
26
26
|
* @param {NetworkNode} rootNode
|
|
27
27
|
* @param {NetworkNodeOutput} networkNodeOutput
|
|
@@ -40,12 +40,12 @@ export class PageDependencyGraph {
|
|
|
40
40
|
*/
|
|
41
41
|
static _pruneNode(node: Node): void;
|
|
42
42
|
/**
|
|
43
|
-
* @param {LH.
|
|
43
|
+
* @param {LH.TraceEvent[]} mainThreadEvents
|
|
44
44
|
* @param {Array<Lantern.NetworkRequest>} networkRecords
|
|
45
45
|
* @param {URLArtifact} URL
|
|
46
46
|
* @return {Node}
|
|
47
47
|
*/
|
|
48
|
-
static createGraph(
|
|
48
|
+
static createGraph(mainThreadEvents: LH.TraceEvent[], networkRecords: Array<Lantern.NetworkRequest>, URL: URLArtifact): Node;
|
|
49
49
|
/**
|
|
50
50
|
*
|
|
51
51
|
* @param {Node} rootNode
|
|
@@ -109,10 +109,10 @@ class PageDependencyGraph {
|
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
/**
|
|
112
|
-
* @param {LH.
|
|
112
|
+
* @param {LH.TraceEvent[]} mainThreadEvents
|
|
113
113
|
* @return {Array<CPUNode>}
|
|
114
114
|
*/
|
|
115
|
-
static getCPUNodes(
|
|
115
|
+
static getCPUNodes(mainThreadEvents) {
|
|
116
116
|
/** @type {Array<CPUNode>} */
|
|
117
117
|
const nodes = [];
|
|
118
118
|
let i = 0;
|
|
@@ -128,6 +128,9 @@ class PageDependencyGraph {
|
|
|
128
128
|
continue;
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
+
/** @type {number|undefined} */
|
|
132
|
+
let correctedEndTs = undefined;
|
|
133
|
+
|
|
131
134
|
// Capture all events that occurred within the task
|
|
132
135
|
/** @type {Array<LH.TraceEvent>} */
|
|
133
136
|
const children = [];
|
|
@@ -136,10 +139,19 @@ class PageDependencyGraph {
|
|
|
136
139
|
i < mainThreadEvents.length && mainThreadEvents[i].ts < endTime;
|
|
137
140
|
i++
|
|
138
141
|
) {
|
|
142
|
+
// Temporary fix for a Chrome bug where some RunTask events can be overlapping.
|
|
143
|
+
// We correct that here be ensuring each RunTask ends at least 1 microsecond before the next
|
|
144
|
+
// https://github.com/GoogleChrome/lighthouse/issues/15896
|
|
145
|
+
// https://issues.chromium.org/issues/329678173
|
|
146
|
+
if (TraceProcessor.isScheduleableTask(mainThreadEvents[i]) && mainThreadEvents[i].dur) {
|
|
147
|
+
correctedEndTs = mainThreadEvents[i].ts - 1;
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
|
|
139
151
|
children.push(mainThreadEvents[i]);
|
|
140
152
|
}
|
|
141
153
|
|
|
142
|
-
nodes.push(new CPUNode(evt, children));
|
|
154
|
+
nodes.push(new CPUNode(evt, children, correctedEndTs));
|
|
143
155
|
}
|
|
144
156
|
|
|
145
157
|
return nodes;
|
|
@@ -359,7 +371,7 @@ class PageDependencyGraph {
|
|
|
359
371
|
isFirst = foundFirstParse = true;
|
|
360
372
|
}
|
|
361
373
|
|
|
362
|
-
if (isFirst || node.
|
|
374
|
+
if (isFirst || node.duration >= minimumEvtDur) {
|
|
363
375
|
// Don't prune this node. The task is long / important so it will impact simulation.
|
|
364
376
|
continue;
|
|
365
377
|
}
|
|
@@ -393,14 +405,14 @@ class PageDependencyGraph {
|
|
|
393
405
|
}
|
|
394
406
|
|
|
395
407
|
/**
|
|
396
|
-
* @param {LH.
|
|
408
|
+
* @param {LH.TraceEvent[]} mainThreadEvents
|
|
397
409
|
* @param {Array<Lantern.NetworkRequest>} networkRecords
|
|
398
410
|
* @param {URLArtifact} URL
|
|
399
411
|
* @return {Node}
|
|
400
412
|
*/
|
|
401
|
-
static createGraph(
|
|
413
|
+
static createGraph(mainThreadEvents, networkRecords, URL) {
|
|
402
414
|
const networkNodeOutput = PageDependencyGraph.getNetworkNodeOutput(networkRecords);
|
|
403
|
-
const cpuNodes = PageDependencyGraph.getCPUNodes(
|
|
415
|
+
const cpuNodes = PageDependencyGraph.getCPUNodes(mainThreadEvents);
|
|
404
416
|
const {requestedUrl, mainDocumentUrl} = URL;
|
|
405
417
|
if (!requestedUrl) throw new Error('requestedUrl is required to get the root request');
|
|
406
418
|
if (!mainDocumentUrl) throw new Error('mainDocumentUrl is required to get the main resource');
|
|
@@ -19,27 +19,17 @@ export class ConnectionPool {
|
|
|
19
19
|
_initializeConnections(): void;
|
|
20
20
|
/**
|
|
21
21
|
* @param {Array<TcpConnection>} connections
|
|
22
|
-
* @param {{ignoreConnectionReused?: boolean, observedConnectionWasReused: boolean}} options
|
|
23
22
|
*/
|
|
24
|
-
_findAvailableConnectionWithLargestCongestionWindow(connections: Array<TcpConnection
|
|
25
|
-
ignoreConnectionReused?: boolean | undefined;
|
|
26
|
-
observedConnectionWasReused: boolean;
|
|
27
|
-
}): TcpConnection | null;
|
|
23
|
+
_findAvailableConnectionWithLargestCongestionWindow(connections: Array<TcpConnection>): TcpConnection | null;
|
|
28
24
|
/**
|
|
29
25
|
* This method finds an available connection to the origin specified by the network record or null
|
|
30
26
|
* if no connection was available. If returned, connection will not be available for other network
|
|
31
27
|
* records until release is called.
|
|
32
28
|
*
|
|
33
|
-
* If ignoreConnectionReused is true, acquire will consider all connections not in use as available.
|
|
34
|
-
* Otherwise, only connections that have matching "warmth" are considered available.
|
|
35
|
-
*
|
|
36
29
|
* @param {Lantern.NetworkRequest} record
|
|
37
|
-
* @param {{ignoreConnectionReused?: boolean}} options
|
|
38
30
|
* @return {?TcpConnection}
|
|
39
31
|
*/
|
|
40
|
-
acquire(record: Lantern.NetworkRequest
|
|
41
|
-
ignoreConnectionReused?: boolean;
|
|
42
|
-
}): TcpConnection | null;
|
|
32
|
+
acquire(record: Lantern.NetworkRequest): TcpConnection | null;
|
|
43
33
|
/**
|
|
44
34
|
* Return the connection currently being used to fetch a record. If no connection
|
|
45
35
|
* currently being used for this record, an error will be thrown.
|
|
@@ -85,24 +85,13 @@ export class ConnectionPool {
|
|
|
85
85
|
|
|
86
86
|
/**
|
|
87
87
|
* @param {Array<TcpConnection>} connections
|
|
88
|
-
* @param {{ignoreConnectionReused?: boolean, observedConnectionWasReused: boolean}} options
|
|
89
88
|
*/
|
|
90
|
-
_findAvailableConnectionWithLargestCongestionWindow(connections
|
|
91
|
-
const {ignoreConnectionReused, observedConnectionWasReused} = options;
|
|
92
|
-
|
|
89
|
+
_findAvailableConnectionWithLargestCongestionWindow(connections) {
|
|
93
90
|
/** @type {TcpConnection|null} */
|
|
94
91
|
let maxConnection = null;
|
|
95
92
|
for (let i = 0; i < connections.length; i++) {
|
|
96
93
|
const connection = connections[i];
|
|
97
94
|
|
|
98
|
-
// Normally, we want to make sure the connection warmth matches the state of the record
|
|
99
|
-
// we're acquiring for. Do this check first since it's the common case and cheaper than our
|
|
100
|
-
// "in use" check below.
|
|
101
|
-
// Use the _warmed property instead of the getter because this is a surprisingly hot code path.
|
|
102
|
-
if (!ignoreConnectionReused && connection._warmed !== observedConnectionWasReused) {
|
|
103
|
-
continue;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
95
|
// Connections that are in use are never available.
|
|
107
96
|
if (this._connectionsInUse.has(connection)) {
|
|
108
97
|
continue;
|
|
@@ -121,23 +110,15 @@ export class ConnectionPool {
|
|
|
121
110
|
* if no connection was available. If returned, connection will not be available for other network
|
|
122
111
|
* records until release is called.
|
|
123
112
|
*
|
|
124
|
-
* If ignoreConnectionReused is true, acquire will consider all connections not in use as available.
|
|
125
|
-
* Otherwise, only connections that have matching "warmth" are considered available.
|
|
126
|
-
*
|
|
127
113
|
* @param {Lantern.NetworkRequest} record
|
|
128
|
-
* @param {{ignoreConnectionReused?: boolean}} options
|
|
129
114
|
* @return {?TcpConnection}
|
|
130
115
|
*/
|
|
131
|
-
acquire(record
|
|
116
|
+
acquire(record) {
|
|
132
117
|
if (this._connectionsByRecord.has(record)) throw new Error('Record already has a connection');
|
|
133
118
|
|
|
134
119
|
const origin = record.parsedURL.securityOrigin;
|
|
135
|
-
const observedConnectionWasReused = !!this._connectionReusedByRequestId.get(record.requestId);
|
|
136
120
|
const connections = this._connectionsByOrigin.get(origin) || [];
|
|
137
|
-
const connectionToUse = this._findAvailableConnectionWithLargestCongestionWindow(connections
|
|
138
|
-
ignoreConnectionReused: options.ignoreConnectionReused,
|
|
139
|
-
observedConnectionWasReused,
|
|
140
|
-
});
|
|
121
|
+
const connectionToUse = this._findAvailableConnectionWithLargestCongestionWindow(connections);
|
|
141
122
|
|
|
142
123
|
if (!connectionToUse) return null;
|
|
143
124
|
|
|
@@ -140,15 +140,6 @@ class NetworkAnalyzer {
|
|
|
140
140
|
const {timing, connectionReused, record} = info;
|
|
141
141
|
if (connectionReused) return;
|
|
142
142
|
|
|
143
|
-
// In LR, network records are missing connection timing, but we've smuggled it in via headers.
|
|
144
|
-
if (global.isLightrider && record.lrStatistics) {
|
|
145
|
-
if (record.protocol.startsWith('h3')) {
|
|
146
|
-
return record.lrStatistics.TCPMs;
|
|
147
|
-
} else {
|
|
148
|
-
return [record.lrStatistics.TCPMs / 2, record.lrStatistics.TCPMs / 2];
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
143
|
const {connectStart, sslStart, sslEnd, connectEnd} = timing;
|
|
153
144
|
if (connectEnd >= 0 && connectStart >= 0 && record.protocol.startsWith('h3')) {
|
|
154
145
|
// These values are equal to sslStart and sslEnd for h3.
|
|
@@ -256,9 +247,7 @@ class NetworkAnalyzer {
|
|
|
256
247
|
*/
|
|
257
248
|
static _estimateResponseTimeByOrigin(records, rttByOrigin) {
|
|
258
249
|
return NetworkAnalyzer._estimateValueByOrigin(records, ({record, timing}) => {
|
|
259
|
-
|
|
260
|
-
// close to the response time.
|
|
261
|
-
if (global.isLightrider && record.lrStatistics) return record.lrStatistics.requestMs;
|
|
250
|
+
if (record.serverResponseTime !== undefined) return record.serverResponseTime;
|
|
262
251
|
|
|
263
252
|
if (!Number.isFinite(timing.receiveHeadersEnd) || timing.receiveHeadersEnd < 0) return;
|
|
264
253
|
if (!Number.isFinite(timing.sendEnd) || timing.sendEnd < 0) return;
|
|
@@ -29,7 +29,6 @@ export class Simulator<T = any> {
|
|
|
29
29
|
_layoutTaskMultiplier: number;
|
|
30
30
|
/** @type {Array<Node>} */
|
|
31
31
|
_cachedNodeListByStartPosition: Array<Node>;
|
|
32
|
-
_flexibleOrdering: boolean;
|
|
33
32
|
_nodeTimings: SimulatorTimingMap;
|
|
34
33
|
/** @type {Map<string, number>} */
|
|
35
34
|
_numberInProgressByType: Map<string, number>;
|
|
@@ -131,17 +130,16 @@ export class Simulator<T = any> {
|
|
|
131
130
|
* Estimates the time taken to process all of the graph's nodes, returns the overall time along with
|
|
132
131
|
* each node annotated by start/end times.
|
|
133
132
|
*
|
|
134
|
-
*
|
|
133
|
+
* Simulator/connection pool are allowed to deviate from what was
|
|
135
134
|
* observed in the trace/devtoolsLog and start requests as soon as they are queued (i.e. do not
|
|
136
135
|
* wait around for a warm connection to be available if the original record was fetched on a warm
|
|
137
136
|
* connection).
|
|
138
137
|
*
|
|
139
138
|
* @param {Node} graph
|
|
140
|
-
* @param {{
|
|
139
|
+
* @param {{label?: string}=} options
|
|
141
140
|
* @return {Lantern.Simulation.Result<T>}
|
|
142
141
|
*/
|
|
143
142
|
simulate(graph: Node, options?: {
|
|
144
|
-
flexibleOrdering?: boolean;
|
|
145
143
|
label?: string;
|
|
146
144
|
} | undefined): Lantern.Simulation.Result<T>;
|
|
147
145
|
/**
|
|
@@ -80,7 +80,6 @@ class Simulator {
|
|
|
80
80
|
this._cachedNodeListByStartPosition = [];
|
|
81
81
|
|
|
82
82
|
// Properties reset on every `.simulate` call but duplicated here for type checking
|
|
83
|
-
this._flexibleOrdering = false;
|
|
84
83
|
this._nodeTimings = new SimulatorTimingMap();
|
|
85
84
|
/** @type {Map<string, number>} */
|
|
86
85
|
this._numberInProgressByType = new Map();
|
|
@@ -197,9 +196,7 @@ class Simulator {
|
|
|
197
196
|
* @return {?TcpConnection}
|
|
198
197
|
*/
|
|
199
198
|
_acquireConnection(record) {
|
|
200
|
-
return this._connectionPool.acquire(record
|
|
201
|
-
ignoreConnectionReused: this._flexibleOrdering,
|
|
202
|
-
});
|
|
199
|
+
return this._connectionPool.acquire(record);
|
|
203
200
|
}
|
|
204
201
|
|
|
205
202
|
/**
|
|
@@ -276,7 +273,7 @@ class Simulator {
|
|
|
276
273
|
? this._layoutTaskMultiplier
|
|
277
274
|
: this._cpuSlowdownMultiplier;
|
|
278
275
|
const totalDuration = Math.min(
|
|
279
|
-
Math.round(cpuNode.
|
|
276
|
+
Math.round(cpuNode.duration / 1000 * multiplier),
|
|
280
277
|
DEFAULT_MAXIMUM_CPU_TASK_DURATION
|
|
281
278
|
);
|
|
282
279
|
const estimatedTimeElapsed = totalDuration - timingData.timeElapsed;
|
|
@@ -425,13 +422,13 @@ class Simulator {
|
|
|
425
422
|
* Estimates the time taken to process all of the graph's nodes, returns the overall time along with
|
|
426
423
|
* each node annotated by start/end times.
|
|
427
424
|
*
|
|
428
|
-
*
|
|
425
|
+
* Simulator/connection pool are allowed to deviate from what was
|
|
429
426
|
* observed in the trace/devtoolsLog and start requests as soon as they are queued (i.e. do not
|
|
430
427
|
* wait around for a warm connection to be available if the original record was fetched on a warm
|
|
431
428
|
* connection).
|
|
432
429
|
*
|
|
433
430
|
* @param {Node} graph
|
|
434
|
-
* @param {{
|
|
431
|
+
* @param {{label?: string}=} options
|
|
435
432
|
* @return {Lantern.Simulation.Result<T>}
|
|
436
433
|
*/
|
|
437
434
|
simulate(graph, options) {
|
|
@@ -441,11 +438,9 @@ class Simulator {
|
|
|
441
438
|
|
|
442
439
|
options = Object.assign({
|
|
443
440
|
label: undefined,
|
|
444
|
-
flexibleOrdering: false,
|
|
445
441
|
}, options);
|
|
446
442
|
|
|
447
443
|
// initialize the necessary data containers
|
|
448
|
-
this._flexibleOrdering = !!options.flexibleOrdering;
|
|
449
444
|
this._dns = new DNSCache({rtt: this._rtt});
|
|
450
445
|
this._initializeConnectionPool(graph);
|
|
451
446
|
this._initializeAuxiliaryData();
|
|
@@ -470,11 +465,9 @@ class Simulator {
|
|
|
470
465
|
}
|
|
471
466
|
|
|
472
467
|
if (!nodesInProgress.size) {
|
|
473
|
-
//
|
|
474
|
-
//
|
|
475
|
-
|
|
476
|
-
this._flexibleOrdering = true;
|
|
477
|
-
continue;
|
|
468
|
+
// Interplay between fromDiskCache and connectionReused can be incorrect,
|
|
469
|
+
// have to give up.
|
|
470
|
+
throw new Error('Failed to start a node');
|
|
478
471
|
}
|
|
479
472
|
|
|
480
473
|
// set the available throughput for all connections based on # inflight
|
|
@@ -67,9 +67,6 @@ export class NetworkRequest<T = any> {
|
|
|
67
67
|
resourceSize: number;
|
|
68
68
|
fromDiskCache: boolean;
|
|
69
69
|
fromMemoryCache: boolean;
|
|
70
|
-
// TODO(15841): remove from lantern.
|
|
71
|
-
/** Extra timing information available only when run in Lightrider. */
|
|
72
|
-
lrStatistics: LightriderStatistics | undefined;
|
|
73
70
|
finished: boolean;
|
|
74
71
|
statusCode: number;
|
|
75
72
|
/** The network request that this one redirected to */
|
|
@@ -80,6 +77,11 @@ export class NetworkRequest<T = any> {
|
|
|
80
77
|
/** The chain of network requests that redirected to this one */
|
|
81
78
|
redirects: NetworkRequest[] | undefined;
|
|
82
79
|
timing: LH.Crdp.Network.ResourceTiming | undefined;
|
|
80
|
+
/**
|
|
81
|
+
* Optional value for how long the server took to respond to this request.
|
|
82
|
+
* When not provided, the server response time is derived from the timing object.
|
|
83
|
+
*/
|
|
84
|
+
serverResponseTime: number | undefined;
|
|
83
85
|
resourceType: LH.Crdp.Network.ResourceType | undefined;
|
|
84
86
|
mimeType: string;
|
|
85
87
|
priority: LH.Crdp.Network.ResourcePriority;
|
package/core/lib/lh-error.d.ts
CHANGED
|
@@ -105,6 +105,7 @@ export namespace UIStrings {
|
|
|
105
105
|
const missingRequiredArtifact: string;
|
|
106
106
|
const erroredRequiredArtifact: string;
|
|
107
107
|
const oldChromeDoesNotSupportFeature: string;
|
|
108
|
+
const targetCrashed: string;
|
|
108
109
|
}
|
|
109
110
|
declare namespace ERRORS {
|
|
110
111
|
namespace NO_SPEEDLINE_FRAMES {
|
|
@@ -340,6 +341,14 @@ declare namespace ERRORS {
|
|
|
340
341
|
import message_30 = UIStrings.erroredRequiredArtifact;
|
|
341
342
|
export { message_30 as message };
|
|
342
343
|
}
|
|
344
|
+
namespace TARGET_CRASHED {
|
|
345
|
+
const code_31: string;
|
|
346
|
+
export { code_31 as code };
|
|
347
|
+
import message_31 = UIStrings.targetCrashed;
|
|
348
|
+
export { message_31 as message };
|
|
349
|
+
const lhrRuntimeError_22: boolean;
|
|
350
|
+
export { lhrRuntimeError_22 as lhrRuntimeError };
|
|
351
|
+
}
|
|
343
352
|
}
|
|
344
353
|
export {};
|
|
345
354
|
//# sourceMappingURL=lh-error.d.ts.map
|
package/core/lib/lh-error.js
CHANGED
|
@@ -76,13 +76,13 @@ const UIStrings = {
|
|
|
76
76
|
criTimeout: 'Timeout waiting for initial Debugger Protocol connection.',
|
|
77
77
|
/**
|
|
78
78
|
* @description Error message explaining that a resource that was required for testing was never collected. "artifactName" will be replaced with the name of the resource that wasn't collected.
|
|
79
|
-
* @example {
|
|
79
|
+
* @example {MainDocumentContent} artifactName
|
|
80
80
|
* */
|
|
81
81
|
missingRequiredArtifact: 'Required {artifactName} gatherer did not run.',
|
|
82
82
|
/**
|
|
83
83
|
* @description Error message explaining that there was an error while trying to collect a resource that was required for testing. "artifactName" will be replaced with the name of the resource that wasn't collected; "errorMessage" will be replaced with a string description of the error that occurred.
|
|
84
|
-
* @example {
|
|
85
|
-
* @example {
|
|
84
|
+
* @example {MainDocumentContent} artifactName
|
|
85
|
+
* @example {Could not find main document} errorMessage
|
|
86
86
|
* */
|
|
87
87
|
erroredRequiredArtifact: 'Required {artifactName} gatherer encountered an error: {errorMessage}',
|
|
88
88
|
|
|
@@ -91,6 +91,9 @@ const UIStrings = {
|
|
|
91
91
|
* @example {Largest Contentful Paint} featureName
|
|
92
92
|
* */
|
|
93
93
|
oldChromeDoesNotSupportFeature: 'This version of Chrome is too old to support \'{featureName}\'. Use a newer version to see full results.',
|
|
94
|
+
|
|
95
|
+
/** Error message explaining that the browser tab that Lighthouse is inspecting has crashed. */
|
|
96
|
+
targetCrashed: 'Browser tab has unexpectedly crashed.',
|
|
94
97
|
};
|
|
95
98
|
|
|
96
99
|
const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
|
|
@@ -423,6 +426,13 @@ const ERRORS = {
|
|
|
423
426
|
message: UIStrings.erroredRequiredArtifact,
|
|
424
427
|
},
|
|
425
428
|
|
|
429
|
+
/** The page has crashed and will no longer respond to 99% of CDP commmands. */
|
|
430
|
+
TARGET_CRASHED: {
|
|
431
|
+
code: 'TARGET_CRASHED',
|
|
432
|
+
message: UIStrings.targetCrashed,
|
|
433
|
+
lhrRuntimeError: true,
|
|
434
|
+
},
|
|
435
|
+
|
|
426
436
|
// Hey! When adding a new error type, update lighthouse-result.proto too.
|
|
427
437
|
// Only necessary for runtime errors, which come from artifacts or pageLoadErrors.
|
|
428
438
|
};
|
|
@@ -108,19 +108,62 @@ function upgradeLhrForCompatibility(lhr) {
|
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
-
// This backcompat converts old LHRs
|
|
112
|
-
//
|
|
113
|
-
//
|
|
111
|
+
// This backcompat converts old LHRs to use the "hidden" and "diagnostics" groups.
|
|
112
|
+
// <9.0.0 LHRs used "no group" to identify audits that should be hidden in performance instead of the "hidden" group.
|
|
113
|
+
// <9.0.0 LHRs used "load-opportunities" for opportunity audits that are now grouped under "diagnostics".
|
|
114
|
+
// >=9.0.0 && <12.0.0 LHRs use "no group" to identify opportunities and diagnostics whose groups are assigned by details type.
|
|
115
|
+
// >=12.0.0 LHRs use the "hidden" group to identify hidden audits and "diagnostics" to identify diagnostics. "no group" is meaningless.
|
|
114
116
|
const [majorVersion] = lhr.lighthouseVersion.split('.').map(Number);
|
|
115
117
|
const perfCategory = lhr.categories['performance'];
|
|
116
|
-
if (
|
|
117
|
-
if (
|
|
118
|
-
|
|
118
|
+
if (perfCategory) {
|
|
119
|
+
if (majorVersion < 9) {
|
|
120
|
+
if (!lhr.categoryGroups) lhr.categoryGroups = {};
|
|
121
|
+
lhr.categoryGroups['hidden'] = {title: ''};
|
|
122
|
+
for (const auditRef of perfCategory.auditRefs) {
|
|
123
|
+
if (!auditRef.group) {
|
|
124
|
+
auditRef.group = 'hidden';
|
|
125
|
+
} else if (auditRef.group === 'load-opportunities') {
|
|
126
|
+
auditRef.group = 'diagnostics';
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
} else if (majorVersion < 12) {
|
|
130
|
+
for (const auditRef of perfCategory.auditRefs) {
|
|
131
|
+
if (!auditRef.group) {
|
|
132
|
+
auditRef.group = 'diagnostics';
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (majorVersion < 12 && perfCategory) {
|
|
139
|
+
/** @type {Map<string, string[]>} */
|
|
140
|
+
const metricRelevanceMap = new Map();
|
|
141
|
+
|
|
119
142
|
for (const auditRef of perfCategory.auditRefs) {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
143
|
+
/** @type {string[]|undefined} */
|
|
144
|
+
// @ts-expect-error Removed in v12
|
|
145
|
+
const relevantAudits = auditRef.relevantAudits;
|
|
146
|
+
if (!relevantAudits || !auditRef.acronym) continue;
|
|
147
|
+
|
|
148
|
+
for (const auditId of relevantAudits) {
|
|
149
|
+
const acronyms = metricRelevanceMap.get(auditId) || [];
|
|
150
|
+
acronyms.push(auditRef.acronym);
|
|
151
|
+
metricRelevanceMap.set(auditId, acronyms);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
for (const [auditId, acronyms] of metricRelevanceMap) {
|
|
156
|
+
if (!acronyms.length) continue;
|
|
157
|
+
|
|
158
|
+
const audit = lhr.audits[auditId];
|
|
159
|
+
if (!audit) continue;
|
|
160
|
+
|
|
161
|
+
// Old versions can still define metric savings, let's not mess with it.
|
|
162
|
+
if (audit.metricSavings) continue;
|
|
163
|
+
|
|
164
|
+
audit.metricSavings = {};
|
|
165
|
+
for (const acronym of acronyms) {
|
|
166
|
+
audit.metricSavings[acronym] = 0;
|
|
124
167
|
}
|
|
125
168
|
}
|
|
126
169
|
}
|
|
@@ -150,6 +150,9 @@ function getPageLoadError(navigationError, context) {
|
|
|
150
150
|
let finalRecord;
|
|
151
151
|
if (mainRecord) {
|
|
152
152
|
finalRecord = NetworkAnalyzer.resolveRedirects(mainRecord);
|
|
153
|
+
} else {
|
|
154
|
+
// We have no network requests to process, use the navError
|
|
155
|
+
return navigationError;
|
|
153
156
|
}
|
|
154
157
|
|
|
155
158
|
if (finalRecord?.mimeType === XHTML_MIME_TYPE) {
|