lighthouse 9.5.0-dev.20220607 → 9.6.2

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 (262) hide show
  1. package/dist/report/bundle.esm.js +467 -520
  2. package/dist/report/flow.js +22 -22
  3. package/dist/report/standalone.js +19 -19
  4. package/flow-report/src/summary/category.tsx +1 -1
  5. package/flow-report/src/topbar.tsx +2 -10
  6. package/flow-report/src/util.ts +1 -1
  7. package/flow-report/test/topbar-test.tsx +8 -7
  8. package/jest.config.js +1 -1
  9. package/lighthouse-cli/.eslintrc.cjs +4 -3
  10. package/lighthouse-cli/bin.js +1 -0
  11. package/lighthouse-cli/cli-flags.js +4 -3
  12. package/lighthouse-cli/commands/commands.js +1 -0
  13. package/lighthouse-cli/commands/list-audits.js +1 -0
  14. package/lighthouse-cli/commands/list-locales.js +1 -0
  15. package/lighthouse-cli/commands/list-trace-categories.js +1 -0
  16. package/lighthouse-cli/index.js +1 -0
  17. package/lighthouse-cli/printer.js +1 -0
  18. package/lighthouse-cli/run.js +24 -9
  19. package/lighthouse-cli/sentry-prompt.js +1 -0
  20. package/lighthouse-cli/test/smokehouse/core-tests.js +1 -0
  21. package/lighthouse-cli/test/smokehouse/frontends/back-compat-util.js +1 -0
  22. package/lighthouse-cli/test/smokehouse/frontends/lib.js +1 -0
  23. package/lighthouse-cli/test/smokehouse/frontends/node.js +1 -0
  24. package/lighthouse-cli/test/smokehouse/frontends/smokehouse-bin.js +1 -0
  25. package/lighthouse-cli/test/smokehouse/lib/child-process-error.js +1 -0
  26. package/lighthouse-cli/test/smokehouse/lib/concurrent-mapper.js +1 -0
  27. package/lighthouse-cli/test/smokehouse/lib/local-console.js +1 -0
  28. package/lighthouse-cli/test/smokehouse/lighthouse-runners/bundle.js +23 -109
  29. package/lighthouse-cli/test/smokehouse/lighthouse-runners/cli.js +3 -2
  30. package/lighthouse-cli/test/smokehouse/lighthouse-runners/devtools.js +19 -28
  31. package/lighthouse-cli/test/smokehouse/report-assert.js +4 -3
  32. package/lighthouse-cli/test/smokehouse/smokehouse.js +1 -1
  33. package/lighthouse-cli/test/smokehouse/version-check-test.js +3 -0
  34. package/lighthouse-cli/test/smokehouse/version-check.js +1 -0
  35. package/lighthouse-core/audits/accessibility/accesskeys.js +1 -1
  36. package/lighthouse-core/audits/accessibility/aria-allowed-attr.js +1 -1
  37. package/lighthouse-core/audits/accessibility/aria-command-name.js +1 -1
  38. package/lighthouse-core/audits/accessibility/aria-hidden-body.js +1 -1
  39. package/lighthouse-core/audits/accessibility/aria-hidden-focus.js +1 -1
  40. package/lighthouse-core/audits/accessibility/aria-input-field-name.js +1 -1
  41. package/lighthouse-core/audits/accessibility/aria-meter-name.js +1 -1
  42. package/lighthouse-core/audits/accessibility/aria-progressbar-name.js +1 -1
  43. package/lighthouse-core/audits/accessibility/aria-required-attr.js +1 -1
  44. package/lighthouse-core/audits/accessibility/aria-required-children.js +1 -1
  45. package/lighthouse-core/audits/accessibility/aria-required-parent.js +1 -1
  46. package/lighthouse-core/audits/accessibility/aria-roles.js +1 -1
  47. package/lighthouse-core/audits/accessibility/aria-toggle-field-name.js +1 -1
  48. package/lighthouse-core/audits/accessibility/aria-tooltip-name.js +1 -1
  49. package/lighthouse-core/audits/accessibility/aria-treeitem-name.js +1 -1
  50. package/lighthouse-core/audits/accessibility/aria-valid-attr-value.js +1 -1
  51. package/lighthouse-core/audits/accessibility/aria-valid-attr.js +1 -1
  52. package/lighthouse-core/audits/accessibility/button-name.js +1 -1
  53. package/lighthouse-core/audits/accessibility/bypass.js +1 -1
  54. package/lighthouse-core/audits/accessibility/color-contrast.js +1 -1
  55. package/lighthouse-core/audits/accessibility/definition-list.js +1 -1
  56. package/lighthouse-core/audits/accessibility/dlitem.js +1 -1
  57. package/lighthouse-core/audits/accessibility/document-title.js +1 -1
  58. package/lighthouse-core/audits/accessibility/duplicate-id-active.js +1 -1
  59. package/lighthouse-core/audits/accessibility/duplicate-id-aria.js +1 -1
  60. package/lighthouse-core/audits/accessibility/form-field-multiple-labels.js +1 -1
  61. package/lighthouse-core/audits/accessibility/frame-title.js +1 -1
  62. package/lighthouse-core/audits/accessibility/heading-order.js +1 -1
  63. package/lighthouse-core/audits/accessibility/html-has-lang.js +1 -1
  64. package/lighthouse-core/audits/accessibility/html-lang-valid.js +1 -1
  65. package/lighthouse-core/audits/accessibility/image-alt.js +1 -1
  66. package/lighthouse-core/audits/accessibility/input-image-alt.js +1 -1
  67. package/lighthouse-core/audits/accessibility/label.js +1 -1
  68. package/lighthouse-core/audits/accessibility/link-name.js +1 -1
  69. package/lighthouse-core/audits/accessibility/list.js +1 -1
  70. package/lighthouse-core/audits/accessibility/listitem.js +5 -5
  71. package/lighthouse-core/audits/accessibility/meta-refresh.js +1 -1
  72. package/lighthouse-core/audits/accessibility/meta-viewport.js +1 -1
  73. package/lighthouse-core/audits/accessibility/object-alt.js +1 -1
  74. package/lighthouse-core/audits/accessibility/tabindex.js +1 -1
  75. package/lighthouse-core/audits/accessibility/td-headers-attr.js +1 -1
  76. package/lighthouse-core/audits/accessibility/th-has-data-cells.js +1 -1
  77. package/lighthouse-core/audits/accessibility/valid-lang.js +1 -1
  78. package/lighthouse-core/audits/accessibility/video-caption.js +1 -1
  79. package/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js +1 -2
  80. package/lighthouse-core/audits/byte-efficiency/duplicated-javascript.js +12 -11
  81. package/lighthouse-core/audits/byte-efficiency/efficient-animated-content.js +1 -1
  82. package/lighthouse-core/audits/byte-efficiency/legacy-javascript.js +24 -22
  83. package/lighthouse-core/audits/byte-efficiency/offscreen-images.js +2 -3
  84. package/lighthouse-core/audits/byte-efficiency/render-blocking-resources.js +3 -2
  85. package/lighthouse-core/audits/byte-efficiency/unminified-css.js +5 -3
  86. package/lighthouse-core/audits/byte-efficiency/unminified-javascript.js +11 -10
  87. package/lighthouse-core/audits/byte-efficiency/unused-css-rules.js +1 -0
  88. package/lighthouse-core/audits/byte-efficiency/unused-javascript.js +8 -11
  89. package/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js +1 -1
  90. package/lighthouse-core/audits/byte-efficiency/uses-text-compression.js +1 -1
  91. package/lighthouse-core/audits/deprecations.js +598 -8
  92. package/lighthouse-core/audits/diagnostics.js +3 -4
  93. package/lighthouse-core/audits/dobetterweb/doctype.js +9 -20
  94. package/lighthouse-core/audits/dobetterweb/geolocation-on-start.js +1 -1
  95. package/lighthouse-core/audits/dobetterweb/no-document-write.js +1 -1
  96. package/lighthouse-core/audits/dobetterweb/notification-on-start.js +1 -1
  97. package/lighthouse-core/audits/dobetterweb/uses-http2.js +1 -2
  98. package/lighthouse-core/audits/dobetterweb/uses-passive-event-listeners.js +1 -1
  99. package/lighthouse-core/audits/errors-in-console.js +2 -2
  100. package/lighthouse-core/audits/long-tasks.js +2 -3
  101. package/lighthouse-core/audits/metrics/first-contentful-paint-3g.js +2 -2
  102. package/lighthouse-core/audits/metrics/first-contentful-paint.js +2 -3
  103. package/lighthouse-core/audits/metrics/first-meaningful-paint.js +2 -3
  104. package/lighthouse-core/audits/metrics/interactive.js +2 -3
  105. package/lighthouse-core/audits/metrics/largest-contentful-paint.js +2 -3
  106. package/lighthouse-core/audits/metrics/max-potential-fid.js +2 -3
  107. package/lighthouse-core/audits/metrics/speed-index.js +2 -3
  108. package/lighthouse-core/audits/metrics/total-blocking-time.js +2 -3
  109. package/lighthouse-core/audits/metrics.js +2 -3
  110. package/lighthouse-core/audits/no-unload-listeners.js +15 -7
  111. package/lighthouse-core/audits/predictive-perf.js +1 -2
  112. package/lighthouse-core/audits/preload-lcp-image.js +3 -3
  113. package/lighthouse-core/audits/redirects.js +1 -1
  114. package/lighthouse-core/audits/script-treemap-data.js +45 -45
  115. package/lighthouse-core/audits/seo/http-status-code.js +4 -2
  116. package/lighthouse-core/audits/seo/link-text.js +0 -2
  117. package/lighthouse-core/audits/server-response-time.js +17 -2
  118. package/lighthouse-core/audits/service-worker.js +2 -7
  119. package/lighthouse-core/audits/timing-budget.js +2 -3
  120. package/lighthouse-core/audits/uses-rel-preconnect.js +1 -1
  121. package/lighthouse-core/audits/uses-rel-preload.js +1 -1
  122. package/lighthouse-core/audits/valid-source-maps.js +14 -12
  123. package/lighthouse-core/audits/violation-audit.js +1 -1
  124. package/lighthouse-core/audits/work-during-interaction.js +2 -2
  125. package/lighthouse-core/computed/critical-request-chains.js +9 -5
  126. package/lighthouse-core/computed/js-bundles.js +11 -11
  127. package/lighthouse-core/computed/main-resource.js +2 -3
  128. package/lighthouse-core/computed/metrics/first-contentful-paint-all-frames.js +1 -1
  129. package/lighthouse-core/computed/metrics/first-contentful-paint.js +1 -1
  130. package/lighthouse-core/computed/metrics/first-meaningful-paint.js +1 -1
  131. package/lighthouse-core/computed/metrics/interactive.js +1 -1
  132. package/lighthouse-core/computed/metrics/lantern-first-contentful-paint.js +1 -1
  133. package/lighthouse-core/computed/metrics/lantern-first-meaningful-paint.js +1 -1
  134. package/lighthouse-core/computed/metrics/lantern-interactive.js +1 -1
  135. package/lighthouse-core/computed/metrics/lantern-largest-contentful-paint.js +1 -1
  136. package/lighthouse-core/computed/metrics/lantern-max-potential-fid.js +1 -1
  137. package/lighthouse-core/computed/metrics/lantern-metric.js +5 -3
  138. package/lighthouse-core/computed/metrics/lantern-speed-index.js +1 -1
  139. package/lighthouse-core/computed/metrics/lantern-total-blocking-time.js +1 -1
  140. package/lighthouse-core/computed/metrics/largest-contentful-paint-all-frames.js +1 -1
  141. package/lighthouse-core/computed/metrics/largest-contentful-paint.js +1 -1
  142. package/lighthouse-core/computed/metrics/max-potential-fid.js +1 -1
  143. package/lighthouse-core/computed/metrics/metric.js +0 -1
  144. package/lighthouse-core/computed/metrics/speed-index.js +1 -1
  145. package/lighthouse-core/computed/metrics/timing-summary.js +6 -13
  146. package/lighthouse-core/computed/metrics/total-blocking-time.js +1 -1
  147. package/lighthouse-core/computed/module-duplication.js +5 -6
  148. package/lighthouse-core/computed/page-dependency-graph.js +28 -55
  149. package/lighthouse-core/computed/resource-summary.js +5 -5
  150. package/lighthouse-core/computed/unused-css.js +7 -6
  151. package/lighthouse-core/computed/unused-javascript-summary.js +36 -16
  152. package/lighthouse-core/config/budget.js +2 -2
  153. package/lighthouse-core/config/default-config.js +1 -2
  154. package/lighthouse-core/fraggle-rock/api.js +1 -1
  155. package/lighthouse-core/fraggle-rock/config/default-config.js +0 -3
  156. package/lighthouse-core/fraggle-rock/gather/base-artifacts.js +3 -6
  157. package/lighthouse-core/fraggle-rock/gather/driver.js +2 -2
  158. package/lighthouse-core/fraggle-rock/gather/navigation-runner.js +26 -51
  159. package/lighthouse-core/fraggle-rock/gather/runner-helpers.js +3 -0
  160. package/lighthouse-core/fraggle-rock/gather/session.js +22 -23
  161. package/lighthouse-core/fraggle-rock/gather/snapshot-runner.js +4 -5
  162. package/lighthouse-core/fraggle-rock/gather/timespan-runner.js +6 -6
  163. package/lighthouse-core/gather/connections/cri.js +1 -5
  164. package/lighthouse-core/gather/driver/execution-context.js +2 -5
  165. package/lighthouse-core/gather/driver/navigation.js +10 -13
  166. package/lighthouse-core/gather/driver/network-monitor.js +3 -16
  167. package/lighthouse-core/gather/driver.js +1 -6
  168. package/lighthouse-core/gather/fetcher.js +210 -34
  169. package/lighthouse-core/gather/gather-runner.js +17 -13
  170. package/lighthouse-core/gather/gatherers/accessibility.js +4 -16
  171. package/lighthouse-core/gather/gatherers/console-messages.js +2 -4
  172. package/lighthouse-core/gather/gatherers/dobetterweb/doctype.js +2 -4
  173. package/lighthouse-core/gather/gatherers/full-page-screenshot.js +2 -3
  174. package/lighthouse-core/gather/gatherers/iframe-elements.js +1 -4
  175. package/lighthouse-core/gather/gatherers/image-elements.js +0 -2
  176. package/lighthouse-core/gather/gatherers/js-usage.js +87 -12
  177. package/lighthouse-core/gather/gatherers/link-elements.js +14 -12
  178. package/lighthouse-core/gather/gatherers/main-document-content.js +8 -8
  179. package/lighthouse-core/gather/gatherers/script-elements.js +56 -7
  180. package/lighthouse-core/gather/gatherers/seo/robots-txt.js +30 -2
  181. package/lighthouse-core/gather/gatherers/source-maps.js +1 -8
  182. package/lighthouse-core/gather/gatherers/web-app-manifest.js +2 -2
  183. package/lighthouse-core/index.js +2 -26
  184. package/lighthouse-core/lib/cdt/generated/SourceMap.js +1 -1
  185. package/lighthouse-core/lib/dependency-graph/simulator/network-analyzer.js +29 -8
  186. package/lighthouse-core/lib/navigation-error.js +4 -13
  187. package/lighthouse-core/lib/page-functions.js +1 -3
  188. package/lighthouse-core/lib/script-helpers.js +6 -11
  189. package/lighthouse-core/lib/url-shim.js +2 -2
  190. package/lighthouse-core/runner.js +5 -0
  191. package/lighthouse-core/scripts/manual-chrome-launcher.js +1 -0
  192. package/lighthouse-core/util-commonjs.js +6 -8
  193. package/package.json +13 -13
  194. package/readme.md +1 -1
  195. package/report/.eslintrc.cjs +1 -3
  196. package/report/assets/styles.css +5 -10
  197. package/report/assets/templates.html +9 -10
  198. package/report/clients/bundle.js +1 -0
  199. package/report/clients/standalone.js +1 -0
  200. package/report/generator/report-generator.js +21 -46
  201. package/report/renderer/api.js +2 -0
  202. package/report/renderer/category-renderer.js +24 -20
  203. package/report/renderer/components.js +304 -303
  204. package/report/renderer/crc-details-renderer.js +24 -21
  205. package/report/renderer/details-renderer.js +12 -12
  206. package/report/renderer/dom.js +7 -6
  207. package/report/renderer/drop-down-menu.js +1 -0
  208. package/report/renderer/element-screenshot-renderer.js +5 -1
  209. package/report/renderer/features-util.js +1 -0
  210. package/report/renderer/i18n.js +62 -118
  211. package/report/renderer/logger.js +1 -0
  212. package/report/renderer/open-tab.js +3 -1
  213. package/report/renderer/performance-category-renderer.js +11 -10
  214. package/report/renderer/pwa-category-renderer.js +4 -3
  215. package/report/renderer/report-renderer.js +17 -17
  216. package/report/renderer/report-ui-features.js +1 -0
  217. package/report/renderer/snippet-renderer.js +3 -2
  218. package/report/renderer/swap-locale-feature.js +1 -0
  219. package/report/renderer/text-encoding.js +1 -0
  220. package/report/renderer/topbar-features.js +1 -0
  221. package/report/renderer/util.js +6 -7
  222. package/report/test/clients/bundle-test.js +5 -0
  223. package/report/test/generator/file-namer-test.js +1 -0
  224. package/report/test/generator/report-generator-test.js +20 -25
  225. package/report/test/renderer/category-renderer-test.js +4 -3
  226. package/report/test/renderer/components-test.js +3 -0
  227. package/report/test/renderer/crc-details-renderer-test.js +3 -0
  228. package/report/test/renderer/details-renderer-test.js +3 -49
  229. package/report/test/renderer/dom-test.js +3 -0
  230. package/report/test/renderer/element-screenshot-renderer-test.js +6 -3
  231. package/report/test/renderer/i18n-test.js +25 -70
  232. package/report/test/renderer/performance-category-renderer-test.js +5 -14
  233. package/report/test/renderer/pwa-category-renderer-test.js +4 -3
  234. package/report/test/renderer/report-renderer-axe-test.js +4 -3
  235. package/report/test/renderer/report-renderer-test.js +4 -3
  236. package/report/test/renderer/report-ui-features-test.js +4 -3
  237. package/report/test/renderer/snippet-renderer-test.js +3 -0
  238. package/report/test/renderer/text-encoding-test.js +3 -0
  239. package/report/test/renderer/util-test.js +5 -4
  240. package/report/test-assets/faux-psi.js +1 -2
  241. package/shared/localization/format.js +3 -7
  242. package/shared/localization/locales/en-US.json +238 -55
  243. package/shared/localization/locales/en-XL.json +238 -55
  244. package/shared/test/localization/format-test.js +2 -8
  245. package/shared/test/localization/locales-test.js +2 -0
  246. package/shared/test/localization/swap-locale-test.js +2 -0
  247. package/third-party/chromium-synchronization/inspector-issueAdded-types-test.js +2 -0
  248. package/third-party/chromium-synchronization/installability-errors-test.js +2 -0
  249. package/tsconfig.json +0 -1
  250. package/types/artifacts.d.ts +8 -47
  251. package/types/config.d.ts +0 -2
  252. package/types/externs.d.ts +2 -3
  253. package/types/gatherer.d.ts +2 -1
  254. package/types/global-lh.d.ts +0 -3
  255. package/types/lhr/lhr.d.ts +2 -2
  256. package/types/lhr/treemap.d.ts +1 -0
  257. package/changelog.md +0 -5902
  258. package/lighthouse-core/audits/script-elements-test-audit.js +0 -30
  259. package/lighthouse-core/gather/gatherers/scripts.js +0 -154
  260. package/lighthouse-core/scripts/package.json +0 -4
  261. package/report/test/.eslintrc.cjs +0 -11
  262. package/shared/test/localization/.eslintrc.cjs +0 -11
