lighthouse 11.4.0 → 11.5.0

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 (65) hide show
  1. package/cli/test/smokehouse/__snapshots__/report-assert-test.js.snap +14 -4
  2. package/cli/test/smokehouse/core-tests.js +2 -0
  3. package/core/audits/audit.d.ts +5 -0
  4. package/core/audits/audit.js +46 -2
  5. package/core/audits/bf-cache.js +1 -1
  6. package/core/audits/byte-efficiency/byte-efficiency-audit.js +1 -1
  7. package/core/audits/byte-efficiency/legacy-javascript.js +13 -2
  8. package/core/audits/byte-efficiency/render-blocking-resources.d.ts +5 -4
  9. package/core/audits/byte-efficiency/render-blocking-resources.js +15 -9
  10. package/core/audits/byte-efficiency/unused-css-rules.js +1 -1
  11. package/core/audits/byte-efficiency/unused-javascript.js +1 -1
  12. package/core/audits/layout-shift-elements.js +1 -1
  13. package/core/audits/layout-shifts.d.ts +33 -0
  14. package/core/audits/layout-shifts.js +158 -0
  15. package/core/audits/prioritize-lcp-image.js +1 -1
  16. package/core/audits/unsized-images.js +1 -1
  17. package/core/audits/viewport.js +10 -0
  18. package/core/computed/metrics/cumulative-layout-shift.d.ts +20 -1
  19. package/core/computed/metrics/cumulative-layout-shift.js +74 -4
  20. package/core/computed/trace-engine-result.d.ts +40 -0
  21. package/core/computed/trace-engine-result.js +69 -0
  22. package/core/computed/unused-css.js +4 -4
  23. package/core/computed/viewport-meta.d.ts +4 -0
  24. package/core/computed/viewport-meta.js +6 -1
  25. package/core/config/default-config.js +4 -1
  26. package/core/config/metrics-to-audits.js +1 -0
  27. package/core/gather/driver/target-manager.js +10 -1
  28. package/core/gather/driver/wait-for-condition.js +1 -1
  29. package/core/gather/gatherers/dobetterweb/response-compression.js +1 -12
  30. package/core/gather/gatherers/root-causes.d.ts +20 -0
  31. package/core/gather/gatherers/root-causes.js +133 -0
  32. package/core/gather/gatherers/trace-elements.d.ts +38 -7
  33. package/core/gather/gatherers/trace-elements.js +113 -34
  34. package/core/gather/gatherers/trace.js +6 -3
  35. package/core/gather/navigation-runner.js +1 -1
  36. package/core/gather/session.js +16 -3
  37. package/core/lib/i18n/i18n.js +2 -0
  38. package/core/lib/lighthouse-compatibility.js +4 -0
  39. package/core/lib/network-request.js +10 -2
  40. package/core/lib/polyfill-dom-rect.d.ts +2 -0
  41. package/core/lib/polyfill-dom-rect.js +111 -0
  42. package/core/lib/trace-engine.d.ts +7 -0
  43. package/core/lib/trace-engine.js +19 -0
  44. package/core/scripts/download-chrome.sh +17 -0
  45. package/dist/report/bundle.esm.js +14 -10
  46. package/dist/report/flow.js +15 -11
  47. package/dist/report/standalone.js +13 -9
  48. package/flow-report/src/i18n/i18n.d.ts +2 -0
  49. package/package.json +5 -4
  50. package/report/assets/styles.css +9 -5
  51. package/report/renderer/category-renderer.d.ts +5 -12
  52. package/report/renderer/category-renderer.js +18 -18
  53. package/report/renderer/components.js +1 -1
  54. package/report/renderer/performance-category-renderer.d.ts +2 -1
  55. package/report/renderer/performance-category-renderer.js +90 -69
  56. package/report/renderer/pwa-category-renderer.js +11 -2
  57. package/report/renderer/report-utils.d.ts +1 -0
  58. package/report/renderer/report-utils.js +3 -0
  59. package/shared/localization/locales/en-US.json +27 -0
  60. package/shared/localization/locales/en-XL.json +27 -0
  61. package/types/artifacts.d.ts +10 -1
  62. package/types/audit.d.ts +9 -1
  63. package/types/config.d.ts +1 -0
  64. package/types/lhr/audit-result.d.ts +1 -7
  65. package/types/trace-engine.d.ts +1516 -0
