lighthouse 11.4.0-dev.20231217 → 11.4.0-dev.20231219

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.
@@ -3,7 +3,7 @@
3
3
  exports[`getAssertionReport works (multiple failing) 1`] = `
4
4
  "X difference at cumulative-layout-shift audit.details.items.length
5
5
  expected: []
6
- found: [{\\"cumulativeLayoutShiftMainFrame\\":0.13570762803819444}]
6
+ found: [{\\"cumulativeLayoutShiftMainFrame\\":0.13570762803819444,\\"newEngineResult\\":{\\"cumulativeLayoutShift\\":0.13570762803819444,\\"cumulativeLayoutShiftMainFrame\\":0.13570762803819444},\\"newEngineResultDiffered\\":false}]
7
7
 
8
8
 
9
9
  X difference at cumulative-layout-shift audit.details.blah
@@ -28,7 +28,12 @@ exports[`getAssertionReport works (multiple failing) 1`] = `
28
28
  \\"type\\": \\"debugdata\\",
29
29
  \\"items\\": [
30
30
  {
31
- \\"cumulativeLayoutShiftMainFrame\\": 0.13570762803819444
31
+ \\"cumulativeLayoutShiftMainFrame\\": 0.13570762803819444,
32
+ \\"newEngineResult\\": {
33
+ \\"cumulativeLayoutShift\\": 0.13570762803819444,
34
+ \\"cumulativeLayoutShiftMainFrame\\": 0.13570762803819444
35
+ },
36
+ \\"newEngineResultDiffered\\": false
32
37
  }
33
38
  ]
34
39
  }
@@ -38,7 +43,7 @@ exports[`getAssertionReport works (multiple failing) 1`] = `
38
43
  exports[`getAssertionReport works (trivial failing) 1`] = `
39
44
  "X difference at cumulative-layout-shift audit.details.items.length
40
45
  expected: []
41
- found: [{\\"cumulativeLayoutShiftMainFrame\\":0.13570762803819444}]
46
+ found: [{\\"cumulativeLayoutShiftMainFrame\\":0.13570762803819444,\\"newEngineResult\\":{\\"cumulativeLayoutShift\\":0.13570762803819444,\\"cumulativeLayoutShiftMainFrame\\":0.13570762803819444},\\"newEngineResultDiffered\\":false}]
42
47
 
43
48
  found result:
