lighthouse 11.1.0-dev.20231003 → 11.1.0-dev.20231005

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.
Files changed (53) hide show
  1. package/core/audits/audit.js +17 -2
  2. package/core/audits/bf-cache.js +1 -0
  3. package/core/audits/bootup-time.js +4 -1
  4. package/core/audits/byte-efficiency/byte-efficiency-audit.js +1 -1
  5. package/core/audits/byte-efficiency/duplicated-javascript.js +1 -1
  6. package/core/audits/byte-efficiency/efficient-animated-content.js +1 -1
  7. package/core/audits/byte-efficiency/legacy-javascript.js +1 -1
  8. package/core/audits/byte-efficiency/modern-image-formats.js +1 -1
  9. package/core/audits/byte-efficiency/offscreen-images.js +1 -1
  10. package/core/audits/byte-efficiency/render-blocking-resources.js +2 -3
  11. package/core/audits/byte-efficiency/total-byte-weight.js +3 -1
  12. package/core/audits/byte-efficiency/unminified-css.js +1 -1
  13. package/core/audits/byte-efficiency/unminified-javascript.js +1 -1
  14. package/core/audits/byte-efficiency/unused-css-rules.js +1 -1
  15. package/core/audits/byte-efficiency/unused-javascript.js +1 -1
  16. package/core/audits/byte-efficiency/uses-long-cache-ttl.js +2 -7
  17. package/core/audits/byte-efficiency/uses-optimized-images.js +1 -1
  18. package/core/audits/byte-efficiency/uses-responsive-images-snapshot.js +1 -0
  19. package/core/audits/byte-efficiency/uses-responsive-images.js +1 -1
  20. package/core/audits/byte-efficiency/uses-text-compression.js +1 -1
  21. package/core/audits/dobetterweb/dom-size.js +3 -1
  22. package/core/audits/dobetterweb/no-document-write.js +1 -0
  23. package/core/audits/dobetterweb/uses-http2.js +2 -3
  24. package/core/audits/dobetterweb/uses-passive-event-listeners.js +1 -0
  25. package/core/audits/font-display.js +1 -0
  26. package/core/audits/largest-contentful-paint-element.js +3 -2
  27. package/core/audits/layout-shift-elements.js +8 -4
  28. package/core/audits/lcp-lazy-loaded.js +1 -0
  29. package/core/audits/long-tasks.js +8 -1
  30. package/core/audits/mainthread-work-breakdown.js +3 -1
  31. package/core/audits/prioritize-lcp-image.js +2 -3
  32. package/core/audits/redirects.js +2 -3
  33. package/core/audits/server-response-time.js +2 -0
  34. package/core/audits/third-party-facades.js +1 -0
  35. package/core/audits/third-party-summary.js +5 -1
  36. package/core/audits/unsized-images.js +1 -1
  37. package/core/audits/uses-rel-preconnect.js +2 -3
  38. package/core/audits/uses-rel-preload.js +2 -3
  39. package/core/audits/viewport.js +1 -0
  40. package/core/audits/work-during-interaction.js +6 -2
  41. package/core/gather/session.js +2 -0
  42. package/dist/report/bundle.esm.js +1 -1
  43. package/dist/report/flow.js +15 -16
  44. package/dist/report/standalone.js +1 -1
  45. package/package.json +6 -6
  46. package/readme.md +1 -1
  47. package/report/renderer/performance-category-renderer.js +15 -11
  48. package/shared/localization/format.d.ts +21 -16
  49. package/shared/localization/format.js +73 -36
  50. package/shared/type-verifiers.d.ts +1 -1
  51. package/types/audit.d.ts +2 -0
  52. package/types/internal/rxjs.d.ts +3 -0
  53. package/types/lhr/audit-result.d.ts +7 -0
