lighthouse 9.5.0-dev.20230124 → 9.5.0-dev.20230126
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/core/audits/accessibility/accesskeys.js +1 -1
- package/core/audits/accessibility/aria-allowed-attr.js +1 -1
- package/core/audits/accessibility/aria-command-name.js +1 -1
- package/core/audits/accessibility/aria-hidden-body.js +1 -1
- package/core/audits/accessibility/aria-hidden-focus.js +1 -1
- package/core/audits/accessibility/aria-input-field-name.js +1 -1
- package/core/audits/accessibility/aria-meter-name.js +1 -1
- package/core/audits/accessibility/aria-progressbar-name.js +1 -1
- package/core/audits/accessibility/aria-required-attr.js +1 -1
- package/core/audits/accessibility/aria-required-children.js +1 -1
- package/core/audits/accessibility/aria-required-parent.js +1 -1
- package/core/audits/accessibility/aria-roles.js +1 -1
- package/core/audits/accessibility/aria-toggle-field-name.js +1 -1
- package/core/audits/accessibility/aria-tooltip-name.js +1 -1
- package/core/audits/accessibility/aria-treeitem-name.js +1 -1
- package/core/audits/accessibility/aria-valid-attr-value.js +1 -1
- package/core/audits/accessibility/aria-valid-attr.js +1 -1
- package/core/audits/accessibility/button-name.js +1 -1
- package/core/audits/accessibility/bypass.js +1 -1
- package/core/audits/accessibility/color-contrast.js +1 -1
- package/core/audits/accessibility/definition-list.js +1 -1
- package/core/audits/accessibility/dlitem.js +1 -1
- package/core/audits/accessibility/document-title.js +1 -1
- package/core/audits/accessibility/duplicate-id-active.js +1 -1
- package/core/audits/accessibility/duplicate-id-aria.js +1 -1
- package/core/audits/accessibility/form-field-multiple-labels.js +1 -1
- package/core/audits/accessibility/frame-title.js +1 -1
- package/core/audits/accessibility/heading-order.js +1 -1
- package/core/audits/accessibility/html-has-lang.js +1 -1
- package/core/audits/accessibility/html-lang-valid.js +1 -1
- package/core/audits/accessibility/image-alt.js +1 -1
- package/core/audits/accessibility/input-image-alt.js +1 -1
- package/core/audits/accessibility/label.js +1 -1
- package/core/audits/accessibility/link-name.js +1 -1
- package/core/audits/accessibility/list.js +1 -1
- package/core/audits/accessibility/listitem.js +1 -1
- package/core/audits/accessibility/meta-refresh.js +1 -1
- package/core/audits/accessibility/meta-viewport.js +1 -1
- package/core/audits/accessibility/object-alt.js +1 -1
- package/core/audits/accessibility/tabindex.js +1 -1
- package/core/audits/accessibility/td-headers-attr.js +1 -1
- package/core/audits/accessibility/th-has-data-cells.js +1 -1
- package/core/audits/accessibility/valid-lang.js +1 -1
- package/core/audits/accessibility/video-caption.js +1 -1
- package/core/audits/byte-efficiency/legacy-javascript.js +1 -2
- package/core/audits/network-requests.js +7 -6
- package/core/audits/preload-lcp-image.d.ts +12 -3
- package/core/audits/preload-lcp-image.js +32 -13
- package/core/computed/entity-classification.js +1 -1
- package/core/computed/metrics/responsiveness.js +5 -2
- package/core/computed/page-dependency-graph.js +1 -1
- package/core/config/constants.d.ts +2 -2
- package/core/config/constants.js +2 -2
- package/core/gather/gatherers/accessibility.js +3 -1
- package/core/gather/gatherers/trace-elements.js +1 -1
- package/core/lib/network-request.d.ts +2 -0
- package/core/lib/network-request.js +3 -0
- package/core/lib/stack-packs.js +4 -0
- package/core/lib/tracehouse/trace-processor.d.ts +14 -5
- package/core/lib/tracehouse/trace-processor.js +85 -30
- package/core/lib/traces/metric-trace-events.js +1 -1
- package/dist/report/bundle.esm.js +1 -1
- package/dist/report/flow.js +1 -1
- package/dist/report/standalone.js +1 -1
- package/package.json +4 -5
- package/report/assets/styles.css +1 -1
- package/report/renderer/components.js +1 -1
- package/report/test/generator/report-generator-test.js +2 -2
- package/report/test/renderer/performance-category-renderer-test.js +2 -2
- package/shared/localization/locales/en-US.json +74 -44
- package/shared/localization/locales/en-XL.json +74 -44
- package/tsconfig.json +0 -2
- package/types/artifacts.d.ts +9 -2
- package/core/lib/minify-trace.d.ts +0 -6
- package/core/lib/minify-trace.js +0 -172
- package/core/util.cjs +0 -845
- package/core/util.d.cts +0 -342
|
@@ -11,6 +11,7 @@ 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
13
|
import {ByteEfficiencyAudit} from './byte-efficiency/byte-efficiency-audit.js';
|
|
14
|
+
import {ProcessedNavigation} from '../computed/processed-navigation.js';
|
|
14
15
|
|
|
15
16
|
const UIStrings = {
|
|
16
17
|
/** Title of a lighthouse audit that tells a user to preload an image in order to improve their LCP time. */
|
|
@@ -36,8 +37,7 @@ class PreloadLCPImageAudit extends Audit {
|
|
|
36
37
|
title: str_(UIStrings.title),
|
|
37
38
|
description: str_(UIStrings.description),
|
|
38
39
|
supportedModes: ['navigation'],
|
|
39
|
-
requiredArtifacts: ['traces', 'devtoolsLogs', 'GatherContext', 'URL', 'TraceElements',
|
|
40
|
-
'ImageElements'],
|
|
40
|
+
requiredArtifacts: ['traces', 'devtoolsLogs', 'GatherContext', 'URL', 'TraceElements'],
|
|
41
41
|
scoreDisplayMode: Audit.SCORING_MODES.NUMERIC,
|
|
42
42
|
};
|
|
43
43
|
}
|
|
@@ -87,17 +87,11 @@ class PreloadLCPImageAudit extends Audit {
|
|
|
87
87
|
/**
|
|
88
88
|
* @param {LH.Artifacts.NetworkRequest} mainResource
|
|
89
89
|
* @param {LH.Gatherer.Simulation.GraphNode} graph
|
|
90
|
-
* @param {
|
|
91
|
-
* @param {Array<LH.Artifacts.ImageElement>} imageElements
|
|
90
|
+
* @param {string | undefined} lcpUrl
|
|
92
91
|
* @return {{lcpNodeToPreload?: LH.Gatherer.Simulation.GraphNetworkNode, initiatorPath?: InitiatorPath}}
|
|
93
92
|
*/
|
|
94
|
-
static getLCPNodeToPreload(mainResource, graph,
|
|
95
|
-
|
|
96
|
-
return elem.node.devtoolsNodePath === lcpElement.node.devtoolsNodePath;
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
if (!lcpImageElement) return {};
|
|
100
|
-
const lcpUrl = lcpImageElement.src;
|
|
93
|
+
static getLCPNodeToPreload(mainResource, graph, lcpUrl) {
|
|
94
|
+
if (!lcpUrl) return {};
|
|
101
95
|
const {lcpNode, path} = PreloadLCPImageAudit.findLCPNode(graph, lcpUrl);
|
|
102
96
|
if (!lcpNode || !path) return {};
|
|
103
97
|
|
|
@@ -116,6 +110,29 @@ class PreloadLCPImageAudit extends Audit {
|
|
|
116
110
|
};
|
|
117
111
|
}
|
|
118
112
|
|
|
113
|
+
/**
|
|
114
|
+
* Match the LCP event with the paint event to get the URL of the image actually painted.
|
|
115
|
+
* This could differ from the `ImageElement` associated with the nodeId if e.g. the LCP
|
|
116
|
+
* was a pseudo-element associated with a node containing a smaller background-image.
|
|
117
|
+
* @param {LH.Trace} trace
|
|
118
|
+
* @param {LH.Artifacts.ProcessedNavigation} processedNavigation
|
|
119
|
+
* @return {string | undefined}
|
|
120
|
+
*/
|
|
121
|
+
static getLcpUrl(trace, processedNavigation) {
|
|
122
|
+
const lcpEvent = processedNavigation.largestContentfulPaintAllFramesEvt;
|
|
123
|
+
if (!lcpEvent) return;
|
|
124
|
+
|
|
125
|
+
const lcpImagePaintEvent = trace.traceEvents.filter(e => {
|
|
126
|
+
return e.name === 'LargestImagePaint::Candidate' &&
|
|
127
|
+
e.args.frame === lcpEvent.args.frame &&
|
|
128
|
+
e.args.data?.DOMNodeId === lcpEvent.args.data?.nodeId &&
|
|
129
|
+
e.args.data?.size === lcpEvent.args.data?.size;
|
|
130
|
+
// Get last candidate, in case there was more than one.
|
|
131
|
+
}).sort((a, b) => b.ts - a.ts)[0];
|
|
132
|
+
|
|
133
|
+
return lcpImagePaintEvent?.args.data?.imageUrl;
|
|
134
|
+
}
|
|
135
|
+
|
|
119
136
|
/**
|
|
120
137
|
* Computes the estimated effect of preloading the LCP image.
|
|
121
138
|
* @param {LH.Artifacts.TraceElement} lcpElement
|
|
@@ -223,15 +240,17 @@ class PreloadLCPImageAudit extends Audit {
|
|
|
223
240
|
return {score: null, notApplicable: true};
|
|
224
241
|
}
|
|
225
242
|
|
|
226
|
-
const [mainResource, lanternLCP, simulator] = await Promise.all([
|
|
243
|
+
const [processedNavigation, mainResource, lanternLCP, simulator] = await Promise.all([
|
|
244
|
+
ProcessedNavigation.request(trace, context),
|
|
227
245
|
MainResource.request({devtoolsLog, URL}, context),
|
|
228
246
|
LanternLargestContentfulPaint.request(metricData, context),
|
|
229
247
|
LoadSimulator.request({devtoolsLog, settings: context.settings}, context),
|
|
230
248
|
]);
|
|
231
249
|
|
|
250
|
+
const lcpUrl = PreloadLCPImageAudit.getLcpUrl(trace, processedNavigation);
|
|
232
251
|
const graph = lanternLCP.pessimisticGraph;
|
|
233
252
|
// eslint-disable-next-line max-len
|
|
234
|
-
const {lcpNodeToPreload, initiatorPath} = PreloadLCPImageAudit.getLCPNodeToPreload(mainResource, graph,
|
|
253
|
+
const {lcpNodeToPreload, initiatorPath} = PreloadLCPImageAudit.getLCPNodeToPreload(mainResource, graph, lcpUrl);
|
|
235
254
|
|
|
236
255
|
const {results, wastedMs} =
|
|
237
256
|
PreloadLCPImageAudit.computeWasteWithGraph(lcpElement, lcpNodeToPreload, graph, simulator);
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import {makeComputedArtifact} from './computed-artifact.js';
|
|
8
8
|
import {NetworkRecords} from './network-records.js';
|
|
9
|
-
import {Util} from '
|
|
9
|
+
import {Util} from '../../shared/util.js';
|
|
10
10
|
import UrlUtils from '../lib/url-utils.js';
|
|
11
11
|
import thirdPartyWeb from '../lib/third-party-web.js';
|
|
12
12
|
|
|
@@ -87,8 +87,11 @@ class Responsiveness {
|
|
|
87
87
|
return evt.name === 'EventTiming' && evt.ph !== 'e';
|
|
88
88
|
});
|
|
89
89
|
|
|
90
|
-
|
|
91
|
-
|
|
90
|
+
// If trace is from < m103, the timestamps cannot be trusted, so we craft a fallback
|
|
91
|
+
// <m103 traces (bad) had a args.frame
|
|
92
|
+
// m103+ traces (good) have a args.data.frame (https://crrev.com/c/3632661)
|
|
93
|
+
// TODO(compat): remove FallbackTiming handling when we don't care about <m103
|
|
94
|
+
if (candidates.length && candidates.every(candidate => !candidate.args.data?.frame)) {
|
|
92
95
|
return {
|
|
93
96
|
name: 'FallbackTiming',
|
|
94
97
|
duration: responsivenessEvent.args.data.maxDuration,
|
|
@@ -468,7 +468,7 @@ class PageDependencyGraph {
|
|
|
468
468
|
* @return {URLArtifact}
|
|
469
469
|
*/
|
|
470
470
|
static getDocumentUrls(devtoolsLog, networkRecords, processedTrace) {
|
|
471
|
-
const mainFrameId = processedTrace.
|
|
471
|
+
const mainFrameId = processedTrace.mainFrameInfo.frameId;
|
|
472
472
|
|
|
473
473
|
/** @type {string|undefined} */
|
|
474
474
|
let requestedUrl;
|
|
@@ -80,7 +80,7 @@ declare const MOTOG4_EMULATION_METRICS: Required<LH.SharedFlagsSettings['screenE
|
|
|
80
80
|
* @type {Required<LH.SharedFlagsSettings['screenEmulation']>}
|
|
81
81
|
*/
|
|
82
82
|
declare const DESKTOP_EMULATION_METRICS: Required<LH.SharedFlagsSettings['screenEmulation']>;
|
|
83
|
-
declare const MOTOG4_USERAGENT: "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/
|
|
84
|
-
declare const DESKTOP_USERAGENT: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/
|
|
83
|
+
declare const MOTOG4_USERAGENT: "Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Mobile Safari/537.36";
|
|
84
|
+
declare const DESKTOP_USERAGENT: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36";
|
|
85
85
|
export {};
|
|
86
86
|
//# sourceMappingURL=constants.d.ts.map
|
package/core/config/constants.js
CHANGED
|
@@ -81,8 +81,8 @@ const screenEmulationMetrics = {
|
|
|
81
81
|
};
|
|
82
82
|
|
|
83
83
|
|
|
84
|
-
const MOTOG4_USERAGENT = 'Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/
|
|
85
|
-
const DESKTOP_USERAGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/
|
|
84
|
+
const MOTOG4_USERAGENT = 'Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Mobile Safari/537.36'; // eslint-disable-line max-len
|
|
85
|
+
const DESKTOP_USERAGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36'; // eslint-disable-line max-len
|
|
86
86
|
|
|
87
87
|
const userAgents = {
|
|
88
88
|
mobile: MOTOG4_USERAGENT,
|
|
@@ -61,10 +61,12 @@ async function runA11yChecks() {
|
|
|
61
61
|
'frame-focusable-content': {enabled: false},
|
|
62
62
|
'aria-roledescription': {enabled: false},
|
|
63
63
|
'scrollable-region-focusable': {enabled: false},
|
|
64
|
-
// TODO(paulirish): create audits and enable these
|
|
64
|
+
// TODO(paulirish): create audits and enable these 5.
|
|
65
65
|
'input-button-name': {enabled: false},
|
|
66
66
|
'role-img-alt': {enabled: false},
|
|
67
67
|
'select-name': {enabled: false},
|
|
68
|
+
'link-in-text-block': {enabled: false},
|
|
69
|
+
'frame-title-unique': {enabled: false},
|
|
68
70
|
},
|
|
69
71
|
});
|
|
70
72
|
|
|
@@ -231,7 +231,7 @@ class TraceElements extends FRGatherer {
|
|
|
231
231
|
}
|
|
232
232
|
|
|
233
233
|
// These should exist, but trace types are loose.
|
|
234
|
-
const lcpData = processedNavigation.
|
|
234
|
+
const lcpData = processedNavigation.largestContentfulPaintAllFramesEvt?.args?.data;
|
|
235
235
|
if (lcpData?.nodeId === undefined || !lcpData.type) return;
|
|
236
236
|
|
|
237
237
|
return {
|
|
@@ -86,6 +86,8 @@ export class NetworkRequest {
|
|
|
86
86
|
isValid: boolean;
|
|
87
87
|
parsedURL: ParsedURL;
|
|
88
88
|
documentURL: string;
|
|
89
|
+
/** When the renderer process initially discovers a network request, in milliseconds. */
|
|
90
|
+
rendererStartTime: number;
|
|
89
91
|
/**
|
|
90
92
|
* When the network service is about to handle a request, ie. just before going to the
|
|
91
93
|
* HTTP cache or going to the network for DNS/connection setup, in milliseconds.
|
|
@@ -125,6 +125,8 @@ class NetworkRequest {
|
|
|
125
125
|
this.parsedURL = /** @type {ParsedURL} */ ({scheme: ''});
|
|
126
126
|
this.documentURL = '';
|
|
127
127
|
|
|
128
|
+
/** When the renderer process initially discovers a network request, in milliseconds. */
|
|
129
|
+
this.rendererStartTime = -1;
|
|
128
130
|
/**
|
|
129
131
|
* When the network service is about to handle a request, ie. just before going to the
|
|
130
132
|
* HTTP cache or going to the network for DNS/connection setup, in milliseconds.
|
|
@@ -222,6 +224,7 @@ class NetworkRequest {
|
|
|
222
224
|
};
|
|
223
225
|
this.isSecure = UrlUtils.isSecureScheme(this.parsedURL.scheme);
|
|
224
226
|
|
|
227
|
+
this.rendererStartTime = data.timestamp * 1000;
|
|
225
228
|
// Expected to be overriden with better value in `_recomputeTimesWithResourceTiming`.
|
|
226
229
|
this.startTime = data.timestamp * 1000;
|
|
227
230
|
|
package/core/lib/stack-packs.js
CHANGED
|
@@ -148,13 +148,22 @@ export class TraceProcessor {
|
|
|
148
148
|
static getMainThreadTopLevelEvents(trace: LH.Artifacts.ProcessedTrace, startTime?: number | undefined, endTime?: number | undefined): Array<ToplevelEvent>;
|
|
149
149
|
/**
|
|
150
150
|
* @param {LH.TraceEvent[]} events
|
|
151
|
-
* @return {{
|
|
151
|
+
* @return {{startingPid: number, frameId: string}}
|
|
152
152
|
*/
|
|
153
153
|
static findMainFrameIds(events: LH.TraceEvent[]): {
|
|
154
|
-
|
|
155
|
-
tid: number;
|
|
154
|
+
startingPid: number;
|
|
156
155
|
frameId: string;
|
|
157
156
|
};
|
|
157
|
+
/**
|
|
158
|
+
* If there were any cross-origin navigations, there'll be more than one pid returned
|
|
159
|
+
* @param {{startingPid: number, frameId: string}} mainFrameInfo
|
|
160
|
+
* @param {LH.TraceEvent[]} keyEvents
|
|
161
|
+
* @return {Map<number, number>} Map where keys are process IDs and their values are thread IDs
|
|
162
|
+
*/
|
|
163
|
+
static findMainFramePidTids(mainFrameInfo: {
|
|
164
|
+
startingPid: number;
|
|
165
|
+
frameId: string;
|
|
166
|
+
}, keyEvents: LH.TraceEvent[]): Map<number, number>;
|
|
158
167
|
/**
|
|
159
168
|
* @param {LH.TraceEvent} evt
|
|
160
169
|
* @return {boolean}
|
|
@@ -239,14 +248,14 @@ export class TraceProcessor {
|
|
|
239
248
|
* Can also be skewed by several hundred milliseconds or even seconds when the browser takes a long
|
|
240
249
|
* time to unload `about:blank`.
|
|
241
250
|
*
|
|
242
|
-
* @param {{keyEvents: Array<LH.TraceEvent>, frameEvents: Array<LH.TraceEvent>,
|
|
251
|
+
* @param {{keyEvents: Array<LH.TraceEvent>, frameEvents: Array<LH.TraceEvent>, mainFrameInfo: {frameId: string}}} traceEventSubsets
|
|
243
252
|
* @param {TimeOriginDeterminationMethod} method
|
|
244
253
|
* @return {LH.TraceEvent}
|
|
245
254
|
*/
|
|
246
255
|
static computeTimeOrigin(traceEventSubsets: {
|
|
247
256
|
keyEvents: Array<LH.TraceEvent>;
|
|
248
257
|
frameEvents: Array<LH.TraceEvent>;
|
|
249
|
-
|
|
258
|
+
mainFrameInfo: {
|
|
250
259
|
frameId: string;
|
|
251
260
|
};
|
|
252
261
|
}, method: TimeOriginDeterminationMethod): LH.TraceEvent;
|
|
@@ -423,7 +423,7 @@ class TraceProcessor {
|
|
|
423
423
|
|
|
424
424
|
/**
|
|
425
425
|
* @param {LH.TraceEvent[]} events
|
|
426
|
-
* @return {{
|
|
426
|
+
* @return {{startingPid: number, frameId: string}}
|
|
427
427
|
*/
|
|
428
428
|
static findMainFrameIds(events) {
|
|
429
429
|
// Prefer the newer TracingStartedInBrowser event first, if it exists
|
|
@@ -433,14 +433,9 @@ class TraceProcessor {
|
|
|
433
433
|
const frameId = mainFrame?.frame;
|
|
434
434
|
const pid = mainFrame?.processId;
|
|
435
435
|
|
|
436
|
-
|
|
437
|
-
e.cat === '__metadata' && e.name === 'thread_name' && e.args.name === 'CrRendererMain');
|
|
438
|
-
const tid = threadNameEvt?.tid;
|
|
439
|
-
|
|
440
|
-
if (pid && tid && frameId) {
|
|
436
|
+
if (pid && frameId) {
|
|
441
437
|
return {
|
|
442
|
-
pid,
|
|
443
|
-
tid,
|
|
438
|
+
startingPid: pid,
|
|
444
439
|
frameId,
|
|
445
440
|
};
|
|
446
441
|
}
|
|
@@ -454,8 +449,7 @@ class TraceProcessor {
|
|
|
454
449
|
const frameId = startedInPageEvt.args.data.page;
|
|
455
450
|
if (frameId) {
|
|
456
451
|
return {
|
|
457
|
-
|
|
458
|
-
tid: startedInPageEvt.tid,
|
|
452
|
+
startingPid: startedInPageEvt.pid,
|
|
459
453
|
frameId,
|
|
460
454
|
};
|
|
461
455
|
}
|
|
@@ -478,8 +472,7 @@ class TraceProcessor {
|
|
|
478
472
|
const frameId = navStartEvt.args.frame;
|
|
479
473
|
if (frameId) {
|
|
480
474
|
return {
|
|
481
|
-
|
|
482
|
-
tid: navStartEvt.tid,
|
|
475
|
+
startingPid: navStartEvt.pid,
|
|
483
476
|
frameId,
|
|
484
477
|
};
|
|
485
478
|
}
|
|
@@ -488,6 +481,47 @@ class TraceProcessor {
|
|
|
488
481
|
throw this.createNoTracingStartedError();
|
|
489
482
|
}
|
|
490
483
|
|
|
484
|
+
/**
|
|
485
|
+
* If there were any cross-origin navigations, there'll be more than one pid returned
|
|
486
|
+
* @param {{startingPid: number, frameId: string}} mainFrameInfo
|
|
487
|
+
* @param {LH.TraceEvent[]} keyEvents
|
|
488
|
+
* @return {Map<number, number>} Map where keys are process IDs and their values are thread IDs
|
|
489
|
+
*/
|
|
490
|
+
static findMainFramePidTids(mainFrameInfo, keyEvents) {
|
|
491
|
+
const frameCommittedEvts = keyEvents.filter(evt =>
|
|
492
|
+
evt.name === 'FrameCommittedInBrowser' &&
|
|
493
|
+
evt.args?.data?.frame === mainFrameInfo.frameId
|
|
494
|
+
);
|
|
495
|
+
|
|
496
|
+
// "Modern" traces with a navigation have a FrameCommittedInBrowser event
|
|
497
|
+
const mainFramePids = frameCommittedEvts.length
|
|
498
|
+
? frameCommittedEvts.map(e => e?.args?.data?.processId)
|
|
499
|
+
// …But old traces and some timespan traces may not. In these situations, we'll assume the
|
|
500
|
+
// primary process ID remains constant (as there were no navigations).
|
|
501
|
+
: [mainFrameInfo.startingPid];
|
|
502
|
+
|
|
503
|
+
const pidToTid = new Map();
|
|
504
|
+
|
|
505
|
+
mainFramePids.forEach(pid => {
|
|
506
|
+
// While renderer tids are generally predictable, we'll doublecheck it
|
|
507
|
+
const threadNameEvt = keyEvents.find(e =>
|
|
508
|
+
e.cat === '__metadata' &&
|
|
509
|
+
e.pid === pid &&
|
|
510
|
+
e.ph === 'M' &&
|
|
511
|
+
e.name === 'thread_name' &&
|
|
512
|
+
e.args.name === 'CrRendererMain'
|
|
513
|
+
);
|
|
514
|
+
const tid = threadNameEvt?.tid;
|
|
515
|
+
|
|
516
|
+
if (!tid) {
|
|
517
|
+
throw new Error('Unable to determine tid for renderer process');
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
pidToTid.set(pid, tid);
|
|
521
|
+
});
|
|
522
|
+
return pidToTid;
|
|
523
|
+
}
|
|
524
|
+
|
|
491
525
|
/**
|
|
492
526
|
* @param {LH.TraceEvent} evt
|
|
493
527
|
* @return {boolean}
|
|
@@ -583,6 +617,9 @@ class TraceProcessor {
|
|
|
583
617
|
while (parentFrames.has(cur)) {
|
|
584
618
|
cur = /** @type {string} */ (parentFrames.get(cur));
|
|
585
619
|
}
|
|
620
|
+
if (cur === undefined) {
|
|
621
|
+
throw new Error('Unexpected undefined frameId');
|
|
622
|
+
}
|
|
586
623
|
frameIdToRootFrameId.set(frame.id, cur);
|
|
587
624
|
}
|
|
588
625
|
|
|
@@ -609,7 +646,15 @@ class TraceProcessor {
|
|
|
609
646
|
});
|
|
610
647
|
|
|
611
648
|
// Find the inspected frame
|
|
612
|
-
const
|
|
649
|
+
const mainFrameInfo = this.findMainFrameIds(keyEvents);
|
|
650
|
+
const rendererPidToTid = this.findMainFramePidTids(mainFrameInfo, keyEvents);
|
|
651
|
+
|
|
652
|
+
// Subset all trace events to just our tab's process (incl threads other than main)
|
|
653
|
+
// stable-sort events to keep them correctly nested.
|
|
654
|
+
const processEvents = TraceProcessor
|
|
655
|
+
.filteredTraceSort(trace.traceEvents, e => rendererPidToTid.has(e.pid));
|
|
656
|
+
|
|
657
|
+
// TODO(paulirish): filter down frames (and subsequent actions) to the primary process tree & frame tree
|
|
613
658
|
|
|
614
659
|
/** @type {Map<string, {id: string, url: string, parent?: string}>} */
|
|
615
660
|
const framesById = new Map();
|
|
@@ -645,18 +690,32 @@ class TraceProcessor {
|
|
|
645
690
|
parent: evt.args.data.parent,
|
|
646
691
|
});
|
|
647
692
|
});
|
|
693
|
+
|
|
648
694
|
const frames = [...framesById.values()];
|
|
649
695
|
const frameIdToRootFrameId = this.resolveRootFrames(frames);
|
|
650
696
|
|
|
697
|
+
const inspectedTreeFrameIds = [...frameIdToRootFrameId.entries()]
|
|
698
|
+
.filter(([, rootFrameId]) => rootFrameId === mainFrameInfo.frameId)
|
|
699
|
+
.map(([child]) => child);
|
|
700
|
+
|
|
651
701
|
// Filter to just events matching the main frame ID, just to make sure.
|
|
652
|
-
|
|
702
|
+
/** @param {LH.TraceEvent} e */
|
|
703
|
+
function associatedToMainFrame(e) {
|
|
704
|
+
const frameId = e.args?.data?.frame || e.args.frame;
|
|
705
|
+
return frameId === mainFrameInfo.frameId;
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
/** @param {LH.TraceEvent} e */
|
|
709
|
+
function associatedToAllFrames(e) {
|
|
710
|
+
const frameId = e.args?.data?.frame || e.args.frame;
|
|
711
|
+
return frameId ? inspectedTreeFrameIds.includes(frameId) : false;
|
|
712
|
+
}
|
|
713
|
+
const frameEvents = keyEvents.filter(e => associatedToMainFrame(e));
|
|
653
714
|
|
|
654
715
|
// Filter to just events matching the main frame ID or any child frame IDs.
|
|
655
716
|
let frameTreeEvents = [];
|
|
656
|
-
if (frameIdToRootFrameId.has(
|
|
657
|
-
frameTreeEvents = keyEvents.filter(e =>
|
|
658
|
-
return e.args.frame && frameIdToRootFrameId.get(e.args.frame) === mainFrameIds.frameId;
|
|
659
|
-
});
|
|
717
|
+
if (frameIdToRootFrameId.has(mainFrameInfo.frameId)) {
|
|
718
|
+
frameTreeEvents = keyEvents.filter(e => associatedToAllFrames(e));
|
|
660
719
|
} else {
|
|
661
720
|
// In practice, there should always be TracingStartedInBrowser/FrameCommittedInBrowser events to
|
|
662
721
|
// define the frame tree. Unfortunately, many test traces do not that frame info due to minification.
|
|
@@ -665,23 +724,17 @@ class TraceProcessor {
|
|
|
665
724
|
'TraceProcessor',
|
|
666
725
|
'frameTreeEvents may be incomplete, make sure the trace has frame events'
|
|
667
726
|
);
|
|
668
|
-
frameIdToRootFrameId.set(
|
|
727
|
+
frameIdToRootFrameId.set(mainFrameInfo.frameId, mainFrameInfo.frameId);
|
|
669
728
|
frameTreeEvents = frameEvents;
|
|
670
729
|
}
|
|
671
730
|
|
|
672
731
|
// Compute our time origin to use for all relative timings.
|
|
673
732
|
const timeOriginEvt = this.computeTimeOrigin(
|
|
674
|
-
{keyEvents, frameEvents,
|
|
733
|
+
{keyEvents, frameEvents, mainFrameInfo: mainFrameInfo},
|
|
675
734
|
timeOriginDeterminationMethod
|
|
676
735
|
);
|
|
677
736
|
|
|
678
|
-
|
|
679
|
-
// stable-sort events to keep them correctly nested.
|
|
680
|
-
const processEvents = TraceProcessor
|
|
681
|
-
.filteredTraceSort(trace.traceEvents, e => e.pid === mainFrameIds.pid);
|
|
682
|
-
|
|
683
|
-
const mainThreadEvents = processEvents
|
|
684
|
-
.filter(e => e.tid === mainFrameIds.tid);
|
|
737
|
+
const mainThreadEvents = processEvents.filter(e => e.tid === rendererPidToTid.get(e.pid));
|
|
685
738
|
|
|
686
739
|
// Ensure our traceEnd reflects all page activity.
|
|
687
740
|
const traceEnd = this.computeTraceEnd(trace.traceEvents, timeOriginEvt);
|
|
@@ -694,7 +747,7 @@ class TraceProcessor {
|
|
|
694
747
|
frameEvents,
|
|
695
748
|
frameTreeEvents,
|
|
696
749
|
processEvents,
|
|
697
|
-
|
|
750
|
+
mainFrameInfo,
|
|
698
751
|
timeOriginEvt,
|
|
699
752
|
timings: {
|
|
700
753
|
timeOrigin: 0,
|
|
@@ -704,6 +757,8 @@ class TraceProcessor {
|
|
|
704
757
|
timeOrigin: timeOriginEvt.ts,
|
|
705
758
|
traceEnd: traceEnd.timestamp,
|
|
706
759
|
},
|
|
760
|
+
_keyEvents: keyEvents,
|
|
761
|
+
_rendererPidToTid: rendererPidToTid,
|
|
707
762
|
};
|
|
708
763
|
}
|
|
709
764
|
|
|
@@ -807,7 +862,7 @@ class TraceProcessor {
|
|
|
807
862
|
* Can also be skewed by several hundred milliseconds or even seconds when the browser takes a long
|
|
808
863
|
* time to unload `about:blank`.
|
|
809
864
|
*
|
|
810
|
-
* @param {{keyEvents: Array<LH.TraceEvent>, frameEvents: Array<LH.TraceEvent>,
|
|
865
|
+
* @param {{keyEvents: Array<LH.TraceEvent>, frameEvents: Array<LH.TraceEvent>, mainFrameInfo: {frameId: string}}} traceEventSubsets
|
|
811
866
|
* @param {TimeOriginDeterminationMethod} method
|
|
812
867
|
* @return {LH.TraceEvent}
|
|
813
868
|
*/
|
|
@@ -833,7 +888,7 @@ class TraceProcessor {
|
|
|
833
888
|
const fetchStart = traceEventSubsets.keyEvents.find(event => {
|
|
834
889
|
if (event.name !== 'ResourceSendRequest') return false;
|
|
835
890
|
const data = event.args.data || {};
|
|
836
|
-
return data.frame === traceEventSubsets.
|
|
891
|
+
return data.frame === traceEventSubsets.mainFrameInfo.frameId;
|
|
837
892
|
});
|
|
838
893
|
if (!fetchStart) throw this.createNoResourceSendRequestError();
|
|
839
894
|
return fetchStart;
|
|
@@ -128,7 +128,7 @@ class MetricTraceEvents {
|
|
|
128
128
|
if (!timeOriginMetric) return {errorMessage: 'timeorigin Metric not found in definitions'};
|
|
129
129
|
try {
|
|
130
130
|
const frameIds = TraceProcessor.findMainFrameIds(this._traceEvents);
|
|
131
|
-
return {pid: frameIds.
|
|
131
|
+
return {pid: frameIds.startingPid, tid: 1, ts: timeOriginMetric.ts};
|
|
132
132
|
} catch (err) {
|
|
133
133
|
return {errorMessage: err.message};
|
|
134
134
|
}
|