44
49
  {
@@ -58,7 +63,12 @@ exports[`getAssertionReport works (trivial failing) 1`] = `
58
63
  \\"type\\": \\"debugdata\\",
59
64
  \\"items\\": [
60
65
  {
61
- \\"cumulativeLayoutShiftMainFrame\\": 0.13570762803819444
66
+ \\"cumulativeLayoutShiftMainFrame\\": 0.13570762803819444,
67
+ \\"newEngineResult\\": {
68
+ \\"cumulativeLayoutShift\\": 0.13570762803819444,
69
+ \\"cumulativeLayoutShiftMainFrame\\": 0.13570762803819444
70
+ },
71
+ \\"newEngineResultDiffered\\": false
62
72
  }
63
73
  ]
64
74
  }
@@ -4,6 +4,7 @@ export type LayoutShiftEvent = {
4
4
  isMainFrame: boolean;
5
5
  weightedScore: number;
6
6
  impactedNodes?: LH.Artifacts.TraceImpactedNode[];
7
+ event: LH.TraceEvent;
7
8
  };
8
9
  declare const CumulativeLayoutShiftComputed: typeof CumulativeLayoutShift & {
9
10
  request: (dependencies: import("../../index.js").Trace, context: import("../../../types/utility-types.js").default.ImmutableObject<{
@@ -12,6 +13,11 @@ declare const CumulativeLayoutShiftComputed: typeof CumulativeLayoutShift & {
12
13
  cumulativeLayoutShift: number;
13
14
  cumulativeLayoutShiftMainFrame: number;
14
15
  impactByNodeId: Map<number, number>;
16
+ newEngineResult?: {
17
+ cumulativeLayoutShift: number;
18
+ cumulativeLayoutShiftMainFrame: number;
19
+ } | undefined;
20
+ newEngineResultDiffered: boolean;
15
21
  }>;
16
22
  };
17
23
  declare class CumulativeLayoutShift {
@@ -43,15 +49,28 @@ declare class CumulativeLayoutShift {
43
49
  * @return {number}
44
50
  */
45
51
  static calculate(layoutShiftEvents: Array<LayoutShiftEvent>): number;
52
+ /**
53
+ * @param {LayoutShiftEvent[]} allFrameShiftEvents
54
+ * @param {LayoutShiftEvent[]} mainFrameShiftEvents
55
+ */
56
+ static computeWithSharedTraceEngine(allFrameShiftEvents: LayoutShiftEvent[], mainFrameShiftEvents: LayoutShiftEvent[]): Promise<{
57
+ cumulativeLayoutShift: any;
58
+ cumulativeLayoutShiftMainFrame: any;
59
+ }>;
46
60
  /**
47
61
  * @param {LH.Trace} trace
48
62
  * @param {LH.Artifacts.ComputedContext} context
49
- * @return {Promise<{cumulativeLayoutShift: number, cumulativeLayoutShiftMainFrame: number, impactByNodeId: Map<number, number>}>}
63
+ * @return {Promise<{cumulativeLayoutShift: number, cumulativeLayoutShiftMainFrame: number, impactByNodeId: Map<number, number>, newEngineResult?: {cumulativeLayoutShift: number, cumulativeLayoutShiftMainFrame: number}, newEngineResultDiffered: boolean}>}
50
64
  */
51
65
  static compute_(trace: LH.Trace, context: LH.Artifacts.ComputedContext): Promise<{
52
66
  cumulativeLayoutShift: number;
53
67
  cumulativeLayoutShiftMainFrame: number;
54
68
  impactByNodeId: Map<number, number>;
69
+ newEngineResult?: {
70
+ cumulativeLayoutShift: number;
71
+ cumulativeLayoutShiftMainFrame: number;
72
+ } | undefined;
73
+ newEngineResultDiffered: boolean;
55
74
  }>;
56
75
  }
57
76
  import { ProcessedTrace } from '../processed-trace.js';
@@ -7,8 +7,10 @@
7
7
  import {makeComputedArtifact} from '../computed-artifact.js';
8
8
  import {ProcessedTrace} from '../processed-trace.js';
9
9
  import * as RectHelpers from '../../lib/rect-helpers.js';
10
+ import * as TraceEngine from '../../lib/trace-engine.js';
11
+ import {Sentry} from '../../lib/sentry.js';
10
12
 
11
- /** @typedef {{ts: number, isMainFrame: boolean, weightedScore: number, impactedNodes?: LH.Artifacts.TraceImpactedNode[]}} LayoutShiftEvent */
13
+ /** @typedef {{ts: number, isMainFrame: boolean, weightedScore: number, impactedNodes?: LH.Artifacts.TraceImpactedNode[], event: LH.TraceEvent}} LayoutShiftEvent */
12
14
 
13
15
  const RECENT_INPUT_WINDOW = 500;
14
16
 
@@ -67,6 +69,7 @@ class CumulativeLayoutShift {
67
69
  isMainFrame: event.args.data.is_main_frame,
68
70
  weightedScore: event.args.data.weighted_score_delta,
69
71
  impactedNodes: event.args.data.impacted_nodes,
72
+ event,
70
73
  });
71
74
  }
72
75
 
@@ -145,10 +148,29 @@ class CumulativeLayoutShift {
145
148
  return maxScore;
146
149
  }
147
150
 
151
+ /**
152
+ * @param {LayoutShiftEvent[]} allFrameShiftEvents
153
+ * @param {LayoutShiftEvent[]} mainFrameShiftEvents
154
+ */
155
+ static async computeWithSharedTraceEngine(allFrameShiftEvents, mainFrameShiftEvents) {
156
+ /** @param {LH.TraceEvent[]} events */
157
+ const run = async (events) => {
158
+ const processor = new TraceEngine.TraceProcessor({
159
+ LayoutShifts: TraceEngine.TraceHandlers.LayoutShifts,
160
+ Screenshots: TraceEngine.TraceHandlers.Screenshots,
161
+ });
162
+ await processor.parse(events);
163
+ return processor.data.LayoutShifts.sessionMaxScore;
164
+ };
165
+ const cumulativeLayoutShift = await run(allFrameShiftEvents.map(e => e.event));
166
+ const cumulativeLayoutShiftMainFrame = await run(mainFrameShiftEvents.map(e => e.event));
167
+ return {cumulativeLayoutShift, cumulativeLayoutShiftMainFrame};
168
+ }
169
+
148
170
  /**
149
171
  * @param {LH.Trace} trace
150
172
  * @param {LH.Artifacts.ComputedContext} context
151
- * @return {Promise<{cumulativeLayoutShift: number, cumulativeLayoutShiftMainFrame: number, impactByNodeId: Map<number, number>}>}
173
+ * @return {Promise<{cumulativeLayoutShift: number, cumulativeLayoutShiftMainFrame: number, impactByNodeId: Map<number, number>, newEngineResult?: {cumulativeLayoutShift: number, cumulativeLayoutShiftMainFrame: number}, newEngineResultDiffered: boolean}>}
152
174
  */
153
175
  static async compute_(trace, context) {
154
176
  const processedTrace = await ProcessedTrace.request(trace, context);
@@ -157,11 +179,59 @@ class CumulativeLayoutShift {
157
179
  CumulativeLayoutShift.getLayoutShiftEvents(processedTrace);
158
180
  const impactByNodeId = CumulativeLayoutShift.getImpactByNodeId(allFrameShiftEvents);
159
181
  const mainFrameShiftEvents = allFrameShiftEvents.filter(e => e.isMainFrame);
182
+ const cumulativeLayoutShift = CumulativeLayoutShift.calculate(allFrameShiftEvents);
183
+ const cumulativeLayoutShiftMainFrame = CumulativeLayoutShift.calculate(mainFrameShiftEvents);
184
+
185
+ // Run with the new trace engine, and only throw an error if we are running our unit tests.
186
+ // Otherwise, simply report any differences or errors to Sentry.
187
+ // TODO: TraceEngine always drops `had_recent_input` events, but Lighthouse is more lenient.
188
+ // See comment in `getLayoutShiftEvents`. We need to upstream this.
189
+ let newEngineResult;
190
+ let newEngineResultDiffered = false;
191
+ let tryNewTraceEngine = true;
192
+ if (allFrameShiftEvents.some(e => e.event.args.data?.had_recent_input)) {
193
+ tryNewTraceEngine = false;
194
+ }
195
+ if (tryNewTraceEngine) {
196
+ try {
197
+ newEngineResult =
198
+ await this.computeWithSharedTraceEngine(allFrameShiftEvents, mainFrameShiftEvents);
199
+ newEngineResultDiffered =
200
+ newEngineResult.cumulativeLayoutShift !== cumulativeLayoutShift ||
201
+ newEngineResult.cumulativeLayoutShiftMainFrame !== cumulativeLayoutShiftMainFrame;
202
+ if (newEngineResultDiffered) {
203
+ newEngineResultDiffered = true;
204
+ const expected = JSON.stringify({cumulativeLayoutShift, cumulativeLayoutShiftMainFrame});
205
+ const got = JSON.stringify(newEngineResult);
206
+ throw new Error(`new trace engine differed. expected: ${expected}, got: ${got}`);
207
+ }
208
+ } catch (err) {
209
+ console.error(err);
210
+ newEngineResultDiffered = true;
211
+
212
+ const error = new Error('Error when using new trace engine', {cause: err});
213
+ // @ts-expect-error Check for running from tests.
214
+ if (global.expect) {
215
+ throw error;
216
+ } else {
217
+ Sentry.captureException(error, {
218
+ tags: {computed: 'new-trace-engine'},
219
+ level: 'error',
220
+ extra: {
221
+ // Not sure if Sentry handles `cause`, so just in case add the info in a second place.
222
+ errorMsg: err.toString(),
223
+ },
224
+ });
225
+ }
226
+ }
227
+ }
160
228
 
161
229
  return {
162
- cumulativeLayoutShift: CumulativeLayoutShift.calculate(allFrameShiftEvents),
163
- cumulativeLayoutShiftMainFrame: CumulativeLayoutShift.calculate(mainFrameShiftEvents),
230
+ cumulativeLayoutShift,
231
+ cumulativeLayoutShiftMainFrame,
164
232
  impactByNodeId,
233
+ newEngineResult,
234
+ newEngineResultDiffered,
165
235
  };
166
236
  }
167
237
  }
@@ -23,12 +23,6 @@ import {fetchResponseBodyFromCache} from '../../driver/network.js';
23
23
  import {NetworkRecords} from '../../../computed/network-records.js';
24
24
 
25
25
  const CHROME_EXTENSION_PROTOCOL = 'chrome-extension:';
26
- const compressionHeaders = [
27
- 'content-encoding',
28
- 'x-original-content-encoding',
29
- 'x-content-encoding-over-network',
30
- ];
31
- const compressionTypes = ['gzip', 'br', 'deflate'];
32
26
  const binaryMimeTypes = ['image', 'audio', 'video'];
33
27
  /** @type {LH.Crdp.Network.ResourceType[]} */
34
28
  const textResourceTypes = [
@@ -71,12 +65,7 @@ class ResponseCompression extends BaseGatherer {
71
65
  return;
72
66
  }
73
67
 
74
- const isContentEncoded = (record.responseHeaders || []).find(header =>
75
- compressionHeaders.includes(header.name.toLowerCase()) &&
76
- compressionTypes.includes(header.value)
77
- );
78
-
79
- if (!isContentEncoded) {
68
+ if (!NetworkRequest.isContentEncoded(record)) {
80
69
  unoptimizedResponses.push({
81
70
  requestId: record.requestId,
82
71
  url: record.url,
@@ -56,9 +56,9 @@ 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
+ // Not used by Lighthouse (yet) but included for users that want JS samples when looking at
60
+ // a trace collected by Lighthouse (e.g. "View Trace" workflow in DevTools)
61
+ 'disabled-by-default-v8.cpu_profiler',
62
62
  ];
63
63
  }
64
64
 
@@ -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,4 @@
1
+ export const TraceProcessor: any;
2
+ export const TraceHandlers: any;
3
+ export const RootCauses: any;
4
+ //# sourceMappingURL=trace-engine.d.ts.map
@@ -0,0 +1,16 @@
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
+ const TraceProcessor = TraceEngine.Processor.TraceProcessor;
9
+ const TraceHandlers = TraceEngine.Handlers.ModelHandlers;
10
+ const RootCauses = TraceEngine.RootCauses.RootCauses.RootCauses;
11
+
12
+ export {
13
+ TraceProcessor,
14
+ TraceHandlers,
15
+ RootCauses,
16
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "lighthouse",
3
3
  "type": "module",
4
- "version": "11.4.0-dev.20231217",
4
+ "version": "11.4.0-dev.20231219",
5
5
  "description": "Automated auditing, performance metrics, and best practices for the web.",
6
6
  "main": "./core/index.js",
7
7
  "bin": {
@@ -180,6 +180,7 @@
180
180
  "webtreemap-cdt": "^3.2.1"
181
181
  },
182
182
  "dependencies": {
183
+ "@paulirish/trace_engine": "^0.0.7",
183
184
  "@sentry/node": "^6.17.4",
184
185
  "axe-core": "^4.8.1",
185
186
  "chrome-launcher": "^1.1.0",