lighthouse 11.3.0-dev.20231130 → 11.3.0-dev.20231201
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/byte-efficiency/byte-efficiency-audit.d.ts +14 -0
- package/core/audits/byte-efficiency/byte-efficiency-audit.js +60 -0
- package/core/audits/byte-efficiency/legacy-javascript.d.ts +6 -4
- package/core/audits/byte-efficiency/legacy-javascript.js +21 -18
- package/core/computed/entity-classification.js +1 -1
- package/core/computed/resource-summary.d.ts +0 -1
- package/core/computed/resource-summary.js +2 -2
- package/core/gather/navigation-runner.js +1 -1
- package/core/lib/network-recorder.d.ts +8 -0
- package/core/lib/network-recorder.js +13 -0
- package/core/lib/network-request.d.ts +11 -0
- package/core/lib/network-request.js +18 -1
- package/core/lib/url-utils.d.ts +6 -0
- package/core/lib/url-utils.js +14 -2
- package/core/scripts/download-chrome.sh +54 -16
- package/dist/report/bundle.esm.js +4 -4
- package/dist/report/flow.js +2 -2
- package/dist/report/standalone.js +5 -5
- package/package.json +2 -1
- package/report/renderer/report-ui-features.js +3 -2
- package/shared/util.d.ts +14 -2
- package/shared/util.js +25 -3
|
@@ -43,6 +43,19 @@ export class ByteEfficiencyAudit extends Audit {
|
|
|
43
43
|
* @return {number}
|
|
44
44
|
*/
|
|
45
45
|
static estimateTransferSize(networkRecord: LH.Artifacts.NetworkRequest | undefined, totalBytes: number, resourceType?: LH.Crdp.Network.ResourceType | undefined): number;
|
|
46
|
+
/**
|
|
47
|
+
* Estimates the number of bytes the content of this network record would have consumed on the network based on the
|
|
48
|
+
* uncompressed size (totalBytes). Uses the actual transfer size from the network record if applicable,
|
|
49
|
+
* minus the size of the response headers.
|
|
50
|
+
*
|
|
51
|
+
* This differs from `estimateTransferSize` only in that is subtracts the response headers from the estimate.
|
|
52
|
+
*
|
|
53
|
+
* @param {LH.Artifacts.NetworkRequest|undefined} networkRecord
|
|
54
|
+
* @param {number} totalBytes Uncompressed size of the resource
|
|
55
|
+
* @param {LH.Crdp.Network.ResourceType=} resourceType
|
|
56
|
+
* @return {number}
|
|
57
|
+
*/
|
|
58
|
+
static estimateCompressedContentSize(networkRecord: LH.Artifacts.NetworkRequest | undefined, totalBytes: number, resourceType?: LH.Crdp.Network.ResourceType | undefined): number;
|
|
46
59
|
/**
|
|
47
60
|
* @param {LH.Artifacts} artifacts
|
|
48
61
|
* @param {LH.Audit.Context} context
|
|
@@ -99,4 +112,5 @@ export class ByteEfficiencyAudit extends Audit {
|
|
|
99
112
|
static audit_(artifacts: LH.Artifacts, networkRecords: Array<LH.Artifacts.NetworkRequest>, context: LH.Audit.Context): ByteEfficiencyProduct | Promise<ByteEfficiencyProduct>;
|
|
100
113
|
}
|
|
101
114
|
import { Audit } from '../audit.js';
|
|
115
|
+
import { NetworkRequest } from '../../lib/network-request.js';
|
|
102
116
|
//# sourceMappingURL=byte-efficiency-audit.d.ts.map
|
|
@@ -13,6 +13,7 @@ import {PageDependencyGraph} from '../../computed/page-dependency-graph.js';
|
|
|
13
13
|
import {LanternLargestContentfulPaint} from '../../computed/metrics/lantern-largest-contentful-paint.js';
|
|
14
14
|
import {LanternFirstContentfulPaint} from '../../computed/metrics/lantern-first-contentful-paint.js';
|
|
15
15
|
import {LCPImageRecord} from '../../computed/lcp-image-record.js';
|
|
16
|
+
import {NetworkRequest} from '../../lib/network-request.js';
|
|
16
17
|
|
|
17
18
|
const str_ = i18n.createIcuMessageFn(import.meta.url, {});
|
|
18
19
|
|
|
@@ -100,6 +101,65 @@ class ByteEfficiencyAudit extends Audit {
|
|
|
100
101
|
}
|
|
101
102
|
}
|
|
102
103
|
|
|
104
|
+
/**
|
|
105
|
+
* Estimates the number of bytes the content of this network record would have consumed on the network based on the
|
|
106
|
+
* uncompressed size (totalBytes). Uses the actual transfer size from the network record if applicable,
|
|
107
|
+
* minus the size of the response headers.
|
|
108
|
+
*
|
|
109
|
+
* This differs from `estimateTransferSize` only in that is subtracts the response headers from the estimate.
|
|
110
|
+
*
|
|
111
|
+
* @param {LH.Artifacts.NetworkRequest|undefined} networkRecord
|
|
112
|
+
* @param {number} totalBytes Uncompressed size of the resource
|
|
113
|
+
* @param {LH.Crdp.Network.ResourceType=} resourceType
|
|
114
|
+
* @return {number}
|
|
115
|
+
*/
|
|
116
|
+
static estimateCompressedContentSize(networkRecord, totalBytes, resourceType) {
|
|
117
|
+
if (!networkRecord) {
|
|
118
|
+
// We don't know how many bytes this asset used on the network, but we can guess it was
|
|
119
|
+
// roughly the size of the content gzipped.
|
|
120
|
+
// See https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/optimize-encoding-and-transfer for specific CSS/Script examples
|
|
121
|
+
// See https://discuss.httparchive.org/t/file-size-and-compression-savings/145 for fallback multipliers
|
|
122
|
+
switch (resourceType) {
|
|
123
|
+
case 'Stylesheet':
|
|
124
|
+
// Stylesheets tend to compress extremely well.
|
|
125
|
+
return Math.round(totalBytes * 0.2);
|
|
126
|
+
case 'Script':
|
|
127
|
+
case 'Document':
|
|
128
|
+
// Scripts and HTML compress fairly well too.
|
|
129
|
+
return Math.round(totalBytes * 0.33);
|
|
130
|
+
default:
|
|
131
|
+
// Otherwise we'll just fallback to the average savings in HTTPArchive
|
|
132
|
+
return Math.round(totalBytes * 0.5);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Get the size of the response body on the network.
|
|
137
|
+
let contentTransferSize = networkRecord.transferSize || 0;
|
|
138
|
+
if (!NetworkRequest.isContentEncoded(networkRecord)) {
|
|
139
|
+
// This is not encoded, so we can use resourceSize directly.
|
|
140
|
+
// This would be equivalent to transfer size minus headers transfer size, but transfer size
|
|
141
|
+
// may also include bytes for SSL connection etc.
|
|
142
|
+
contentTransferSize = networkRecord.resourceSize;
|
|
143
|
+
} else if (networkRecord.responseHeadersTransferSize) {
|
|
144
|
+
// Subtract the size of the encoded headers.
|
|
145
|
+
contentTransferSize =
|
|
146
|
+
Math.max(0, contentTransferSize - networkRecord.responseHeadersTransferSize);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (networkRecord.resourceType === resourceType) {
|
|
150
|
+
// This was a regular standalone asset, just use the transfer size.
|
|
151
|
+
return contentTransferSize;
|
|
152
|
+
} else {
|
|
153
|
+
// This was an asset that was inlined in a different resource type (e.g. HTML document).
|
|
154
|
+
// Use the compression ratio of the resource to estimate the total transferred bytes.
|
|
155
|
+
const resourceSize = networkRecord.resourceSize || 0;
|
|
156
|
+
// Get the compression ratio, if it's an invalid number, assume no compression.
|
|
157
|
+
const compressionRatio = Number.isFinite(resourceSize) && resourceSize > 0 ?
|
|
158
|
+
(contentTransferSize / resourceSize) : 1;
|
|
159
|
+
return Math.round(totalBytes * compressionRatio);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
103
163
|
/**
|
|
104
164
|
* @param {LH.Artifacts} artifacts
|
|
105
165
|
* @param {LH.Audit.Context} context
|
|
@@ -61,16 +61,18 @@ declare class LegacyJavascript extends ByteEfficiencyAudit {
|
|
|
61
61
|
*/
|
|
62
62
|
static estimateWastedBytes(matches: PatternMatchResult[]): number;
|
|
63
63
|
/**
|
|
64
|
-
* Utility function to estimate
|
|
64
|
+
* Utility function to estimate the ratio of the compression on the resource.
|
|
65
|
+
* This excludes the size of the response headers.
|
|
66
|
+
* Also caches the calculation.
|
|
65
67
|
*
|
|
66
68
|
* Note: duplicated-javascript does this exact thing. In the future, consider
|
|
67
|
-
* making a generic estimator on
|
|
68
|
-
* @param {Map<string, number>}
|
|
69
|
+
* making a generic estimator on ByteEfficiencyAudit.
|
|
70
|
+
* @param {Map<string, number>} compressionRatioByUrl
|
|
69
71
|
* @param {string} url
|
|
70
72
|
* @param {LH.Artifacts} artifacts
|
|
71
73
|
* @param {Array<LH.Artifacts.NetworkRequest>} networkRecords
|
|
72
74
|
*/
|
|
73
|
-
static
|
|
75
|
+
static estimateCompressionRatioForContent(compressionRatioByUrl: Map<string, number>, url: string, artifacts: LH.Artifacts, networkRecords: Array<LH.Artifacts.NetworkRequest>): Promise<number>;
|
|
74
76
|
/**
|
|
75
77
|
* @param {LH.Artifacts} artifacts
|
|
76
78
|
* @param {Array<LH.Artifacts.NetworkRequest>} networkRecords
|
|
@@ -391,34 +391,37 @@ class LegacyJavascript extends ByteEfficiencyAudit {
|
|
|
391
391
|
}
|
|
392
392
|
|
|
393
393
|
/**
|
|
394
|
-
* Utility function to estimate
|
|
394
|
+
* Utility function to estimate the ratio of the compression on the resource.
|
|
395
|
+
* This excludes the size of the response headers.
|
|
396
|
+
* Also caches the calculation.
|
|
395
397
|
*
|
|
396
398
|
* Note: duplicated-javascript does this exact thing. In the future, consider
|
|
397
|
-
* making a generic estimator on
|
|
398
|
-
* @param {Map<string, number>}
|
|
399
|
+
* making a generic estimator on ByteEfficiencyAudit.
|
|
400
|
+
* @param {Map<string, number>} compressionRatioByUrl
|
|
399
401
|
* @param {string} url
|
|
400
402
|
* @param {LH.Artifacts} artifacts
|
|
401
403
|
* @param {Array<LH.Artifacts.NetworkRequest>} networkRecords
|
|
402
404
|
*/
|
|
403
|
-
static async
|
|
404
|
-
|
|
405
|
-
|
|
405
|
+
static async estimateCompressionRatioForContent(compressionRatioByUrl, url,
|
|
406
|
+
artifacts, networkRecords) {
|
|
407
|
+
let compressionRatio = compressionRatioByUrl.get(url);
|
|
408
|
+
if (compressionRatio !== undefined) return compressionRatio;
|
|
406
409
|
|
|
407
410
|
const script = artifacts.Scripts.find(script => script.url === url);
|
|
408
411
|
|
|
409
|
-
if (!script
|
|
412
|
+
if (!script) {
|
|
410
413
|
// Can't find content, so just use 1.
|
|
411
|
-
|
|
414
|
+
compressionRatio = 1;
|
|
412
415
|
} else {
|
|
413
416
|
const networkRecord = getRequestForScript(networkRecords, script);
|
|
414
|
-
const contentLength = script.length || 0;
|
|
415
|
-
const
|
|
416
|
-
ByteEfficiencyAudit.
|
|
417
|
-
|
|
417
|
+
const contentLength = networkRecord?.resourceSize || script.length || 0;
|
|
418
|
+
const compressedSize =
|
|
419
|
+
ByteEfficiencyAudit.estimateCompressedContentSize(networkRecord, contentLength, 'Script');
|
|
420
|
+
compressionRatio = compressedSize / contentLength;
|
|
418
421
|
}
|
|
419
422
|
|
|
420
|
-
|
|
421
|
-
return
|
|
423
|
+
compressionRatioByUrl.set(url, compressionRatio);
|
|
424
|
+
return compressionRatio;
|
|
422
425
|
}
|
|
423
426
|
|
|
424
427
|
/**
|
|
@@ -443,14 +446,14 @@ class LegacyJavascript extends ByteEfficiencyAudit {
|
|
|
443
446
|
]);
|
|
444
447
|
|
|
445
448
|
/** @type {Map<string, number>} */
|
|
446
|
-
const
|
|
449
|
+
const compressionRatioByUrl = new Map();
|
|
447
450
|
|
|
448
451
|
const scriptToMatchResults =
|
|
449
452
|
this.detectAcrossScripts(matcher, artifacts.Scripts, networkRecords, bundles);
|
|
450
453
|
for (const [script, matches] of scriptToMatchResults.entries()) {
|
|
451
|
-
const
|
|
452
|
-
|
|
453
|
-
const wastedBytes = Math.round(this.estimateWastedBytes(matches) *
|
|
454
|
+
const compressionRatio = await this.estimateCompressionRatioForContent(
|
|
455
|
+
compressionRatioByUrl, script.url, artifacts, networkRecords);
|
|
456
|
+
const wastedBytes = Math.round(this.estimateWastedBytes(matches) * compressionRatio);
|
|
454
457
|
/** @type {typeof items[number]} */
|
|
455
458
|
const item = {
|
|
456
459
|
url: script.url,
|
|
@@ -59,7 +59,7 @@ class EntityClassification {
|
|
|
59
59
|
// Make up an entity only for valid http/https URLs.
|
|
60
60
|
if (!parsedUrl.protocol.startsWith('http')) return;
|
|
61
61
|
|
|
62
|
-
const rootDomain =
|
|
62
|
+
const rootDomain = UrlUtils.getRootDomain(url);
|
|
63
63
|
if (!rootDomain) return;
|
|
64
64
|
if (entityCache.has(rootDomain)) return entityCache.get(rootDomain);
|
|
65
65
|
|
|
@@ -39,7 +39,6 @@ declare class ResourceSummary {
|
|
|
39
39
|
budgets: LH.Util.ImmutableObject<LH.Budget[] | null>;
|
|
40
40
|
}, context: LH.Artifacts.ComputedContext): Promise<Record<LH.Budget.ResourceType, ResourceEntry>>;
|
|
41
41
|
}
|
|
42
|
-
import { Util } from '../../shared/util.js';
|
|
43
42
|
import { Budget } from '../config/budget.js';
|
|
44
43
|
import { NetworkRequest } from '../lib/network-request.js';
|
|
45
44
|
import { EntityClassification } from './entity-classification.js';
|
|
@@ -9,7 +9,7 @@ import {makeComputedArtifact} from './computed-artifact.js';
|
|
|
9
9
|
import {NetworkRecords} from './network-records.js';
|
|
10
10
|
import {NetworkRequest} from '../lib/network-request.js';
|
|
11
11
|
import {Budget} from '../config/budget.js';
|
|
12
|
-
import
|
|
12
|
+
import UrlUtils from '../lib/url-utils.js';
|
|
13
13
|
|
|
14
14
|
/** @typedef {{count: number, resourceSize: number, transferSize: number}} ResourceEntry */
|
|
15
15
|
|
|
@@ -59,7 +59,7 @@ class ResourceSummary {
|
|
|
59
59
|
firstPartyHosts = budget.options.firstPartyHostnames;
|
|
60
60
|
} else {
|
|
61
61
|
firstPartyHosts = classifiedEntities.firstParty?.domains.map(domain => `*.${domain}`) ||
|
|
62
|
-
[`*.${
|
|
62
|
+
[`*.${UrlUtils.getRootDomain(URLArtifact.finalDisplayedUrl)}`];
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
networkRecords.filter(record => {
|
|
@@ -220,7 +220,7 @@ async function _navigation(navigationContext) {
|
|
|
220
220
|
|
|
221
221
|
// Every required url is initialized to an empty string in `getBaseArtifacts`.
|
|
222
222
|
// If we haven't set all the required urls yet, set them here.
|
|
223
|
-
if (!Object.values(phaseState.baseArtifacts).every(Boolean)) {
|
|
223
|
+
if (!Object.values(phaseState.baseArtifacts.URL).every(Boolean)) {
|
|
224
224
|
phaseState.baseArtifacts.URL = {
|
|
225
225
|
requestedUrl: navigateResult.requestedUrl,
|
|
226
226
|
mainDocumentUrl: navigateResult.mainDocumentUrl,
|
|
@@ -66,6 +66,14 @@ export class NetworkRecorder extends NetworkRecorder_base {
|
|
|
66
66
|
targetType: LH.Protocol.TargetType;
|
|
67
67
|
sessionId?: string;
|
|
68
68
|
}): void;
|
|
69
|
+
/**
|
|
70
|
+
* @param {{params: LH.Crdp.Network.ResponseReceivedExtraInfoEvent, targetType: LH.Protocol.TargetType, sessionId?: string}} event
|
|
71
|
+
*/
|
|
72
|
+
onResponseReceivedExtraInfo(event: {
|
|
73
|
+
params: LH.Crdp.Network.ResponseReceivedExtraInfoEvent;
|
|
74
|
+
targetType: LH.Protocol.TargetType;
|
|
75
|
+
sessionId?: string;
|
|
76
|
+
}): void;
|
|
69
77
|
/**
|
|
70
78
|
* @param {{params: LH.Crdp.Network.DataReceivedEvent, targetType: LH.Protocol.TargetType, sessionId?: string}} event
|
|
71
79
|
*/
|
|
@@ -138,6 +138,18 @@ class NetworkRecorder extends RequestEventEmitter {
|
|
|
138
138
|
request.onResponseReceived(data);
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
+
/**
|
|
142
|
+
* @param {{params: LH.Crdp.Network.ResponseReceivedExtraInfoEvent, targetType: LH.Protocol.TargetType, sessionId?: string}} event
|
|
143
|
+
*/
|
|
144
|
+
onResponseReceivedExtraInfo(event) {
|
|
145
|
+
const data = event.params;
|
|
146
|
+
const request = this._findRealRequestAndSetSession(
|
|
147
|
+
data.requestId, event.targetType, event.sessionId);
|
|
148
|
+
if (!request) return;
|
|
149
|
+
log.verbose('network', `${request.url} response received extra info`);
|
|
150
|
+
request.onResponseReceivedExtraInfo(data);
|
|
151
|
+
}
|
|
152
|
+
|
|
141
153
|
/**
|
|
142
154
|
* @param {{params: LH.Crdp.Network.DataReceivedEvent, targetType: LH.Protocol.TargetType, sessionId?: string}} event
|
|
143
155
|
*/
|
|
@@ -196,6 +208,7 @@ class NetworkRecorder extends RequestEventEmitter {
|
|
|
196
208
|
case 'Network.requestWillBeSent': return this.onRequestWillBeSent(event);
|
|
197
209
|
case 'Network.requestServedFromCache': return this.onRequestServedFromCache(event);
|
|
198
210
|
case 'Network.responseReceived': return this.onResponseReceived(event);
|
|
211
|
+
case 'Network.responseReceivedExtraInfo': return this.onResponseReceivedExtraInfo(event);
|
|
199
212
|
case 'Network.dataReceived': return this.onDataReceived(event);
|
|
200
213
|
case 'Network.loadingFinished': return this.onLoadingFinished(event);
|
|
201
214
|
case 'Network.loadingFailed': return this.onLoadingFailed(event);
|
|
@@ -66,6 +66,12 @@ export class NetworkRequest {
|
|
|
66
66
|
* @return {boolean}
|
|
67
67
|
*/
|
|
68
68
|
static isHstsRequest(record: NetworkRequest): boolean;
|
|
69
|
+
/**
|
|
70
|
+
* Returns whether the network request was sent encoded.
|
|
71
|
+
* @param {NetworkRequest} record
|
|
72
|
+
* @return {boolean}
|
|
73
|
+
*/
|
|
74
|
+
static isContentEncoded(record: NetworkRequest): boolean;
|
|
69
75
|
/**
|
|
70
76
|
* Resource size is almost always the right one to be using because of the below:
|
|
71
77
|
* `transferSize = resourceSize + headers.length`.
|
|
@@ -98,6 +104,7 @@ export class NetworkRequest {
|
|
|
98
104
|
/** When the last byte of the response body is received, in milliseconds. */
|
|
99
105
|
networkEndTime: number;
|
|
100
106
|
transferSize: number;
|
|
107
|
+
responseHeadersTransferSize: number;
|
|
101
108
|
resourceSize: number;
|
|
102
109
|
fromDiskCache: boolean;
|
|
103
110
|
fromMemoryCache: boolean;
|
|
@@ -155,6 +162,10 @@ export class NetworkRequest {
|
|
|
155
162
|
* @param {LH.Crdp.Network.ResponseReceivedEvent} data
|
|
156
163
|
*/
|
|
157
164
|
onResponseReceived(data: LH.Crdp.Network.ResponseReceivedEvent): void;
|
|
165
|
+
/**
|
|
166
|
+
* @param {LH.Crdp.Network.ResponseReceivedExtraInfoEvent} data
|
|
167
|
+
*/
|
|
168
|
+
onResponseReceivedExtraInfo(data: LH.Crdp.Network.ResponseReceivedExtraInfoEvent): void;
|
|
158
169
|
/**
|
|
159
170
|
* @param {LH.Crdp.Network.DataReceivedEvent} data
|
|
160
171
|
*/
|
|
@@ -135,6 +135,7 @@ class NetworkRequest {
|
|
|
135
135
|
|
|
136
136
|
// Go read the comment on _updateTransferSizeForLightrider.
|
|
137
137
|
this.transferSize = 0;
|
|
138
|
+
this.responseHeadersTransferSize = 0;
|
|
138
139
|
this.resourceSize = 0;
|
|
139
140
|
this.fromDiskCache = false;
|
|
140
141
|
this.fromMemoryCache = false;
|
|
@@ -246,6 +247,13 @@ class NetworkRequest {
|
|
|
246
247
|
this.frameId = data.frameId;
|
|
247
248
|
}
|
|
248
249
|
|
|
250
|
+
/**
|
|
251
|
+
* @param {LH.Crdp.Network.ResponseReceivedExtraInfoEvent} data
|
|
252
|
+
*/
|
|
253
|
+
onResponseReceivedExtraInfo(data) {
|
|
254
|
+
this.responseHeadersText = data.headersText || '';
|
|
255
|
+
}
|
|
256
|
+
|
|
249
257
|
/**
|
|
250
258
|
* @param {LH.Crdp.Network.DataReceivedEvent} data
|
|
251
259
|
*/
|
|
@@ -344,6 +352,7 @@ class NetworkRequest {
|
|
|
344
352
|
this.responseHeadersEndTime = timestamp * 1000;
|
|
345
353
|
|
|
346
354
|
this.transferSize = response.encodedDataLength;
|
|
355
|
+
this.responseHeadersTransferSize = response.encodedDataLength;
|
|
347
356
|
if (typeof response.fromDiskCache === 'boolean') this.fromDiskCache = response.fromDiskCache;
|
|
348
357
|
if (typeof response.fromPrefetchCache === 'boolean') {
|
|
349
358
|
this.fromPrefetchCache = response.fromPrefetchCache;
|
|
@@ -354,7 +363,6 @@ class NetworkRequest {
|
|
|
354
363
|
this.timing = response.timing;
|
|
355
364
|
if (resourceType) this.resourceType = RESOURCE_TYPES[resourceType];
|
|
356
365
|
this.mimeType = response.mimeType;
|
|
357
|
-
this.responseHeadersText = response.headersText || '';
|
|
358
366
|
this.responseHeaders = NetworkRequest._headersDictToHeadersArray(response.headers);
|
|
359
367
|
|
|
360
368
|
this.fetchedViaServiceWorker = !!response.fromServiceWorker;
|
|
@@ -600,6 +608,15 @@ class NetworkRequest {
|
|
|
600
608
|
return reason === 'HSTS' && NetworkRequest.isSecureRequest(destination);
|
|
601
609
|
}
|
|
602
610
|
|
|
611
|
+
/**
|
|
612
|
+
* Returns whether the network request was sent encoded.
|
|
613
|
+
* @param {NetworkRequest} record
|
|
614
|
+
* @return {boolean}
|
|
615
|
+
*/
|
|
616
|
+
static isContentEncoded(record) {
|
|
617
|
+
return record.responseHeaders.some(item => item.name === 'Content-Encoding');
|
|
618
|
+
}
|
|
619
|
+
|
|
603
620
|
/**
|
|
604
621
|
* Resource size is almost always the right one to be using because of the below:
|
|
605
622
|
* `transferSize = resourceSize + headers.length`.
|
package/core/lib/url-utils.d.ts
CHANGED
|
@@ -23,6 +23,12 @@ declare class UrlUtils {
|
|
|
23
23
|
* @return {?string}
|
|
24
24
|
*/
|
|
25
25
|
static getOrigin(url: string): string | null;
|
|
26
|
+
/**
|
|
27
|
+
* Returns a primary domain for provided hostname (e.g. www.example.com -> example.com).
|
|
28
|
+
* @param {string|URL} url hostname or URL object
|
|
29
|
+
* @return {string}
|
|
30
|
+
*/
|
|
31
|
+
static getRootDomain(url: string | URL): string;
|
|
26
32
|
/**
|
|
27
33
|
* Check if rootDomains matches
|
|
28
34
|
*
|
package/core/lib/url-utils.js
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import {getDomain} from 'tldts';
|
|
8
|
+
|
|
7
9
|
import {Util} from '../../shared/util.js';
|
|
8
10
|
import {LighthouseError} from './lh-error.js';
|
|
9
11
|
|
|
@@ -99,6 +101,16 @@ class UrlUtils {
|
|
|
99
101
|
}
|
|
100
102
|
}
|
|
101
103
|
|
|
104
|
+
/**
|
|
105
|
+
* Returns a primary domain for provided hostname (e.g. www.example.com -> example.com).
|
|
106
|
+
* @param {string|URL} url hostname or URL object
|
|
107
|
+
* @return {string}
|
|
108
|
+
*/
|
|
109
|
+
static getRootDomain(url) {
|
|
110
|
+
const parsedUrl = Util.createOrReturnURL(url);
|
|
111
|
+
return getDomain(parsedUrl.href) || parsedUrl.hostname;
|
|
112
|
+
}
|
|
113
|
+
|
|
102
114
|
/**
|
|
103
115
|
* Check if rootDomains matches
|
|
104
116
|
*
|
|
@@ -120,8 +132,8 @@ class UrlUtils {
|
|
|
120
132
|
}
|
|
121
133
|
|
|
122
134
|
// get the string before the tld
|
|
123
|
-
const urlARootDomain =
|
|
124
|
-
const urlBRootDomain =
|
|
135
|
+
const urlARootDomain = UrlUtils.getRootDomain(urlAInfo);
|
|
136
|
+
const urlBRootDomain = UrlUtils.getRootDomain(urlBInfo);
|
|
125
137
|
|
|
126
138
|
return urlARootDomain === urlBRootDomain;
|
|
127
139
|
}
|
|
@@ -6,9 +6,22 @@
|
|
|
6
6
|
##
|
|
7
7
|
|
|
8
8
|
# Download chrome inside of our CI env.
|
|
9
|
+
# Takes one arg - the location to extract ToT chrome to. Defaults to .tmp/chrome-tot
|
|
10
|
+
# If already exists, this script does nothing.
|
|
9
11
|
|
|
10
12
|
set -euo pipefail
|
|
11
13
|
|
|
14
|
+
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|
15
|
+
LH_ROOT_DIR="$SCRIPT_DIR/../.."
|
|
16
|
+
|
|
17
|
+
chrome_out=${1:-"$LH_ROOT_DIR/.tmp/chrome-tot"}
|
|
18
|
+
mkdir -p "$LH_ROOT_DIR/.tmp"
|
|
19
|
+
|
|
20
|
+
if [ -e "$chrome_out" ]; then
|
|
21
|
+
echo "cached chrome found"
|
|
22
|
+
exit 0
|
|
23
|
+
fi
|
|
24
|
+
|
|
12
25
|
unameOut="$(uname -s)"
|
|
13
26
|
case "${unameOut}" in
|
|
14
27
|
Linux*) machine=Linux;;
|
|
@@ -17,24 +30,49 @@ case "${unameOut}" in
|
|
|
17
30
|
*) machine="UNKNOWN:${unameOut}"
|
|
18
31
|
esac
|
|
19
32
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
33
|
+
# Only set this to true when actual ToT is broken and we can't fix it yet.
|
|
34
|
+
should_hardcode_ci=true
|
|
35
|
+
|
|
36
|
+
if [[ "${CI:-}" ]] && [ "$should_hardcode_ci" == true ]; then
|
|
37
|
+
rev=1228630
|
|
38
|
+
if [ "$machine" == "MinGw" ]; then
|
|
39
|
+
url="http://commondatastorage.googleapis.com/chromium-browser-snapshots/Win_x64/$rev/chrome-win.zip"
|
|
40
|
+
elif [ "$machine" == "Linux" ]; then
|
|
41
|
+
url="http://commondatastorage.googleapis.com/chromium-browser-snapshots/Linux_x64/$rev/chrome-linux.zip"
|
|
42
|
+
elif [ "$machine" == "Mac" ]; then
|
|
43
|
+
arch="$(uname -m)"
|
|
44
|
+
if [ "$arch" == "arm64" ]; then
|
|
45
|
+
url="http://commondatastorage.googleapis.com/chromium-browser-snapshots/Mac_Arm/$rev/chrome-mac.zip"
|
|
46
|
+
else
|
|
47
|
+
url="http://commondatastorage.googleapis.com/chromium-browser-snapshots/Mac/$rev/chrome-mac.zip"
|
|
48
|
+
fi
|
|
28
49
|
else
|
|
29
|
-
|
|
50
|
+
echo "unsupported platform"
|
|
51
|
+
exit 1
|
|
30
52
|
fi
|
|
31
53
|
else
|
|
32
|
-
|
|
33
|
-
|
|
54
|
+
if [ "$machine" == "MinGw" ]; then
|
|
55
|
+
url="https://download-chromium.appspot.com/dl/Win?type=snapshots"
|
|
56
|
+
elif [ "$machine" == "Linux" ]; then
|
|
57
|
+
url="https://download-chromium.appspot.com/dl/Linux_x64?type=snapshots"
|
|
58
|
+
elif [ "$machine" == "Mac" ]; then
|
|
59
|
+
arch="$(uname -m)"
|
|
60
|
+
if [ "$arch" == "arm64" ]; then
|
|
61
|
+
url="https://download-chromium.appspot.com/dl/Mac_Arm?type=snapshots"
|
|
62
|
+
else
|
|
63
|
+
url="https://download-chromium.appspot.com/dl/Mac?type=snapshots"
|
|
64
|
+
fi
|
|
65
|
+
else
|
|
66
|
+
echo "unsupported platform"
|
|
67
|
+
exit 1
|
|
68
|
+
fi
|
|
34
69
|
fi
|
|
35
70
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
71
|
+
echo "downloading $url"
|
|
72
|
+
|
|
73
|
+
mkdir -p .tmp-download && cd .tmp-download
|
|
74
|
+
curl "$url" -Lo chrome.zip && unzip -q chrome.zip && rm chrome.zip
|
|
75
|
+
mv * "$chrome_out"
|
|
76
|
+
cd - && rm -rf .tmp-download
|
|
77
|
+
|
|
78
|
+
ls "$chrome_out"
|