main
js 112 lines 3.57 KB
Raw
1 /**
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 * @flow
8 */
9
10 function isLikelyStaticResource(initiatorType: string) {
11 switch (initiatorType) {
12 case 'css':
13 case 'script':
14 case 'font':
15 case 'img':
16 case 'image':
17 case 'input':
18 case 'link':
19 return true;
20 default:
21 return false;
22 }
23 }
24
25 export default function estimateBandwidth(): number {
26 // Estimate the current bandwidth for downloading static resources given resources already
27 // loaded.
28 // $FlowFixMe[method-unbinding]
29 if (typeof performance.getEntriesByType === 'function') {
30 let count = 0;
31 let bits = 0;
32 const resourceEntries = performance.getEntriesByType('resource');
33 for (let i = 0; i < resourceEntries.length; i++) {
34 const entry = resourceEntries[i];
35 // $FlowFixMe[prop-missing]
36 const transferSize: number = entry.transferSize;
37 // $FlowFixMe[prop-missing]
38 const initiatorType: string = entry.initiatorType;
39 const duration = entry.duration;
40 if (
41 !transferSize ||
42 !duration ||
43 !isLikelyStaticResource(initiatorType)
44 ) {
45 // Skip cached, cross-orgin entries and resources likely to be dynamically generated.
46 continue;
47 }
48 // Find any overlapping entries that were transferring at the same time since the total
49 // bps at the time will include those bytes.
50 let overlappingBytes = 0;
51 // $FlowFixMe[prop-missing]
52 const parentEndTime: number = entry.responseEnd;
53 let j;
54 for (j = i + 1; j < resourceEntries.length; j++) {
55 const overlapEntry = resourceEntries[j];
56 const overlapStartTime = overlapEntry.startTime;
57 if (overlapStartTime > parentEndTime) {
58 break;
59 }
60 // $FlowFixMe[prop-missing]
61 const overlapTransferSize: number = overlapEntry.transferSize;
62 // $FlowFixMe[prop-missing]
63 const overlapInitiatorType: string = overlapEntry.initiatorType;
64 if (
65 !overlapTransferSize ||
66 !isLikelyStaticResource(overlapInitiatorType)
67 ) {
68 // Skip cached, cross-orgin entries and resources likely to be dynamically generated.
69 continue;
70 }
71 // $FlowFixMe[prop-missing]
72 const overlapEndTime: number = overlapEntry.responseEnd;
73 const overlapFactor =
74 overlapEndTime < parentEndTime
75 ? 1
76 : (parentEndTime - overlapStartTime) /
77 (overlapEndTime - overlapStartTime);
78 overlappingBytes += overlapTransferSize * overlapFactor;
79 }
80 // Skip past any entries we already considered overlapping. Otherwise we'd have to go
81 // back to consider previous entries when we then handled them.
82 i = j - 1;
83
84 const bps =
85 ((transferSize + overlappingBytes) * 8) / (entry.duration / 1000);
86 bits += bps;
87 count++;
88 if (count > 10) {
89 // We have enough to get an average.
90 break;
91 }
92 }
93 if (count > 0) {
94 return bits / count / 1e6;
95 }
96 }
97
98 // Fallback to the navigator.connection estimate if available
99 // $FlowFixMe[prop-missing]
100 if (navigator.connection) {
101 // $FlowFixMe[incompatible-use]
102 const downlink: ?number = navigator.connection.downlink;
103 if (typeof downlink === 'number') {
104 return downlink;
105 }
106 }
107
108 // Otherwise, use a default of 5mbps to compute heuristics.
109 // This can happen commonly in Safari if all static resources and images are loaded
110 // cross-orgin.
111 return 5;
112 }