@@ -14,6 +14,7 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */
17
+ 'use strict';
17
18
 
18
19
  /**
19
20
  * @fileoverview This file contains helpers for constructing and rendering the
@@ -105,33 +106,34 @@ class CriticalRequestChainRenderer {
105
106
 
106
107
  // Construct lines and add spacers for sub requests.
107
108
  segment.treeMarkers.forEach(separator => {
108
- const classSeparator = separator ?
109
- 'lh-tree-marker lh-vert' :
110
- 'lh-tree-marker';
111
- treeMarkeEl.append(
112
- dom.createElement('span', classSeparator),
113
- dom.createElement('span', 'lh-tree-marker')
114
- );
109
+ if (separator) {
110
+ treeMarkeEl.appendChild(dom.createElement('span', 'lh-tree-marker lh-vert'));
111
+ treeMarkeEl.appendChild(dom.createElement('span', 'lh-tree-marker'));
112
+ } else {
113
+ treeMarkeEl.appendChild(dom.createElement('span', 'lh-tree-marker'));
114
+ treeMarkeEl.appendChild(dom.createElement('span', 'lh-tree-marker'));
115
+ }
115
116
  });
116
117
 
117
- const classLastChild = segment.isLastChild ?
118
- 'lh-tree-marker lh-up-right' :
119
- 'lh-tree-marker lh-vert-right';
120
- const classHasChildren = segment.hasChildren ?
121
- 'lh-tree-marker lh-horiz-down' :
122
- 'lh-tree-marker lh-right';
118
+ if (segment.isLastChild) {
119
+ treeMarkeEl.appendChild(dom.createElement('span', 'lh-tree-marker lh-up-right'));
120
+ treeMarkeEl.appendChild(dom.createElement('span', 'lh-tree-marker lh-right'));
121
+ } else {
122
+ treeMarkeEl.appendChild(dom.createElement('span', 'lh-tree-marker lh-vert-right'));
123
+ treeMarkeEl.appendChild(dom.createElement('span', 'lh-tree-marker lh-right'));
124
+ }
123
125
 
124
- treeMarkeEl.append(
125
- dom.createElement('span', classLastChild),
126
- dom.createElement('span', 'lh-tree-marker lh-right'),
127
- dom.createElement('span', classHasChildren)
128
- );
126
+ if (segment.hasChildren) {
127
+ treeMarkeEl.appendChild(dom.createElement('span', 'lh-tree-marker lh-horiz-down'));
128
+ } else {
129
+ treeMarkeEl.appendChild(dom.createElement('span', 'lh-tree-marker lh-right'));
130
+ }
129
131
 
130
132
  // Fill in url, host, and request size information.
131
133
  const url = segment.node.request.url;
132
134
  const linkEl = detailsRenderer.renderTextURL(url);
133
135
  const treevalEl = dom.find('.lh-crc-node__tree-value', chainEl);
134
- treevalEl.append(linkEl);
136
+ treevalEl.appendChild(linkEl);
135
137
 
136
138
  if (!segment.hasChildren) {
137
139
  const {startTime, endTime, transferSize} = segment.node.request;
@@ -140,7 +142,8 @@ class CriticalRequestChainRenderer {
140
142
  const span2 = dom.createElement('span', 'lh-crc-node__chain-duration');
141
143
  span2.textContent = Util.i18n.formatBytesToKiB(transferSize, 0.01);
142
144
 
143
- treevalEl.append(span, span2);
145
+ treevalEl.appendChild(span);
146
+ treevalEl.appendChild(span2);
144
147
  }
145
148
 
146
149
  return chainEl;
@@ -156,7 +159,7 @@ class CriticalRequestChainRenderer {
156
159
  * @param {DetailsRenderer} detailsRenderer
157
160
  */
