-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathhowSlowForPage.js
More file actions
149 lines (126 loc) · 5.61 KB
/
howSlowForPage.js
File metadata and controls
149 lines (126 loc) · 5.61 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
class HowSlowForPage {
constructor(swPath) {
this.bandwidth = undefined;
this.rtt = undefined;
this.firstRequestSent = false;
this.navigationStart = 0;
this.initSW(swPath).then(() => this.listenToSW());
this.storageIO();
}
getBandwidth() {
return this.bandwidth;
}
getRTT() {
return this.rtt;
}
// Initializes the Service Worker, or at least tries
initSW(swPath) {
return new Promise((resolve, reject) => {
if (!('serviceWorker' in window.navigator)) {
// No browser support
reject('Service Workers not supported');
}
if (window.navigator.serviceWorker.controller) {
if (window.navigator.serviceWorker.controller.scriptURL.indexOf(swPath) >= 0) {
// The service worker is already active
resolve();
} else {
reject('Giving up. Service Worker conflict with: ' + window.navigator.serviceWorker.controller.scriptURL);
}
} else {
window.navigator.serviceWorker.register(swPath)
.then(window.navigator.serviceWorker.ready)
.then(function(serviceWorkerRegistration) {
// The service worker is registered and should even be ready now
resolve();
});
}
});
}
// Geting ready to receive messages from the Service Worker
listenToSW() {
window.navigator.serviceWorker.onmessage = (event) => {
if (event.data.command === 'timingsPlz') {
// The Service Workers asks for resource timings
const timings = this.readLatestResourceTimings();
if (timings.length > 0) {
this.sendResourceTimings(timings);
}
} else if (event.data.command === 'stats') {
// The Service Workers sends the latest stats
this.bandwidth = event.data.bandwidth;
this.rtt = event.data.rtt;
}
};
}
// Gathers the latest ResourceTimings and sends them to SW
sendResourceTimings(simplifiedTimings) {
try {
navigator.serviceWorker.controller.postMessage({
'command': 'eatThat',
'timings': simplifiedTimings
});
} catch(error) {}
}
// Gathers the ResourceTimings from the API
readLatestResourceTimings() {
// Not compatible browsers
if (!window.performance || !window.performance.getEntriesByType || !window.performance.timing) {
return [];
}
let timings = [];
if (!this.firstRequestSent) {
// Save this for later
this.navigationStart = window.performance.timing.navigationStart;
// The first HTML resource is as intersting as the others... maybe even more!
timings.push({
name: window.location.href,
transferSize: window.performance.timing.transferSize,
domainLookupStart: window.performance.timing.domainLookupStart,
domainLookupEnd: window.performance.timing.domainLookupEnd,
connectStart: window.performance.timing.connectStart,
connectEnd: window.performance.timing.connectEnd,
requestStart: window.performance.timing.requestStart,
responseStart: window.performance.timing.responseStart,
responseEnd: window.performance.timing.responseEnd
});
this.firstRequestSent = true;
}
window.performance.getEntriesByType('resource').forEach((timing) => {
timings.push({
name: timing.name,
transferSize: timing.transferSize,
domainLookupStart: Math.round(this.navigationStart + timing.domainLookupStart),
domainLookupEnd: Math.round(this.navigationStart + timing.domainLookupEnd),
connectStart: Math.round(this.navigationStart + timing.connectStart),
connectEnd: Math.round(this.navigationStart + timing.connectEnd),
secureConnectionStart: Math.round(this.navigationStart + timing.secureConnectionStart),
requestStart: Math.round(this.navigationStart + timing.requestStart),
responseStart: Math.round(this.navigationStart + timing.responseStart),
responseEnd: Math.round(this.navigationStart + timing.responseEnd)
});
});
// Now lets clear resourceTimings
window.performance.clearResourceTimings();
window.performance.setResourceTimingBufferSize(200);
// TODO: add an option to avoid clearing ResourceTimings...
// ... some other scripts might need them!
return timings;
}
// On the SW's side, stats are saved in IndexedDB. Here we have access to LocalStorage.
storageIO() {
// When leaving the page, save stats into LocalStorage for faster ignition
window.addEventListener('unload', () => {
if (this.bandwidth || this.rtt) {
window.localStorage.setItem('howslow', this.bandwidth + ',' + this.rtt);
}
});
// And when arriving on the page, retrieve stats
let stats = window.localStorage.getItem('howslow');
if (stats) {
stats = stats.split(',');
this.bandwidth = stats[0] || undefined;
this.rtt = stats[1] || undefined;
}
}
}