@@ -23,10 +23,13 @@ import {LighthouseError} from '../../lib/lh-error.js';
23
23
  import {Responsiveness} from '../../computed/metrics/responsiveness.js';
24
24
  import {CumulativeLayoutShift} from '../../computed/metrics/cumulative-layout-shift.js';
25
25
  import {ExecutionContext} from '../driver/execution-context.js';
26
+ import RootCauses from './root-causes.js';
27
+ import {TraceEngineResult} from '../../computed/trace-engine-result.js';
26
28
 
27
29
  /** @typedef {{nodeId: number, animations?: {name?: string, failureReasonsMask?: number, unsupportedProperties?: string[]}[], type?: string}} TraceElementData */
28
30
 
29
31
  const MAX_LAYOUT_SHIFT_ELEMENTS = 15;
32
+ const MAX_LAYOUT_SHIFTS = 15;
30
33
 
31
34
  /**
32
35
  * @this {HTMLElement}
@@ -44,10 +47,10 @@ function getNodeDetailsData() {
44
47
  /* c8 ignore stop */
45
48
 
46
49
  class TraceElements extends BaseGatherer {
47
- /** @type {LH.Gatherer.GathererMeta<'Trace'>} */
50
+ /** @type {LH.Gatherer.GathererMeta<'Trace'|'RootCauses'>} */
48
51
  meta = {
49
52
  supportedModes: ['timespan', 'navigation'],
50
- dependencies: {Trace: Trace.symbol},
53
+ dependencies: {Trace: Trace.symbol, RootCauses: RootCauses.symbol},
51
54
  };
52
55
 
53
56
  /** @type {Map<string, string>} */
@@ -64,11 +67,11 @@ class TraceElements extends BaseGatherer {
64
67
  }
65
68
 
66
69
  /**
67
- * This function finds the top (up to 15) elements that contribute to the CLS score of the page.
70
+ * This function finds the top (up to 15) elements that shift on the page.
68
71
  *
69
72
  * @param {LH.Trace} trace
70
73
  * @param {LH.Gatherer.Context} context
71
- * @return {Promise<Array<TraceElementData>>}
74
+ * @return {Promise<Array<{nodeId: number}>>}
72
75
  */
73
76
  static async getTopLayoutShiftElements(trace, context) {
74
77
  const {impactByNodeId} = await CumulativeLayoutShift.request(trace, context);
@@ -79,6 +82,66 @@ class TraceElements extends BaseGatherer {
79
82
  .map(([nodeId]) => ({nodeId}));
80
83
  }
81
84
 
85
+ /**
86
+ * We want to a single representative node to represent the shift, so let's pick
87
+ * the one with the largest impact (size x distance moved).
88
+ *
89
+ * @param {LH.Artifacts.TraceImpactedNode[]} impactedNodes
90
+ * @param {Map<number, number>} impactByNodeId
91
+ * @return {number|undefined}
92
+ */
93
+ static getBiggestImpactNodeForShiftEvent(impactedNodes, impactByNodeId) {
94
+ let biggestImpactNodeId;
95
+ let biggestImpactNodeScore = Number.NEGATIVE_INFINITY;
96
+ for (const node of impactedNodes) {
97
+ const impactScore = impactByNodeId.get(node.node_id);
98
+ if (impactScore !== undefined && impactScore > biggestImpactNodeScore) {
99
+ biggestImpactNodeId = node.node_id;
100
+ biggestImpactNodeScore = impactScore;
101
+ }
102
+ }
103
+ return biggestImpactNodeId;
104
+ }
105
+
106
+ /**
107
+ * This function finds the top (up to 15) layout shifts on the page, and returns
108
+ * the id of the largest impacted node of each shift, along with any related nodes
109
+ * that may have caused the shift.
110
+ *
111
+ * @param {LH.Trace} trace
112
+ * @param {LH.Artifacts.TraceEngineResult} traceEngineResult
113
+ * @param {LH.Artifacts.TraceEngineRootCauses} rootCauses
114
+ * @param {LH.Gatherer.Context} context
115
+ * @return {Promise<Array<{nodeId: number}>>}
116
+ */
117
+ static async getTopLayoutShifts(trace, traceEngineResult, rootCauses, context) {
118
+ const {impactByNodeId} = await CumulativeLayoutShift.request(trace, context);
119
+ const clusters = traceEngineResult.LayoutShifts.clusters ?? [];
120
+ const layoutShiftEvents = clusters.flatMap(c => c.events);
121
+
122
+ return layoutShiftEvents
123
+ .sort((a, b) => b.args.data.weighted_score_delta - a.args.data.weighted_score_delta)
124
+ .slice(0, MAX_LAYOUT_SHIFTS)
125
+ .flatMap(event => {
126
+ const nodeIds = [];
127
+ const biggestImpactedNodeId =
128
+ this.getBiggestImpactNodeForShiftEvent(event.args.data.impacted_nodes, impactByNodeId);
129
+ if (biggestImpactedNodeId !== undefined) {
130
+ nodeIds.push(biggestImpactedNodeId);
131
+ }
132
+
133
+ const index = layoutShiftEvents.indexOf(event);
134
+ const shiftRootCauses = rootCauses.layoutShifts[index];
135
+ if (shiftRootCauses) {
136
+ for (const cause of shiftRootCauses.unsizedMedia) {
137
+ nodeIds.push(cause.node.backendNodeId);
138
+ }
139
+ }
140
+
141
+ return nodeIds.map(nodeId => ({nodeId}));
142
+ });
143
+ }
144
+
82
145
  /**
83
146
  * @param {LH.Trace} trace
84
147
  * @param {LH.Gatherer.Context} context
@@ -195,61 +258,77 @@ class TraceElements extends BaseGatherer {
195
258
  }
196
259
 
197
260
  /**
198
- * @param {LH.Gatherer.Context<'Trace'>} context
261
+ * @param {LH.Gatherer.ProtocolSession} session
262
+ * @param {number} backendNodeId
263
+ */
264
+ async getNodeDetails(session, backendNodeId) {
265
+ try {
266
+ const objectId = await resolveNodeIdToObjectId(session, backendNodeId);
267
+ if (!objectId) return null;
268
+
269
+ const deps = ExecutionContext.serializeDeps([
270
+ pageFunctions.getNodeDetails,
271
+ getNodeDetailsData,
272
+ ]);
273
+ return await session.sendCommand('Runtime.callFunctionOn', {
274
+ objectId,
275
+ functionDeclaration: `function () {
276
+ ${deps}
277
+ return getNodeDetailsData.call(this);
278
+ }`,
279
+ returnByValue: true,
280
+ awaitPromise: true,
281
+ });
282
+ } catch (err) {
283
+ Sentry.captureException(err, {
284
+ tags: {gatherer: 'TraceElements'},
285
+ level: 'error',
286
+ });
287
+ }
288
+
289
+ return null;
290
+ }
291
+
292
+ /**
293
+ * @param {LH.Gatherer.Context<'Trace'|'RootCauses'>} context
199
294
  * @return {Promise<LH.Artifacts.TraceElement[]>}
200
295
  */
201
296
  async getArtifact(context) {
202
297
  const session = context.driver.defaultSession;
203
298
 
204
299
  const trace = context.dependencies.Trace;
205
- if (!trace) {
206
- throw new Error('Trace is missing!');
207
- }
300
+ const traceEngineResult = await TraceEngineResult.request({trace}, context);
301
+ const rootCauses = context.dependencies.RootCauses;
208
302
 
209
303
  const processedTrace = await ProcessedTrace.request(trace, context);
210
304
  const {mainThreadEvents} = processedTrace;
211
305
 
212
306
  const lcpNodeData = await TraceElements.getLcpElement(trace, context);
213
- const clsNodeData = await TraceElements.getTopLayoutShiftElements(trace, context);
307
+ const shiftElementsNodeData = await TraceElements.getTopLayoutShiftElements(trace, context);
308
+ const shiftsData = await TraceElements.getTopLayoutShifts(
309
+ trace, traceEngineResult, rootCauses, context);
214
310
  const animatedElementData = await this.getAnimatedElements(mainThreadEvents);
215
311
  const responsivenessElementData = await TraceElements.getResponsivenessElement(trace, context);
216
312
 
217
313
  /** @type {Map<string, TraceElementData[]>} */
218
314
  const backendNodeDataMap = new Map([
219
315
  ['largest-contentful-paint', lcpNodeData ? [lcpNodeData] : []],
220
- ['layout-shift', clsNodeData],
316
+ ['layout-shift-element', shiftElementsNodeData],
317
+ ['layout-shift', shiftsData],
221
318
  ['animation', animatedElementData],
222
319
  ['responsiveness', responsivenessElementData ? [responsivenessElementData] : []],
223
320
  ]);
224
321
 
322
+ /** @type {Map<number, LH.Crdp.Runtime.CallFunctionOnResponse | null>} */
323
+ const callFunctionOnCache = new Map();
225
324
  const traceElements = [];
226
325
  for (const [traceEventType, backendNodeData] of backendNodeDataMap) {
227
326
  for (let i = 0; i < backendNodeData.length; i++) {
228
327
  const backendNodeId = backendNodeData[i].nodeId;
229
- let response;
230
- try {
231
- const objectId = await resolveNodeIdToObjectId(session, backendNodeId);
232
- if (!objectId) continue;
233
-
234
- const deps = ExecutionContext.serializeDeps([
235
- pageFunctions.getNodeDetails,
236
- getNodeDetailsData,
237
- ]);
238
- response = await session.sendCommand('Runtime.callFunctionOn', {
239
- objectId,
240
- functionDeclaration: `function () {
241
- ${deps}
242
- return getNodeDetailsData.call(this);
243
- }`,
244
- returnByValue: true,
245
- awaitPromise: true,
246
- });
247
- } catch (err) {
248
- Sentry.captureException(err, {
249
- tags: {gatherer: 'TraceElements'},
250
- level: 'error',
251
- });
252
- continue;
328
+ let response = callFunctionOnCache.get(backendNodeId);
329
+ if (response === undefined) {
330
+ response = await this.getNodeDetails(session, backendNodeId);
331
+ callFunctionOnCache.set(backendNodeId, response);
253
332
  }
254
333
 
255
334
  if (response?.result?.value) {
@@ -56,9 +56,12 @@ class Trace extends BaseGatherer {
56
56
  'disabled-by-default-devtools.timeline.frame',
57
57
  'latencyInfo',
58
58
 
59
- // A bug introduced in M92 causes these categories to crash targets on Linux.
60
- // See https://github.com/GoogleChrome/lighthouse/issues/12835 for full investigation.
61
- // 'disabled-by-default-v8.cpu_profiler',
59
+ // For CLS root causes.
60
+ 'disabled-by-default-devtools.timeline.invalidationTracking',
61
+
62
+ // Not used by Lighthouse (yet) but included for users that want JS samples when looking at
63
+ // a trace collected by Lighthouse (e.g. "View Trace" workflow in DevTools)
64
+ 'disabled-by-default-v8.cpu_profiler',
62
65
  ];
63
66
  }
64
67
 
@@ -303,7 +303,7 @@ async function navigationGather(page, requestor, options = {}) {
303
303
  page,
304
304
  resolvedConfig,
305
305
  requestor: normalizedRequestor,
306
- computedCache: new Map(),
306
+ computedCache,
307
307
  };
308
308
  const {baseArtifacts} = await _setup(context);
309
309
 
@@ -10,6 +10,17 @@ import {LighthouseError} from '../lib/lh-error.js';
10
10
 
11
11
  // Controls how long to wait for a response after sending a DevTools protocol command.
12
12
  const DEFAULT_PROTOCOL_TIMEOUT = 30000;
13
+ const PPTR_BUFFER = 50;
14
+
15
+ /**
16
+ * Puppeteer timeouts must fit into an int32 and the maximum timeout for `setTimeout` is a *signed*
17
+ * int32. However, this also needs to account for the puppeteer buffer we add to the timeout later.
18
+ *
19
+ * So this is defined as the max *signed* int32 minus PPTR_BUFFER.
20
+ *
21
+ * In human terms, this timeout is ~25 days which is as good as infinity for all practical purposes.
22
+ */
23
+ const MAX_TIMEOUT = 2147483647 - PPTR_BUFFER;
13
24
 
14
25
  /** @typedef {LH.Protocol.StrictEventEmitterClass<LH.CrdpEvents>} CrdpEventMessageEmitter */
15
26
  const CrdpEventEmitter = /** @type {CrdpEventMessageEmitter} */ (EventEmitter);
@@ -70,6 +81,7 @@ class ProtocolSession extends CrdpEventEmitter {
70
81
  * @param {number} ms
71
82
  */
72
83
  setNextProtocolTimeout(ms) {
84
+ if (ms > MAX_TIMEOUT) ms = MAX_TIMEOUT;
73
85
  this._nextProtocolTimeout = ms;
74
86
  }
75
87
 
@@ -86,15 +98,16 @@ class ProtocolSession extends CrdpEventEmitter {
86
98
  /** @type {NodeJS.Timer|undefined} */
87
99
  let timeout;
88
100
  const timeoutPromise = new Promise((resolve, reject) => {
89
- if (timeoutMs === Infinity) return;
90
-
91
101
  // eslint-disable-next-line max-len
92
102
  timeout = setTimeout(reject, timeoutMs, new LighthouseError(LighthouseError.errors.PROTOCOL_TIMEOUT, {
93
103
  protocolMethod: method,
94
104
  }));
95
105
  });
96
106
 
97
- const resultPromise = this._cdpSession.send(method, ...params);
107
+ const resultPromise = this._cdpSession.send(method, ...params, {
108
+ // Add 50ms to the Puppeteer timeout to ensure the Lighthouse timeout finishes first.
109
+ timeout: timeoutMs + PPTR_BUFFER,
110
+ });
98
111
  const resultWithTimeoutPromise = Promise.race([resultPromise, timeoutPromise]);
99
112
 
100
113
  return resultWithTimeoutPromise.finally(() => {
@@ -17,6 +17,7 @@ import {LH_ROOT} from '../../../shared/root.js';
17
17
  import {isIcuMessage, formatMessage, DEFAULT_LOCALE} from '../../../shared/localization/format.js';
18
18
  import {getModulePath} from '../../../shared/esm-utils.js';
19
19
 
20
+ /* eslint-disable max-len */
20
21
  const UIStrings = {
21
22
  /** Used to show the duration in milliseconds that something lasted. The `{timeInMs}` placeholder will be replaced with the time duration, shown in milliseconds (e.g. 63 ms) */
22
23
  ms: '{timeInMs, number, milliseconds}\xa0ms',
@@ -113,6 +114,7 @@ const UIStrings = {
113
114
  /** Table item value for the severity of a high impact, or dangerous vulnerability. Part of a ranking scale in the form: low, medium, high. */
114
115
  itemSeverityHigh: 'High',
115
116
  };
117
+ /* eslint-enable max-len */
116
118
 
117
119
  /**
118
120
  * Look up the best available locale for the requested language through these fall backs:
@@ -36,6 +36,10 @@ function upgradeLhrForCompatibility(lhr) {
36
36
  audit.scoreDisplayMode = 'notApplicable';
37
37
  }
38
38
 
39
+ if (audit.scoreDisplayMode === 'informative') {
40
+ audit.score = 1;
41
+ }
42
+
39
43
  if (audit.details) {
40
44
  // Turn `auditDetails.type` of undefined (LHR <4.2) and 'diagnostic' (LHR <5.0)
41
45
  // into 'debugdata' (LHR ≥5.0).
@@ -616,8 +616,16 @@ class NetworkRequest {
616
616
  static isContentEncoded(record) {
617
617
  // FYI: older devtools logs (like our test fixtures) seems to be lower case, while modern logs
618
618
  // are Cased-Like-This.
619
- const pattern = /^content-encoding$/i;
620
- return record.responseHeaders.some(item => item.name.match(pattern));
619
+ const patterns = global.isLightrider ? [
620
+ /^x-original-content-encoding$/i,
621
+ ] : [
622
+ /^content-encoding$/i,
623
+ /^x-content-encoding-over-network$/i,
624
+ ];
625
+ const compressionTypes = ['gzip', 'br', 'deflate'];
626
+ return record.responseHeaders.some(header =>
627
+ patterns.some(p => header.name.match(p)) && compressionTypes.includes(header.value)
628
+ );
621
629
  }
622
630
 
623
631
  /**
@@ -0,0 +1,2 @@
1
+ export function polyfillDOMRect(): void;
2
+ //# sourceMappingURL=polyfill-dom-rect.d.ts.map
@@ -0,0 +1,111 @@
1
+ // @ts-nocheck
2
+ /* eslint-disable */
3
+
4
+ function polyfillDOMRect() {
5
+ // devtools assumes clientside :(
6
+
7
+ // Everything else in here is the DOMRect polyfill
8
+ // https://raw.githubusercontent.com/JakeChampion/polyfill-library/master/polyfills/DOMRect/polyfill.js
9
+
10
+ (function (global) {
11
+ function number(v) {
12
+ return v === undefined ? 0 : Number(v);
13
+ }
14
+
15
+ function different(u, v) {
16
+ return u !== v && !(isNaN(u) && isNaN(v));
17
+ }
18
+
19
+ function DOMRect(xArg, yArg, wArg, hArg) {
20
+ let x, y, width, height, left, right, top, bottom;
21
+
22
+ x = number(xArg);
23
+ y = number(yArg);
24
+ width = number(wArg);
25
+ height = number(hArg);
26
+
27
+ Object.defineProperties(this, {
28
+ x: {
29
+ get: function () { return x; },
30
+ set: function (newX) {
31
+ if (different(x, newX)) {
32
+ x = newX;
33
+ left = right = undefined;
34
+ }
35
+ },
36
+ enumerable: true
37
+ },
38
+ y: {
39
+ get: function () { return y; },
40
+ set: function (newY) {
41
+ if (different(y, newY)) {
42
+ y = newY;
43
+ top = bottom = undefined;
44
+ }
45
+ },
46
+ enumerable: true
47
+ },
48
+ width: {
49
+ get: function () { return width; },
50
+ set: function (newWidth) {
51
+ if (different(width, newWidth)) {
52
+ width = newWidth;
53
+ left = right = undefined;
54
+ }
55
+ },
56
+ enumerable: true
57
+ },
58
+ height: {
59
+ get: function () { return height; },
60
+ set: function (newHeight) {
61
+ if (different(height, newHeight)) {
62
+ height = newHeight;
63
+ top = bottom = undefined;
64
+ }
65
+ },
66
+ enumerable: true
67
+ },
68
+ left: {
69
+ get: function () {
70
+ if (left === undefined) {
71
+ left = x + Math.min(0, width);
72
+ }
73
+ return left;
74
+ },
75
+ enumerable: true
76
+ },
77
+ right: {
78
+ get: function () {
79
+ if (right === undefined) {
80
+ right = x + Math.max(0, width);
81
+ }
82
+ return right;
83
+ },
84
+ enumerable: true
85
+ },
86
+ top: {
87
+ get: function () {
88
+ if (top === undefined) {
89
+ top = y + Math.min(0, height);
90
+ }
91
+ return top;
92
+ },
93
+ enumerable: true
94
+ },
95
+ bottom: {
96
+ get: function () {
97
+ if (bottom === undefined) {
98
+ bottom = y + Math.max(0, height);
99
+ }
100
+ return bottom;
101
+ },
102
+ enumerable: true
103
+ }
104
+ });
105
+ }
106
+
107
+ globalThis.DOMRect = DOMRect;
108
+ })(globalThis);
109
+ }
110
+
111
+ export {polyfillDOMRect};
@@ -0,0 +1,7 @@
1
+ /** @type {import('../../types/trace-engine.js').TraceProcessor & typeof import('../../types/trace-engine.js').TraceProcessor} */
2
+ export const TraceProcessor: import('../../types/trace-engine.js').TraceProcessor & typeof import('../../types/trace-engine.js').TraceProcessor;
3
+ /** @type {import('../../types/trace-engine.js').TraceHandlers} */
4
+ export const TraceHandlers: any;
5
+ /** @type {import('../../types/trace-engine.js').RootCauses & typeof import('../../types/trace-engine.js').RootCauses} */
6
+ export const RootCauses: import('../../types/trace-engine.js').RootCauses & typeof import('../../types/trace-engine.js').RootCauses;
7
+ //# sourceMappingURL=trace-engine.d.ts.map
@@ -0,0 +1,19 @@
1
+ // @ts-expect-error missing types
2
+ import * as TraceEngine from '@paulirish/trace_engine';
3
+
4
+ import {polyfillDOMRect} from './polyfill-dom-rect.js';
5
+
6
+ polyfillDOMRect();
7
+
8
+ /** @type {import('../../types/trace-engine.js').TraceProcessor & typeof import('../../types/trace-engine.js').TraceProcessor} */
9
+ const TraceProcessor = TraceEngine.Processor.TraceProcessor;
10
+ /** @type {import('../../types/trace-engine.js').TraceHandlers} */
11
+ const TraceHandlers = TraceEngine.Handlers.ModelHandlers;
12
+ /** @type {import('../../types/trace-engine.js').RootCauses & typeof import('../../types/trace-engine.js').RootCauses} */
13
+ const RootCauses = TraceEngine.RootCauses.RootCauses.RootCauses;
14
+
15
+ export {
16
+ TraceProcessor,
17
+ TraceHandlers,
18
+ RootCauses,
19
+ };
@@ -75,4 +75,21 @@ curl "$url" -Lo chrome.zip && unzip -q chrome.zip && rm chrome.zip
75
75
  mv * "$chrome_out"
76
76
  cd - && rm -rf .tmp-download
77
77
 
78
+ echo "OUTPUT DIR: $chrome_out"
78
79
  ls "$chrome_out"
80
+
81
+ echo "";
82
+ echo "Verifying CHROME_PATH...";
83
+
84
+ if ! [ -f $CHROME_PATH ]; then
85
+ echo "CHROME_PATH does not point to a valid file"
86
+ exit 1
87
+ else
88
+ echo "CHROME_PATH is good!"
89
+ fi
90
+
91
+ # TODO: Find a convenient way to check the version in windows
92
+ if [ "$machine" != "MinGw" ]; then
93
+ echo "CHROME_PATH version:"
94
+ $CHROME_PATH --version
95
+ fi