@@ -49,6 +49,7 @@ class Audit {
49
49
  static get SCORING_MODES() {
50
50
  return {
51
51
  NUMERIC: 'numeric',
52
+ METRIC_SAVINGS: 'metricSavings',
52
53
  BINARY: 'binary',
53
54
  MANUAL: 'manual',
54
55
  INFORMATIVE: 'informative',
@@ -321,7 +322,8 @@ class Audit {
321
322
  */
322
323
  static _normalizeAuditScore(score, scoreDisplayMode, auditId) {
323
324
  if (scoreDisplayMode !== Audit.SCORING_MODES.BINARY &&
324
- scoreDisplayMode !== Audit.SCORING_MODES.NUMERIC) {
325
+ scoreDisplayMode !== Audit.SCORING_MODES.NUMERIC &&
326
+ scoreDisplayMode !== Audit.SCORING_MODES.METRIC_SAVINGS) {
325
327
  return null;
326
328
  }
327
329
 
@@ -363,6 +365,7 @@ class Audit {
363
365
 
364
366
  // Default to binary scoring.
365
367
  let scoreDisplayMode = audit.meta.scoreDisplayMode || Audit.SCORING_MODES.BINARY;
368
+ let score = product.score;
366
369
 
367
370
  // But override if product contents require it.
368
371
  if (product.errorMessage !== undefined) {
@@ -371,9 +374,21 @@ class Audit {
371
374
  } else if (product.notApplicable) {
372
375
  // Audit was determined to not apply to the page.
373
376
  scoreDisplayMode = Audit.SCORING_MODES.NOT_APPLICABLE;
377
+ } else if (product.scoreDisplayMode) {
378
+ scoreDisplayMode = product.scoreDisplayMode;
374
379
  }
375
380
 
376
- const score = Audit._normalizeAuditScore(product.score, scoreDisplayMode, audit.meta.id);
381
+ if (scoreDisplayMode === Audit.SCORING_MODES.METRIC_SAVINGS) {
382
+ if (score && score >= Util.PASS_THRESHOLD) {
383
+ score = 1;
384
+ } else if (Object.values(product.metricSavings || {}).some(v => v)) {
385
+ score = 0;
386
+ } else {
387
+ score = 0.5;
388
+ }
389
+ }
390
+
391
+ score = Audit._normalizeAuditScore(score, scoreDisplayMode, audit.meta.id);
377
392
 
378
393
  let auditTitle = audit.meta.title;
379
394
  if (audit.meta.failureTitle) {
@@ -61,6 +61,7 @@ class BFCache extends Audit {
61
61
  supportedModes: ['navigation', 'timespan'],
62
62
  guidanceLevel: 2,
63
63
  requiredArtifacts: ['BFCacheFailures'],
64
+ scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS,
64
65
  };
65
66
  }
66
67
 
@@ -14,6 +14,7 @@ import {MainThreadTasks} from '../computed/main-thread-tasks.js';
14
14
  import {getExecutionTimingsByURL} from '../lib/tracehouse/task-summary.js';
15
15
  import {TBTImpactTasks} from '../computed/tbt-impact-tasks.js';
16
16
  import {Sentry} from '../lib/sentry.js';
17
+ import {Util} from '../../shared/util.js';
17
18
 
18
19
  const UIStrings = {
19
20
  /** Title of a diagnostic audit that provides detail on the time spent executing javascript files during the load. This descriptive title is shown to users when the amount is acceptable and no user action is required. */
@@ -47,7 +48,7 @@ class BootupTime extends Audit {
47
48
  title: str_(UIStrings.title),
48
49
  failureTitle: str_(UIStrings.failureTitle),
49
50
  description: str_(UIStrings.description),
50
- scoreDisplayMode: Audit.SCORING_MODES.NUMERIC,
51
+ scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS,
51
52
  guidanceLevel: 1,
52
53
  requiredArtifacts: ['traces', 'devtoolsLogs', 'URL', 'GatherContext'],
53
54
  };
@@ -169,6 +170,8 @@ class BootupTime extends Audit {
169
170
 
170
171
  return {
171
172
  score,
173
+ scoreDisplayMode: score >= Util.PASS_THRESHOLD ? Audit.SCORING_MODES.INFORMATIVE : undefined,
174
+ notApplicable: !results.length,
172
175
  numericValue: totalBootupTime,
173
176
  numericUnit: 'millisecond',
174
177
  displayValue: totalBootupTime > 0 ?
@@ -309,7 +309,7 @@ class ByteEfficiencyAudit extends Audit {
309
309
  displayValue,
310
310
  numericValue: wastedMs,
311
311
  numericUnit: 'millisecond',
312
- score: ByteEfficiencyAudit.scoreForWastedMs(wastedMs),
312
+ score: results.length ? 0 : 1,
313
313
  details,
314
314
  metricSavings,
315
315
  };
@@ -46,7 +46,7 @@ class DuplicatedJavascript extends ByteEfficiencyAudit {
46
46
  id: 'duplicated-javascript',
47
47
  title: str_(UIStrings.title),
48
48
  description: str_(UIStrings.description),
49
- scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.NUMERIC,
49
+ scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.METRIC_SAVINGS,
50
50
  guidanceLevel: 2,
51
51
  requiredArtifacts: ['devtoolsLogs', 'traces', 'SourceMaps', 'Scripts',
52
52
  'GatherContext', 'URL'],
@@ -36,7 +36,7 @@ class EfficientAnimatedContent extends ByteEfficiencyAudit {
36
36
  id: 'efficient-animated-content',
37
37
  title: str_(UIStrings.title),
38
38
  description: str_(UIStrings.description),
39
- scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.NUMERIC,
39
+ scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.METRIC_SAVINGS,
40
40
  guidanceLevel: 3,
41
41
  requiredArtifacts: ['devtoolsLogs', 'traces', 'GatherContext', 'URL'],
42
42
  };
@@ -115,7 +115,7 @@ class LegacyJavascript extends ByteEfficiencyAudit {
115
115
  static get meta() {
116
116
  return {
117
117
  id: 'legacy-javascript',
118
- scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.NUMERIC,
118
+ scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.METRIC_SAVINGS,
119
119
  description: str_(UIStrings.description),
120
120
  title: str_(UIStrings.title),
121
121
  guidanceLevel: 2,
@@ -34,7 +34,7 @@ class ModernImageFormats extends ByteEfficiencyAudit {
34
34
  id: 'modern-image-formats',
35
35
  title: str_(UIStrings.title),
36
36
  description: str_(UIStrings.description),
37
- scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.NUMERIC,
37
+ scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.METRIC_SAVINGS,
38
38
  guidanceLevel: 3,
39
39
  requiredArtifacts: ['OptimizedImages', 'devtoolsLogs', 'traces', 'URL', 'GatherContext',
40
40
  'ImageElements'],
@@ -48,7 +48,7 @@ class OffscreenImages extends ByteEfficiencyAudit {
48
48
  id: 'offscreen-images',
49
49
  title: str_(UIStrings.title),
50
50
  description: str_(UIStrings.description),
51
- scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.NUMERIC,
51
+ scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.METRIC_SAVINGS,
52
52
  supportedModes: ['navigation'],
53
53
  guidanceLevel: 2,
54
54
  requiredArtifacts: ['ImageElements', 'ViewportDimensions', 'GatherContext', 'devtoolsLogs',
@@ -12,7 +12,6 @@
12
12
  import {Audit} from '../audit.js';
13
13
  import * as i18n from '../../lib/i18n/i18n.js';
14
14
  import {BaseNode} from '../../lib/dependency-graph/base-node.js';
15
- import {ByteEfficiencyAudit} from './byte-efficiency-audit.js';
16
15
  import {UnusedCSS} from '../../computed/unused-css.js';
17
16
  import {NetworkRequest} from '../../lib/network-request.js';
18
17
  import {ProcessedNavigation} from '../../computed/processed-navigation.js';
@@ -113,7 +112,7 @@ class RenderBlockingResources extends Audit {
113
112
  id: 'render-blocking-resources',
114
113
  title: str_(UIStrings.title),
115
114
  supportedModes: ['navigation'],
116
- scoreDisplayMode: Audit.SCORING_MODES.NUMERIC,
115
+ scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS,
117
116
  description: str_(UIStrings.description),
118
117
  guidanceLevel: 2,
119
118
  // TODO: look into adding an `optionalArtifacts` property that captures the non-required nature
@@ -296,7 +295,7 @@ class RenderBlockingResources extends Audit {
296
295
 
297
296
  return {
298
297
  displayValue,
299
- score: ByteEfficiencyAudit.scoreForWastedMs(wastedMs),
298
+ score: results.length ? 0 : 1,
300
299
  numericValue: wastedMs,
301
300
  numericUnit: 'millisecond',
302
301
  details,
@@ -8,6 +8,7 @@ import {Audit} from '../audit.js';
8
8
  import * as i18n from '../../lib/i18n/i18n.js';
9
9
  import {NetworkRequest} from '../../lib/network-request.js';
10
10
  import {NetworkRecords} from '../../computed/network-records.js';
11
+ import {Util} from '../../../shared/util.js';
11
12
 
12
13
  const UIStrings = {
13
14
  /** Title of a diagnostic audit that provides detail on large network resources required during page load. 'Payloads' is roughly equivalent to 'resources'. This descriptive title is shown to users when the amount is acceptable and no user action is required. */
@@ -34,7 +35,7 @@ class TotalByteWeight extends Audit {
34
35
  title: str_(UIStrings.title),
35
36
  failureTitle: str_(UIStrings.failureTitle),
36
37
  description: str_(UIStrings.description),
37
- scoreDisplayMode: Audit.SCORING_MODES.NUMERIC,
38
+ scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS,
38
39
  guidanceLevel: 1,
39
40
  requiredArtifacts: ['devtoolsLogs'],
40
41
  };
@@ -98,6 +99,7 @@ class TotalByteWeight extends Audit {
98
99
 
99
100
  return {
100
101
  score,
102
+ scoreDisplayMode: score >= Util.PASS_THRESHOLD ? Audit.SCORING_MODES.INFORMATIVE : undefined,
101
103
  numericValue: totalBytes,
102
104
  numericUnit: 'byte',
103
105
  displayValue: str_(UIStrings.displayValue, {totalBytes}),
@@ -34,7 +34,7 @@ class UnminifiedCSS extends ByteEfficiencyAudit {
34
34
  id: 'unminified-css',
35
35
  title: str_(UIStrings.title),
36
36
  description: str_(UIStrings.description),
37
- scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.NUMERIC,
37
+ scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.METRIC_SAVINGS,
38
38
  guidanceLevel: 3,
39
39
  requiredArtifacts: ['CSSUsage', 'devtoolsLogs', 'traces', 'URL', 'GatherContext'],
40
40
  };
@@ -42,7 +42,7 @@ class UnminifiedJavaScript extends ByteEfficiencyAudit {
42
42
  id: 'unminified-javascript',
43
43
  title: str_(UIStrings.title),
44
44
  description: str_(UIStrings.description),
45
- scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.NUMERIC,
45
+ scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.METRIC_SAVINGS,
46
46
  guidanceLevel: 3,
47
47
  requiredArtifacts: ['Scripts', 'devtoolsLogs', 'traces', 'GatherContext', 'URL'],
48
48
  };
@@ -32,7 +32,7 @@ class UnusedCSSRules extends ByteEfficiencyAudit {
32
32
  id: 'unused-css-rules',
33
33
  title: str_(UIStrings.title),
34
34
  description: str_(UIStrings.description),
35
- scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.NUMERIC,
35
+ scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.METRIC_SAVINGS,
36
36
  guidanceLevel: 2,
37
37
  requiredArtifacts: ['CSSUsage', 'URL', 'devtoolsLogs', 'traces', 'GatherContext'],
38
38
  };
@@ -66,7 +66,7 @@ class UnusedJavaScript extends ByteEfficiencyAudit {
66
66
  id: 'unused-javascript',
67
67
  title: str_(UIStrings.title),
68
68
  description: str_(UIStrings.description),
69
- scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.NUMERIC,
69
+ scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.METRIC_SAVINGS,
70
70
  guidanceLevel: 2,
71
71
  requiredArtifacts: ['JsUsage', 'Scripts', 'SourceMaps', 'GatherContext',
72
72
  'devtoolsLogs', 'traces', 'URL'],
@@ -44,7 +44,7 @@ class CacheHeaders extends Audit {
44
44
  title: str_(UIStrings.title),
45
45
  failureTitle: str_(UIStrings.failureTitle),
46
46
  description: str_(UIStrings.description),
47
- scoreDisplayMode: Audit.SCORING_MODES.NUMERIC,
47
+ scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS,
48
48
  guidanceLevel: 3,
49
49
  requiredArtifacts: ['devtoolsLogs'],
50
50
  };
@@ -266,11 +266,6 @@ class CacheHeaders extends Audit {
266
266
  a.url.localeCompare(b.url);
267
267
  });
268
268
 
269
- const score = Audit.computeLogNormalScore(
270
- {p10: context.options.p10, median: context.options.median},
271
- totalWastedBytes
272
- );
273
-
274
269
  /** @type {LH.Audit.Details.Table['headings']} */
275
270
  const headings = [
276
271
  {key: 'url', valueType: 'url', label: str_(i18n.UIStrings.columnURL)},
@@ -285,7 +280,7 @@ class CacheHeaders extends Audit {
285
280
  {wastedBytes: totalWastedBytes, sortedBy: ['totalBytes'], skipSumming: ['cacheLifetimeMs']});
286
281
 
287
282
  return {
288
- score,
283
+ score: results.length ? 0 : 1,
289
284
  numericValue: totalWastedBytes,
290
285
  numericUnit: 'byte',
291
286
  displayValue: str_(UIStrings.displayValue, {itemCount: results.length}),
@@ -34,7 +34,7 @@ class UsesOptimizedImages extends ByteEfficiencyAudit {
34
34
  id: 'uses-optimized-images',
35
35
  title: str_(UIStrings.title),
36
36
  description: str_(UIStrings.description),
37
- scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.NUMERIC,
37
+ scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.METRIC_SAVINGS,
38
38
  guidanceLevel: 2,
39
39
  requiredArtifacts: ['OptimizedImages', 'ImageElements', 'GatherContext', 'devtoolsLogs',
40
40
  'traces', 'URL'],
@@ -42,6 +42,7 @@ class UsesResponsiveImagesSnapshot extends Audit {
42
42
  title: str_(UIStrings.title),
43
43
  failureTitle: str_(UIStrings.failureTitle),
44
44
  description: UsesResponsiveImages.str_(UsesResponsiveImages.UIStrings.description),
45
+ scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS,
45
46
  supportedModes: ['snapshot'],
46
47
  guidanceLevel: 2,
47
48
  requiredArtifacts: ['ImageElements', 'ViewportDimensions'],
@@ -45,7 +45,7 @@ class UsesResponsiveImages extends ByteEfficiencyAudit {
45
45
  id: 'uses-responsive-images',
46
46
  title: str_(UIStrings.title),
47
47
  description: str_(UIStrings.description),
48
- scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.NUMERIC,
48
+ scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.METRIC_SAVINGS,
49
49
  guidanceLevel: 2,
50
50
  requiredArtifacts: ['ImageElements', 'ViewportDimensions', 'GatherContext',
51
51
  'devtoolsLogs', 'traces', 'URL'],
@@ -36,7 +36,7 @@ class ResponsesAreCompressed extends ByteEfficiencyAudit {
36
36
  id: 'uses-text-compression',
37
37
  title: str_(UIStrings.title),
38
38
  description: str_(UIStrings.description),
39
- scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.NUMERIC,
39
+ scoreDisplayMode: ByteEfficiencyAudit.SCORING_MODES.METRIC_SAVINGS,
40
40
  guidanceLevel: 3,
41
41
  requiredArtifacts: ['ResponseCompression', 'GatherContext', 'devtoolsLogs', 'traces', 'URL'],
42
42
  };
@@ -14,6 +14,7 @@
14
14
  import {Audit} from '../audit.js';
15
15
  import * as i18n from '../../lib/i18n/i18n.js';
16
16
  import {TBTImpactTasks} from '../../computed/tbt-impact-tasks.js';
17
+ import {Util} from '../../../shared/util.js';
17
18
 
18
19
  const UIStrings = {
19
20
  /** Title of a diagnostic audit that provides detail on the size of the web page's DOM. The size of a DOM is characterized by the total number of DOM elements and greatest DOM depth. This descriptive title is shown to users when the amount is acceptable and no user action is required. */
@@ -53,7 +54,7 @@ class DOMSize extends Audit {
53
54
  title: str_(UIStrings.title),
54
55
  failureTitle: str_(UIStrings.failureTitle),
55
56
  description: str_(UIStrings.description),
56
- scoreDisplayMode: Audit.SCORING_MODES.NUMERIC,
57
+ scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS,
57
58
  guidanceLevel: 1,
58
59
  requiredArtifacts: ['DOMStats', 'URL', 'GatherContext'],
59
60
  __internalOptionalArtifacts: ['traces', 'devtoolsLogs'],
@@ -167,6 +168,7 @@ class DOMSize extends Audit {
167
168
 
168
169
  return {
169
170
  score,
171
+ scoreDisplayMode: score >= Util.PASS_THRESHOLD ? Audit.SCORING_MODES.INFORMATIVE : undefined,
170
172
  numericValue: stats.totalBodyElements,
171
173
  numericUnit: 'element',
172
174
  displayValue: str_(UIStrings.displayValue, {itemCount: stats.totalBodyElements}),
@@ -55,6 +55,7 @@ class NoDocWriteAudit extends ViolationAudit {
55
55
  description: str_(UIStrings.description),
56
56
  guidanceLevel: 2,
57
57
  requiredArtifacts: ['ConsoleMessages', 'SourceMaps', 'Scripts'],
58
+ scoreDisplayMode: ViolationAudit.SCORING_MODES.METRIC_SAVINGS,
58
59
  };
59
60
  }
60
61
 
@@ -15,7 +15,6 @@
15
15
  import {Audit} from '../audit.js';
16
16
  import {EntityClassification} from '../../computed/entity-classification.js';
17
17
  import UrlUtils from '../../lib/url-utils.js';
18
- import {ByteEfficiencyAudit} from '../byte-efficiency/byte-efficiency-audit.js';
19
18
  import {LanternInteractive} from '../../computed/metrics/lantern-interactive.js';
20
19
  import {NetworkRequest} from '../../lib/network-request.js';
21
20
  import {NetworkRecords} from '../../computed/network-records.js';
@@ -61,7 +60,7 @@ class UsesHTTP2Audit extends Audit {
61
60
  id: 'uses-http2',
62
61
  title: str_(UIStrings.title),
63
62
  description: str_(UIStrings.description),
64
- scoreDisplayMode: Audit.SCORING_MODES.NUMERIC,
63
+ scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS,
65
64
  guidanceLevel: 3,
66
65
  supportedModes: ['timespan', 'navigation'],
67
66
  requiredArtifacts: ['URL', 'devtoolsLogs', 'traces', 'GatherContext'],
@@ -300,7 +299,7 @@ class UsesHTTP2Audit extends Audit {
300
299
  displayValue,
301
300
  numericValue: wastedMsTti,
302
301
  numericUnit: 'millisecond',
303
- score: ByteEfficiencyAudit.scoreForWastedMs(wastedMsTti),
302
+ score: resources.length ? 0 : 1,
304
303
  details,
305
304
  metricSavings: {LCP: wasteLcp.savings, FCP: wasteFcp.savings},
306
305
  };
@@ -38,6 +38,7 @@ class PassiveEventsAudit extends ViolationAudit {
38
38
  description: str_(UIStrings.description),
39
39
  guidanceLevel: 3,
40
40
  requiredArtifacts: ['ConsoleMessages', 'SourceMaps', 'Scripts'],
41
+ scoreDisplayMode: ViolationAudit.SCORING_MODES.METRIC_SAVINGS,
41
42
  };
42
43
  }
43
44
 
@@ -51,6 +51,7 @@ class FontDisplay extends Audit {
51
51
  supportedModes: ['navigation'],
52
52
  guidanceLevel: 3,
53
53
  requiredArtifacts: ['devtoolsLogs', 'CSSUsage', 'URL'],
54
+ scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS,
54
55
  };
55
56
  }
56
57
 
@@ -46,7 +46,7 @@ class LargestContentfulPaintElement extends Audit {
46
46
  id: 'largest-contentful-paint-element',
47
47
  title: str_(UIStrings.title),
48
48
  description: str_(UIStrings.description),
49
- scoreDisplayMode: Audit.SCORING_MODES.INFORMATIVE,
49
+ scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS,
50
50
  guidanceLevel: 1,
51
51
  supportedModes: ['navigation'],
52
52
  requiredArtifacts:
@@ -162,7 +162,8 @@ class LargestContentfulPaintElement extends Audit {
162
162
  const lcpSavings = Math.max(0, metricLcp - idealLcp);
163
163
 
164
164
  return {
165
- score: 1,
165
+ score: lcpSavings ? 0 : 1,
166
+ scoreDisplayMode: lcpSavings ? undefined : Audit.SCORING_MODES.INFORMATIVE,
166
167
  displayValue,
167
168
  details,
168
169
  metricSavings: {
@@ -5,7 +5,8 @@
5
5
 
6
6
  import {Audit} from './audit.js';
7
7
  import * as i18n from '../lib/i18n/i18n.js';
8
- import {CumulativeLayoutShift} from '../computed/metrics/cumulative-layout-shift.js';
8
+ import {CumulativeLayoutShift as CumulativeLayoutShiftComputed} from '../computed/metrics/cumulative-layout-shift.js';
9
+ import CumulativeLayoutShift from './metrics/cumulative-layout-shift.js';
9
10
 
10
11
  const UIStrings = {
11
12
  /** Descriptive title of a diagnostic audit that provides up to the top five elements contributing to Cumulative Layout Shift. */
@@ -27,7 +28,7 @@ class LayoutShiftElements extends Audit {
27
28
  id: 'layout-shift-elements',
28
29
  title: str_(UIStrings.title),
29
30
  description: str_(UIStrings.description),
30
- scoreDisplayMode: Audit.SCORING_MODES.INFORMATIVE,
31
+ scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS,
31
32
  guidanceLevel: 2,
32
33
  requiredArtifacts: ['traces', 'TraceElements'],
33
34
  };
@@ -64,10 +65,13 @@ class LayoutShiftElements extends Audit {
64
65
  }
65
66
 
66
67
  const {cumulativeLayoutShift: clsSavings} =
67
- await CumulativeLayoutShift.request(artifacts.traces[Audit.DEFAULT_PASS], context);
68
+ await CumulativeLayoutShiftComputed.request(artifacts.traces[Audit.DEFAULT_PASS], context);
69
+
70
+ const passed = clsSavings <= CumulativeLayoutShift.defaultOptions.p10;
68
71
 
69
72
  return {
70
- score: 1,
73
+ score: passed ? 1 : 0,
74
+ scoreDisplayMode: passed ? Audit.SCORING_MODES.INFORMATIVE : undefined,
71
75
  metricSavings: {
72
76
  CLS: clsSavings,
73
77
  },
@@ -33,6 +33,7 @@ class LargestContentfulPaintLazyLoaded extends Audit {
33
33
  failureTitle: str_(UIStrings.failureTitle),
34
34
  description: str_(UIStrings.description),
35
35
  supportedModes: ['navigation'],
36
+ scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS,
36
37
  guidanceLevel: 3,
37
38
  requiredArtifacts: ['TraceElements', 'ViewportDimensions', 'ImageElements',
38
39
  'traces', 'devtoolsLogs', 'GatherContext', 'URL'],
@@ -11,6 +11,7 @@ import {MainThreadTasks} from '../computed/main-thread-tasks.js';
11
11
  import {PageDependencyGraph} from '../computed/page-dependency-graph.js';
12
12
  import {LoadSimulator} from '../computed/load-simulator.js';
13
13
  import {getJavaScriptURLs, getAttributableURLForTask} from '../lib/tracehouse/task-summary.js';
14
+ import {TotalBlockingTime} from '../computed/metrics/total-blocking-time.js';
14
15
 
15
16
  /** We don't always have timing data for short tasks, if we're missing timing data. Treat it as though it were 0ms. */
16
17
  const DEFAULT_TIMING = {startTime: 0, endTime: 0, duration: 0};
@@ -66,8 +67,8 @@ class LongTasks extends Audit {
66
67
  scoreDisplayMode: Audit.SCORING_MODES.INFORMATIVE,
67
68
  title: str_(UIStrings.title),
68
69
  description: str_(UIStrings.description),
70
+ requiredArtifacts: ['traces', 'devtoolsLogs', 'URL', 'GatherContext'],
69
71
  guidanceLevel: 1,
70
- requiredArtifacts: ['traces', 'devtoolsLogs', 'URL'],
71
72
  };
72
73
  }
73
74
 
@@ -180,6 +181,9 @@ class LongTasks extends Audit {
180
181
  const devtoolsLog = artifacts.devtoolsLogs[LongTasks.DEFAULT_PASS];
181
182
  const networkRecords = await NetworkRecords.request(devtoolsLog, context);
182
183
 
184
+ const metricComputationData = Audit.makeMetricComputationDataInput(artifacts, context);
185
+ const tbtResult = await TotalBlockingTime.request(metricComputationData, context);
186
+
183
187
  /** @type {Map<LH.TraceEvent, LH.Gatherer.Simulation.NodeTiming>|undefined} */
184
188
  let taskTimingsByEvent;
185
189
 
@@ -245,6 +249,9 @@ class LongTasks extends Audit {
245
249
  notApplicable: results.length === 0,
246
250
  details: tableDetails,
247
251
  displayValue,
252
+ metricSavings: {
253
+ TBT: tbtResult.timing,
254
+ },
248
255
  };
249
256
  }
250
257
  }
@@ -16,6 +16,7 @@ import * as i18n from '../lib/i18n/i18n.js';
16
16
  import {MainThreadTasks} from '../computed/main-thread-tasks.js';
17
17
  import {TotalBlockingTime} from '../computed/metrics/total-blocking-time.js';
18
18
  import {Sentry} from '../lib/sentry.js';
19
+ import {Util} from '../../shared/util.js';
19
20
 
20
21
  const UIStrings = {
21
22
  /** Title of a diagnostic audit that provides detail on the main thread work the browser did to load the page. This descriptive title is shown to users when the amount is acceptable and no user action is required. */
@@ -44,7 +45,7 @@ class MainThreadWorkBreakdown extends Audit {
44
45
  title: str_(UIStrings.title),
45
46
  failureTitle: str_(UIStrings.failureTitle),
46
47
  description: str_(UIStrings.description),
47
- scoreDisplayMode: Audit.SCORING_MODES.NUMERIC,
48
+ scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS,
48
49
  guidanceLevel: 1,
49
50
  requiredArtifacts: ['traces', 'devtoolsLogs', 'URL', 'GatherContext'],
50
51
  };
@@ -141,6 +142,7 @@ class MainThreadWorkBreakdown extends Audit {
141
142
 
142
143
  return {
143
144
  score,
145
+ scoreDisplayMode: score >= Util.PASS_THRESHOLD ? Audit.SCORING_MODES.INFORMATIVE : undefined,
144
146
  numericValue: totalExecutionTime,
145
147
  numericUnit: 'millisecond',
146
148
  displayValue: str_(i18n.UIStrings.seconds, {timeInMs: totalExecutionTime}),
@@ -10,7 +10,6 @@ import {NetworkRequest} from '../lib/network-request.js';
10
10
  import {MainResource} from '../computed/main-resource.js';
11
11
  import {LanternLargestContentfulPaint} from '../computed/metrics/lantern-largest-contentful-paint.js';
12
12
  import {LoadSimulator} from '../computed/load-simulator.js';
13
- import {ByteEfficiencyAudit} from './byte-efficiency/byte-efficiency-audit.js';
14
13
  import {LCPImageRecord} from '../computed/lcp-image-record.js';
15
14
 
16
15
  const UIStrings = {
@@ -40,7 +39,7 @@ class PrioritizeLcpImage extends Audit {
40
39
  supportedModes: ['navigation'],
41
40
  guidanceLevel: 3,
42
41
  requiredArtifacts: ['traces', 'devtoolsLogs', 'GatherContext', 'URL', 'TraceElements'],
43
- scoreDisplayMode: Audit.SCORING_MODES.NUMERIC,
42
+ scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS,
44
43
  };
45
44
  }
46
45
 
@@ -282,7 +281,7 @@ class PrioritizeLcpImage extends Audit {
282
281
  }
283
282
 
284
283
  return {
285
- score: ByteEfficiencyAudit.scoreForWastedMs(wastedMs),
284
+ score: results.length ? 0 : 1,
286
285
  numericValue: wastedMs,
287
286
  numericUnit: 'millisecond',
288
287
  displayValue: wastedMs ? str_(i18n.UIStrings.displayValueMsSavings, {wastedMs}) : '',
@@ -5,7 +5,6 @@
5
5
  */
6
6
 
7
7
  import {Audit} from './audit.js';
8
- import {ByteEfficiencyAudit} from './byte-efficiency/byte-efficiency-audit.js';
9
8
  import * as i18n from '../lib/i18n/i18n.js';
10
9
  import {ProcessedTrace} from '../computed/processed-trace.js';
11
10
  import {NetworkRecords} from '../computed/network-records.js';
@@ -29,7 +28,7 @@ class Redirects extends Audit {
29
28
  id: 'redirects',
30
29
  title: str_(UIStrings.title),
31
30
  description: str_(UIStrings.description),
32
- scoreDisplayMode: Audit.SCORING_MODES.NUMERIC,
31
+ scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS,
33
32
  supportedModes: ['navigation'],
34
33
  guidanceLevel: 2,
35
34
  requiredArtifacts: ['URL', 'GatherContext', 'devtoolsLogs', 'traces'],
@@ -145,7 +144,7 @@ class Redirects extends Audit {
145
144
  {overallSavingsMs: totalWastedMs});
146
145
 
147
146
  return {
148
- score: ByteEfficiencyAudit.scoreForWastedMs(totalWastedMs),
147
+ score: tableRows.length ? 0 : 1,
149
148
  numericValue: totalWastedMs,
150
149
  numericUnit: 'millisecond',
151
150
  displayValue: totalWastedMs ?
@@ -39,6 +39,7 @@ class ServerResponseTime extends Audit {
39
39
  supportedModes: ['navigation'],
40
40
  guidanceLevel: 1,
41
41
  requiredArtifacts: ['devtoolsLogs', 'URL', 'GatherContext'],
42
+ scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS,
42
43
  };
43
44
  }
44
45
 
@@ -91,6 +92,7 @@ class ServerResponseTime extends Audit {
91
92
  numericValue: responseTime,
92
93
  numericUnit: 'millisecond',
93
94
  score: Number(passed),
95
+ scoreDisplayMode: passed ? Audit.SCORING_MODES.INFORMATIVE : undefined,
94
96
  displayValue,
95
97
  details,
96
98
  metricSavings: {
@@ -88,6 +88,7 @@ class ThirdPartyFacades extends Audit {
88
88
  supportedModes: ['navigation'],
89
89
  guidanceLevel: 3,
90
90
  requiredArtifacts: ['traces', 'devtoolsLogs', 'URL', 'GatherContext'],
91
+ scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS,
91
92
  };
92
93
  }
93
94
 
@@ -75,6 +75,7 @@ class ThirdPartySummary extends Audit {
75
75
  failureTitle: str_(UIStrings.failureTitle),
76
76
  description: str_(UIStrings.description),
77
77
  guidanceLevel: 1,
78
+ scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS,
78
79
  requiredArtifacts: ['traces', 'devtoolsLogs', 'URL', 'GatherContext'],
79
80
  };
80
81
  }
@@ -257,8 +258,11 @@ class ThirdPartySummary extends Audit {
257
258
  const details = Audit.makeTableDetails(headings, results,
258
259
  {...overallSummary, isEntityGrouped: true});
259
260
 
261
+ const passed = overallSummary.wastedMs <= PASS_THRESHOLD_IN_MS;
262
+
260
263
  return {
261
- score: Number(overallSummary.wastedMs <= PASS_THRESHOLD_IN_MS),
264
+ score: Number(passed),
265
+ scoreDisplayMode: passed ? Audit.SCORING_MODES.INFORMATIVE : undefined,
262
266
  displayValue: str_(UIStrings.displayValue, {
263
267
  timeInMs: overallSummary.wastedMs,
264
268
  }),
@@ -36,6 +36,7 @@ class UnsizedImages extends Audit {
36
36
  description: str_(UIStrings.description),
37
37
  guidanceLevel: 3,
38
38
  requiredArtifacts: ['ImageElements'],
39
+ scoreDisplayMode: Audit.SCORING_MODES.METRIC_SAVINGS,
39
40
  };
40
41
  }
41
42
 
@@ -153,7 +154,6 @@ class UnsizedImages extends Audit {
153
154
 
154
155
  return {
155
156
  score: unsizedImages.length > 0 ? 0 : 1,
156
- notApplicable: images.length === 0,
157
157
  details: Audit.makeTableDetails(headings, unsizedImages),
158
158
  metricSavings: {
159
159
  CLS: 0,