158
161
  static buildTree(dom, tmpl, segment, elem, details, detailsRenderer) {
159
- elem.append(CRCRenderer.createChainNode(dom, segment, detailsRenderer));
162
+ elem.appendChild(CRCRenderer.createChainNode(dom, segment, detailsRenderer));
160
163
  if (segment.node.children) {
161
164
  for (const key of Object.keys(segment.node.children)) {
162
165
  const childSegment = CRCRenderer.createSegment(segment.node.children, key,
@@ -14,6 +14,7 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */
17
+ 'use strict';
17
18
 
18
19
  /** @typedef {import('./dom.js').DOM} DOM */
19
20
 
@@ -77,8 +78,9 @@ export class DetailsRenderer {
77
78
  * @return {Element}
78
79
  */
79
80
  _renderBytes(details) {
80
- // TODO: handle displayUnit once we have something other than 'KiB'
81
- const value = Util.i18n.formatBytesToKiB(details.value, details.granularity || 0.1);
81
+ // TODO: handle displayUnit once we have something other than 'kb'
82
+ // Note that 'kb' is historical and actually represents KiB.
83
+ const value = Util.i18n.formatBytesToKiB(details.value, details.granularity);
82
84
  const textEl = this._renderText(value);
83
85
  textEl.title = Util.i18n.formatBytes(details.value);
84
86
  return textEl;
@@ -89,11 +91,9 @@ export class DetailsRenderer {
89
91
  * @return {Element}
90
92
  */
91
93
  _renderMilliseconds(details) {
92
- let value;
94
+ let value = Util.i18n.formatMilliseconds(details.value, details.granularity);
93
95
  if (details.displayUnit === 'duration') {
94
96
  value = Util.i18n.formatDuration(details.value);
95
- } else {
96
- value = Util.i18n.formatMilliseconds(details.value, details.granularity || 10);
97
97
  }
98
98
 
99
99
  return this._renderText(value);
@@ -119,12 +119,12 @@ export class DetailsRenderer {
119
119
  }
120
120
 
121
121
  const element = this._dom.createElement('div', 'lh-text__url');
122
- element.append(this._renderLink({text: displayedPath, url}));
122
+ element.appendChild(this._renderLink({text: displayedPath, url}));
123
123
 
124
124
  if (displayedHost) {
125
125
  const hostElem = this._renderText(displayedHost);
126
126
  hostElem.classList.add('lh-text__url-host');
127
- element.append(hostElem);
127
+ element.appendChild(hostElem);
128
128
  }
129
129
 
130
130
  if (title) {
@@ -172,7 +172,7 @@ export class DetailsRenderer {
172
172
  * @return {Element}
173
173
  */
174
174
  _renderNumeric(details) {
175
- const value = Util.i18n.formatNumber(details.value, details.granularity || 0.1);
175
+ const value = Util.i18n.formatNumber(details.value, details.granularity);
176
176
  const element = this._dom.createElement('div', 'lh-numeric');
177
177
  element.textContent = value;
178
178
  return element;
@@ -397,7 +397,7 @@ export class DetailsRenderer {
397
397
 
398
398
  if (valueElement) {
399
399
  const classes = `lh-table-column--${heading.valueType}`;
400
- this._dom.createChildOf(rowElem, 'td', classes).append(valueElement);
400
+ this._dom.createChildOf(rowElem, 'td', classes).appendChild(valueElement);
401
401
  } else {
402
402
  // Empty cell is rendered for a column if:
403
403
  // - the pair is null
@@ -452,7 +452,7 @@ export class DetailsRenderer {
452
452
  const classes = `lh-table-column--${valueType}`;
453
453
  const labelEl = this._dom.createElement('div', 'lh-text');
454
454
  labelEl.textContent = heading.label;
455
- this._dom.createChildOf(theadTrElem, 'th', classes).append(labelEl);
455
+ this._dom.createChildOf(theadTrElem, 'th', classes).appendChild(labelEl);
456
456
  }
457
457
 
458
458
  const tbodyElem = this._dom.createChildOf(tableElem, 'tbody');
@@ -495,13 +495,13 @@ export class DetailsRenderer {
495
495
  if (item.nodeLabel) {
496
496
  const nodeLabelEl = this._dom.createElement('div');
497
497
  nodeLabelEl.textContent = item.nodeLabel;
498
- element.append(nodeLabelEl);
498
+ element.appendChild(nodeLabelEl);
499
499
  }
500
500
  if (item.snippet) {
501
501
  const snippetEl = this._dom.createElement('div');
502
502
  snippetEl.classList.add('lh-node__snippet');
503
503
  snippetEl.textContent = item.snippet;
504
- element.append(snippetEl);
504
+ element.appendChild(snippetEl);
505
505
  }
506
506
  if (item.selector) {
507
507
  element.title = item.selector;
@@ -14,6 +14,7 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */
17
+ 'use strict';
17
18
 
18
19
  /* eslint-env browser */
19
20
 
@@ -97,7 +98,7 @@ export class DOM {
97
98
  */
98
99
  createChildOf(parentElem, elementName, className) {
99
100
  const element = this.createElement(elementName, className);
100
- parentElem.append(element);
101
+ parentElem.appendChild(element);
101
102
  return element;
102
103
  }
103
104
 
@@ -135,7 +136,7 @@ export class DOM {
135
136
  for (const segment of Util.splitMarkdownLink(text)) {
136
137
  if (!segment.isLink) {
137
138
  // Plain text segment.
138
- element.append(this._document.createTextNode(segment.text));
139
+ element.appendChild(this._document.createTextNode(segment.text));
139
140
  continue;
140
141
  }
141
142
 
@@ -153,7 +154,7 @@ export class DOM {
153
154
  a.target = '_blank';
154
155
  a.textContent = segment.text;
155
156
  this.safelySetHref(a, url.href);
156
- element.append(a);
157
+ element.appendChild(a);
157
158
  }
158
159
 
159
160
  return element;
@@ -210,9 +211,9 @@ export class DOM {
210
211
  if (segment.isCode) {
211
212
  const pre = this.createElement('code');
212
213
  pre.textContent = segment.text;
213
- element.append(pre);
214
+ element.appendChild(pre);
214
215
  } else {
215
- element.append(this._document.createTextNode(segment.text));
216
+ element.appendChild(this._document.createTextNode(segment.text));
216
217
  }
217
218
  }
218
219
 
@@ -295,7 +296,7 @@ export class DOM {
295
296
  const a = this.createElement('a');
296
297
  a.download = filename;
297
298
  this.safelySetBlobHref(a, blob);
298
- this._document.body.append(a); // Firefox requires anchor to be in the DOM.
299
+ this._document.body.appendChild(a); // Firefox requires anchor to be in the DOM.
299
300
  a.click();
300
301
 
301
302
  // cleanup.
@@ -3,6 +3,7 @@
3
3
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
4
4
  * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
5
5
  */
6
+ 'use strict';
6
7
 
7
8
  /* eslint-env browser */
8
9
 
@@ -3,6 +3,7 @@
3
3
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
4
4
  * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
5
5
  */
6
+ 'use strict';
6
7
 
7
8
  /**
8
9
  * @fileoverview These functions define {Rect}s and {Size}s using two different coordinate spaces:
@@ -186,7 +187,7 @@ export class ElementScreenshotRenderer {
186
187
  overlay.remove();
187
188
  return;
188
189
  }
189
- overlay.append(screenshotElement);
190
+ overlay.appendChild(screenshotElement);
190
191
  overlay.addEventListener('click', () => overlay.remove());
191
192
  });
192
193
  }
@@ -252,6 +253,9 @@ export class ElementScreenshotRenderer {
252
253
  {width: screenshot.width, height: screenshot.height}
253
254
  );
254
255
 
256
+ const contentEl = dom.find('div.lh-element-screenshot__content', containerEl);
257
+ contentEl.style.top = `-${elementPreviewSizeDC.height}px`;
258
+
255
259
  const imageEl = dom.find('div.lh-element-screenshot__image', containerEl);
256
260
  imageEl.style.width = elementPreviewSizeDC.width + 'px';
257
261
  imageEl.style.height = elementPreviewSizeDC.height + 'px';
@@ -3,6 +3,7 @@
3
3
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
4
4
  * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
5
5
  */
6
+ 'use strict';
6
7
 
7
8
  /* eslint-env browser */
8
9
 
@@ -3,6 +3,7 @@
3
3
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
4
4
  * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
5
5
  */
6
+ 'use strict';
6
7
 
7
8
  // Not named `NBSP` because that creates a duplicate identifier (util.js).
8
9
  const NBSP2 = '\xa0';
@@ -21,7 +22,9 @@ export class I18n {
21
22
  // When testing, use a locale with more exciting numeric formatting.
22
23
  if (locale === 'en-XA') locale = 'de';
23
24
 
24
- this._locale = locale;
25
+ this._numberDateLocale = locale;
26
+ this._numberFormatter = new Intl.NumberFormat(locale);
27
+ this._percentFormatter = new Intl.NumberFormat(locale, {style: 'percent'});
25
28
  this._strings = strings;
26
29
  }
27
30
 
@@ -29,58 +32,15 @@ export class I18n {
29
32
  return this._strings;
30
33
  }
31
34
 
32
- /**
33
- * @param {number} number
34
- * @param {number|undefined} granularity
35
- * @param {Intl.NumberFormatOptions=} opts
36
- * @return {string}
37
- */
38
- _formatNumberWithGranularity(number, granularity, opts = {}) {
39
- if (granularity !== undefined) {
40
- const log10 = -Math.log10(granularity);
41
- if (!Number.isInteger(log10)) {
42
- console.warn(`granularity of ${granularity} is invalid. Using 1 instead`);
43
- granularity = 1;
44
- }
45
-
46
- if (granularity < 1) {
47
- opts = {...opts};
48
- opts.minimumFractionDigits = opts.maximumFractionDigits = Math.ceil(log10);
49
- }
50
-
51
- number = Math.round(number / granularity) * granularity;
52
-
53
- // Avoid displaying a negative value that rounds to zero as "0".
54
- if (Object.is(number, -0)) number = 0;
55
- } else if (Math.abs(number) < 0.0005) {
56
- // Also avoids "-0".
57
- number = 0;
58
- }
59
-
60
- return new Intl.NumberFormat(this._locale, opts).format(number).replace(' ', NBSP2);
61
- }
62
-
63
35
  /**
64
36
  * Format number.
65
37
  * @param {number} number
66
- * @param {number=} granularity Controls how coarse the displayed value is.
67
- * If undefined, the number will be displayed as described
68
- * by the Intl defaults: tinyurl.com/7s67w5x7
69
- * @return {string}
70
- */
71
- formatNumber(number, granularity) {
72
- return this._formatNumberWithGranularity(number, granularity);
73
- }
74
-
75
- /**
76
- * Format integer.
77
- * Just like {@link formatNumber} but uses a granularity of 1, rounding to the nearest
78
- * whole number.
79
- * @param {number} number
38
+ * @param {number=} granularity Number of decimal places to include. Defaults to 0.1.
80
39
  * @return {string}
81
40
  */
82
- formatInteger(number) {
83
- return this._formatNumberWithGranularity(number, 1);
41
+ formatNumber(number, granularity = 0.1) {
42
+ const coarseValue = Math.round(number / granularity) * granularity;
43
+ return this._numberFormatter.format(coarseValue);
84
44
  }
85
45
 
86
46
  /**
@@ -89,99 +49,92 @@ export class I18n {
89
49
  * @return {string}
90
50
  */
91
51
  formatPercent(number) {
92
- return new Intl.NumberFormat(this._locale, {style: 'percent'}).format(number);
52
+ return this._percentFormatter.format(number);
93
53
  }
94
54
 
95
55
  /**
96
56
  * @param {number} size
97
- * @param {number=} granularity Controls how coarse the displayed value is.
98
- * If undefined, the number will be displayed in full.
57
+ * @param {number=} granularity Controls how coarse the displayed value is, defaults to 0.1
99
58
  * @return {string}
100
59
  */
101
- formatBytesToKiB(size, granularity = undefined) {
102
- return this._formatNumberWithGranularity(size / KiB, granularity) + `${NBSP2}KiB`;
60
+ formatBytesToKiB(size, granularity = 0.1) {
61
+ const formatter = this._byteFormatterForGranularity(granularity);
62
+ const kbs = formatter.format(Math.round(size / 1024 / granularity) * granularity);
63
+ return `${kbs}${NBSP2}KiB`;
103
64
  }
104
65
 
105
66
  /**
106
67
  * @param {number} size
107
- * @param {number=} granularity Controls how coarse the displayed value is.
108
- * If undefined, the number will be displayed in full.
68
+ * @param {number=} granularity Controls how coarse the displayed value is, defaults to 0.1
109
69
  * @return {string}
110
70
  */
111
- formatBytesToMiB(size, granularity = undefined) {
112
- return this._formatNumberWithGranularity(size / MiB, granularity) + `${NBSP2}MiB`;
71
+ formatBytesToMiB(size, granularity = 0.1) {
72
+ const formatter = this._byteFormatterForGranularity(granularity);
73
+ const kbs = formatter.format(Math.round(size / (1024 ** 2) / granularity) * granularity);
74
+ return `${kbs}${NBSP2}MiB`;
113
75
  }
114
76
 
115
77
  /**
116
78
  * @param {number} size
117
- * @param {number=} granularity Controls how coarse the displayed value is.
118
- * If undefined, the number will be displayed in full.
79
+ * @param {number=} granularity Controls how coarse the displayed value is, defaults to 1
119
80
  * @return {string}
120
81
  */
121
82
  formatBytes(size, granularity = 1) {
122
- return this._formatNumberWithGranularity(size, granularity, {
123
- style: 'unit',
124
- unit: 'byte',
125
- unitDisplay: 'long',
126
- });
83
+ const formatter = this._byteFormatterForGranularity(granularity);
84
+ const kbs = formatter.format(Math.round(size / granularity) * granularity);
85
+ return `${kbs}${NBSP2}bytes`;
127
86
  }
128
87
 
129
88
  /**
130
89
  * @param {number} size
131
- * @param {number=} granularity Controls how coarse the displayed value is.
132
- * If undefined, the number will be displayed in full.
90
+ * @param {number=} granularity Controls how coarse the displayed value is, defaults to 0.1
133
91
  * @return {string}
134
92
  */
135
- formatBytesWithBestUnit(size, granularity = undefined) {
93
+ formatBytesWithBestUnit(size, granularity = 0.1) {
136
94
  if (size >= MiB) return this.formatBytesToMiB(size, granularity);
137
95
  if (size >= KiB) return this.formatBytesToKiB(size, granularity);
138
- return this._formatNumberWithGranularity(size, granularity, {
139
- style: 'unit',
140
- unit: 'byte',
141
- unitDisplay: 'narrow',
142
- });
96
+ return this.formatNumber(size, granularity) + '\xa0B';
143
97
  }
144
98
 
145
99
  /**
146
- * @param {number} size
147
- * @param {number=} granularity Controls how coarse the displayed value is.
148
- * If undefined, the number will be displayed in full.
149
- * @return {string}
100
+ * Format bytes with a constant number of fractional digits, i.e. for a granularity of 0.1, 10 becomes '10.0'
101
+ * @param {number} granularity Controls how coarse the displayed value is
102
+ * @return {Intl.NumberFormat}
150
103
  */
151
- formatKbps(size, granularity = undefined) {
152
- return this._formatNumberWithGranularity(size, granularity, {
153
- style: 'unit',
154
- unit: 'kilobit-per-second',
155
- unitDisplay: 'short',
104
+ _byteFormatterForGranularity(granularity) {
105
+ // assume any granularity above 1 will not contain fractional parts, i.e. will never be 1.5
106
+ let numberOfFractionDigits = 0;
107
+ if (granularity < 1) {
108
+ numberOfFractionDigits = -Math.floor(Math.log10(granularity));
109
+ }
110
+
111
+ return new Intl.NumberFormat(this._numberDateLocale, {
112
+ ...this._numberFormatter.resolvedOptions(),
113
+ maximumFractionDigits: numberOfFractionDigits,
114
+ minimumFractionDigits: numberOfFractionDigits,
156
115
  });
157
116
  }
158
117
 
159
118
  /**
160
119
  * @param {number} ms
161
- * @param {number=} granularity Controls how coarse the displayed value is.
162
- * If undefined, the number will be displayed in full.
120
+ * @param {number=} granularity Controls how coarse the displayed value is, defaults to 10
163
121
  * @return {string}
164
122
  */
165
- formatMilliseconds(ms, granularity = undefined) {
166
- return this._formatNumberWithGranularity(ms, granularity, {
167
- style: 'unit',
168
- unit: 'millisecond',
169
- unitDisplay: 'short',
170
- });
123
+ formatMilliseconds(ms, granularity = 10) {
124
+ const coarseTime = Math.round(ms / granularity) * granularity;
125
+ return coarseTime === 0
126
+ ? `${this._numberFormatter.format(0)}${NBSP2}ms`
127
+ : `${this._numberFormatter.format(coarseTime)}${NBSP2}ms`;
171
128
  }
172
129
 
173
130
  /**
174
131
  * @param {number} ms
175
- * @param {number=} granularity Controls how coarse the displayed value is.
176
- * If undefined, the number will be displayed in full.
132
+ * @param {number=} granularity Controls how coarse the displayed value is, defaults to 0.1
177
133
  * @return {string}
178
134
  */
179
- formatSeconds(ms, granularity = undefined) {
180
- return this._formatNumberWithGranularity(ms / 1000, granularity, {
181
- style: 'unit',
182
- unit: 'second',
183
- unitDisplay: 'short',
184
- });
135
+ formatSeconds(ms, granularity = 0.1) {
136
+ const coarseTime = Math.round(ms / 1000 / granularity) * granularity;
137
+ return `${this._numberFormatter.format(coarseTime)}${NBSP2}s`;
185
138
  }
186
139
 
187
140
  /**
@@ -201,10 +154,10 @@ export class I18n {
201
154
  // and https://github.com/GoogleChrome/lighthouse/pull/9822
202
155
  let formatter;
203
156
  try {
204
- formatter = new Intl.DateTimeFormat(this._locale, options);
157
+ formatter = new Intl.DateTimeFormat(this._numberDateLocale, options);
205
158
  } catch (err) {
206
159
  options.timeZone = 'UTC';
207
- formatter = new Intl.DateTimeFormat(this._locale, options);
160
+ formatter = new Intl.DateTimeFormat(this._numberDateLocale, options);
208
161
  }
209
162
 
210
163
  return formatter.format(new Date(date));
@@ -216,10 +169,6 @@ export class I18n {
216
169
  * @return {string}
217
170
  */
218
171
  formatDuration(timeInMilliseconds) {
219
- // There is a proposal for a Intl.DurationFormat.
220
- // https://github.com/tc39/proposal-intl-duration-format
221
- // Until then, we do things a bit more manually.
222
-
223
172
  let timeInSeconds = timeInMilliseconds / 1000;
224
173
  if (Math.round(timeInSeconds) === 0) {
225
174
  return 'None';
@@ -228,24 +177,19 @@ export class I18n {
228
177
  /** @type {Array<string>} */
229
178
  const parts = [];
230
179
  /** @type {Record<string, number>} */
231
- const unitToSecondsPer = {
232
- day: 60 * 60 * 24,
233
- hour: 60 * 60,
234
- minute: 60,
235
- second: 1,
180
+ const unitLabels = {
181
+ d: 60 * 60 * 24,
182
+ h: 60 * 60,
183
+ m: 60,
184
+ s: 1,
236
185
  };
237
186
 
238
- Object.keys(unitToSecondsPer).forEach(unit => {
239
- const secondsPerUnit = unitToSecondsPer[unit];
240
- const numberOfUnits = Math.floor(timeInSeconds / secondsPerUnit);
187
+ Object.keys(unitLabels).forEach(label => {
188
+ const unit = unitLabels[label];
189
+ const numberOfUnits = Math.floor(timeInSeconds / unit);
241
190
  if (numberOfUnits > 0) {
242
- timeInSeconds -= numberOfUnits * secondsPerUnit;
243
- const part = this._formatNumberWithGranularity(numberOfUnits, 1, {
244
- style: 'unit',
245
- unit,
246
- unitDisplay: 'narrow',
247
- });
248
- parts.push(part);
191
+ timeInSeconds -= numberOfUnits * unit;
192
+ parts.push(`${numberOfUnits}\xa0${label}`);
249
193
  }
250
194
  });
251
195
 
@@ -14,6 +14,7 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */
17
+ 'use strict';
17
18
 
18
19
  /**
19
20
  * Logs messages via a UI butter.
@@ -14,6 +14,7 @@
14
14
  * See the License for the specific language governing permissions and
15
15
  * limitations under the License.
16
16
  */
17
+ 'use strict';
17
18
 
18
19
  import {TextEncoding} from './text-encoding.js';
19
20
 
@@ -37,7 +38,7 @@ function computeWindowNameSuffix(json) {
37
38
  // @ts-expect-error - If this is a v2 LHR, use old `generatedTime`.
38
39
  const fallbackFetchTime = /** @type {string} */ (json.generatedTime);
39
40
  const fetchTime = json.fetchTime || fallbackFetchTime;
40
- return `${json.lighthouseVersion}-${json.finalUrl}-${fetchTime}`;
41
+ return `${json.lighthouseVersion}-${json.requestedUrl}-${fetchTime}`;
41
42
  }
42
43
 
43
44
  /**
@@ -119,6 +120,7 @@ function openTreemap(json) {
119
120
  /** @type {LH.Treemap.Options} */
120
121
  const treemapOptions = {
121
122
  lhr: {
123
+ requestedUrl: json.requestedUrl,
122
124
  finalUrl: json.finalUrl,
123
125
  audits: {
124
126
  'script-treemap-data': json.audits['script-treemap-data'],