1 | /* |
2 | * Copyright (C) 2010 Google Inc. All rights reserved. |
3 | * |
4 | * Redistribution and use in source and binary forms, with or without |
5 | * modification, are permitted provided that the following conditions are |
6 | * met: |
7 | * |
8 | * * Redistributions of source code must retain the above copyright |
9 | * notice, this list of conditions and the following disclaimer. |
10 | * * Redistributions in binary form must reproduce the above |
11 | * copyright notice, this list of conditions and the following disclaimer |
12 | * in the documentation and/or other materials provided with the |
13 | * distribution. |
14 | * * Neither the name of Google Inc. nor the names of its |
15 | * contributors may be used to endorse or promote products derived from |
16 | * this software without specific prior written permission. |
17 | * |
18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
19 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
20 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
21 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
22 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
23 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
24 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
25 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
26 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
27 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
28 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
29 | */ |
30 | |
31 | #include "config.h" |
32 | #include "PerformanceTiming.h" |
33 | |
34 | #include "Document.h" |
35 | #include "DocumentLoader.h" |
36 | #include "DocumentTiming.h" |
37 | #include "Frame.h" |
38 | #include "FrameLoader.h" |
39 | #include "LoadTiming.h" |
40 | #include "NetworkLoadMetrics.h" |
41 | #include "Performance.h" |
42 | #include "ResourceResponse.h" |
43 | |
44 | namespace WebCore { |
45 | |
46 | PerformanceTiming::PerformanceTiming(DOMWindow* window) |
47 | : DOMWindowProperty(window) |
48 | { |
49 | } |
50 | |
51 | unsigned long long PerformanceTiming::navigationStart() const |
52 | { |
53 | LoadTiming* timing = loadTiming(); |
54 | if (!timing) |
55 | return 0; |
56 | |
57 | return monotonicTimeToIntegerMilliseconds(timing->startTime()); |
58 | } |
59 | |
60 | unsigned long long PerformanceTiming::unloadEventStart() const |
61 | { |
62 | LoadTiming* timing = loadTiming(); |
63 | if (!timing) |
64 | return 0; |
65 | |
66 | if (timing->hasCrossOriginRedirect() || !timing->hasSameOriginAsPreviousDocument()) |
67 | return 0; |
68 | |
69 | return monotonicTimeToIntegerMilliseconds(timing->unloadEventStart()); |
70 | } |
71 | |
72 | unsigned long long PerformanceTiming::unloadEventEnd() const |
73 | { |
74 | LoadTiming* timing = loadTiming(); |
75 | if (!timing) |
76 | return 0; |
77 | |
78 | if (timing->hasCrossOriginRedirect() || !timing->hasSameOriginAsPreviousDocument()) |
79 | return 0; |
80 | |
81 | return monotonicTimeToIntegerMilliseconds(timing->unloadEventEnd()); |
82 | } |
83 | |
84 | unsigned long long PerformanceTiming::redirectStart() const |
85 | { |
86 | LoadTiming* timing = loadTiming(); |
87 | if (!timing) |
88 | return 0; |
89 | |
90 | if (timing->hasCrossOriginRedirect()) |
91 | return 0; |
92 | |
93 | return monotonicTimeToIntegerMilliseconds(timing->redirectStart()); |
94 | } |
95 | |
96 | unsigned long long PerformanceTiming::redirectEnd() const |
97 | { |
98 | LoadTiming* timing = loadTiming(); |
99 | if (!timing) |
100 | return 0; |
101 | |
102 | if (timing->hasCrossOriginRedirect()) |
103 | return 0; |
104 | |
105 | return monotonicTimeToIntegerMilliseconds(timing->redirectEnd()); |
106 | } |
107 | |
108 | unsigned long long PerformanceTiming::fetchStart() const |
109 | { |
110 | LoadTiming* timing = loadTiming(); |
111 | if (!timing) |
112 | return 0; |
113 | |
114 | return monotonicTimeToIntegerMilliseconds(timing->fetchStart()); |
115 | } |
116 | |
117 | unsigned long long PerformanceTiming::domainLookupStart() const |
118 | { |
119 | DocumentLoader* loader = documentLoader(); |
120 | if (!loader) |
121 | return fetchStart(); |
122 | |
123 | const NetworkLoadMetrics& timing = loader->response().deprecatedNetworkLoadMetrics(); |
124 | |
125 | // This will be -1 when a DNS request is not performed. |
126 | // Rather than exposing a special value that indicates no DNS, we "backfill" with fetchStart. |
127 | if (timing.domainLookupStart < 0_ms) |
128 | return fetchStart(); |
129 | |
130 | return resourceLoadTimeRelativeToFetchStart(timing.domainLookupStart); |
131 | } |
132 | |
133 | unsigned long long PerformanceTiming::domainLookupEnd() const |
134 | { |
135 | DocumentLoader* loader = documentLoader(); |
136 | if (!loader) |
137 | return domainLookupStart(); |
138 | |
139 | const NetworkLoadMetrics& timing = loader->response().deprecatedNetworkLoadMetrics(); |
140 | |
141 | // This will be -1 when a DNS request is not performed. |
142 | // Rather than exposing a special value that indicates no DNS, we "backfill" with domainLookupStart. |
143 | if (timing.domainLookupEnd < 0_ms) |
144 | return domainLookupStart(); |
145 | |
146 | return resourceLoadTimeRelativeToFetchStart(timing.domainLookupEnd); |
147 | } |
148 | |
149 | unsigned long long PerformanceTiming::connectStart() const |
150 | { |
151 | DocumentLoader* loader = documentLoader(); |
152 | if (!loader) |
153 | return domainLookupEnd(); |
154 | |
155 | const NetworkLoadMetrics& timing = loader->response().deprecatedNetworkLoadMetrics(); |
156 | |
157 | // connectStart will be -1 when a network request is not made. |
158 | // Rather than exposing a special value that indicates no new connection, we "backfill" with domainLookupEnd. |
159 | Seconds connectStart = timing.connectStart; |
160 | if (connectStart < 0_ms) |
161 | return domainLookupEnd(); |
162 | |
163 | // NetworkLoadMetrics's connect phase includes DNS, however Navigation Timing's |
164 | // connect phase should not. So if there is DNS time, trim it from the start. |
165 | if (timing.domainLookupEnd >= 0_ms && timing.domainLookupEnd > connectStart) |
166 | connectStart = timing.domainLookupEnd; |
167 | |
168 | return resourceLoadTimeRelativeToFetchStart(connectStart); |
169 | } |
170 | |
171 | unsigned long long PerformanceTiming::connectEnd() const |
172 | { |
173 | DocumentLoader* loader = documentLoader(); |
174 | if (!loader) |
175 | return connectStart(); |
176 | |
177 | const NetworkLoadMetrics& timing = loader->response().deprecatedNetworkLoadMetrics(); |
178 | |
179 | // connectEnd will be -1 when a network request is not made. |
180 | // Rather than exposing a special value that indicates no new connection, we "backfill" with connectStart. |
181 | if (timing.connectEnd < 0_ms) |
182 | return connectStart(); |
183 | |
184 | return resourceLoadTimeRelativeToFetchStart(timing.connectEnd); |
185 | } |
186 | |
187 | unsigned long long PerformanceTiming::secureConnectionStart() const |
188 | { |
189 | DocumentLoader* loader = documentLoader(); |
190 | if (!loader) |
191 | return 0; |
192 | |
193 | const NetworkLoadMetrics& timing = loader->response().deprecatedNetworkLoadMetrics(); |
194 | |
195 | if (timing.secureConnectionStart < 0_ms) |
196 | return 0; |
197 | |
198 | return resourceLoadTimeRelativeToFetchStart(timing.secureConnectionStart); |
199 | } |
200 | |
201 | unsigned long long PerformanceTiming::requestStart() const |
202 | { |
203 | DocumentLoader* loader = documentLoader(); |
204 | if (!loader) |
205 | return connectEnd(); |
206 | |
207 | const NetworkLoadMetrics& timing = loader->response().deprecatedNetworkLoadMetrics(); |
208 | |
209 | ASSERT(timing.requestStart >= 0_ms); |
210 | return resourceLoadTimeRelativeToFetchStart(timing.requestStart); |
211 | } |
212 | |
213 | unsigned long long PerformanceTiming::responseStart() const |
214 | { |
215 | DocumentLoader* loader = documentLoader(); |
216 | if (!loader) |
217 | return requestStart(); |
218 | |
219 | const NetworkLoadMetrics& timing = loader->response().deprecatedNetworkLoadMetrics(); |
220 | |
221 | ASSERT(timing.responseStart >= 0_ms); |
222 | return resourceLoadTimeRelativeToFetchStart(timing.responseStart); |
223 | } |
224 | |
225 | unsigned long long PerformanceTiming::responseEnd() const |
226 | { |
227 | LoadTiming* timing = loadTiming(); |
228 | if (!timing) |
229 | return 0; |
230 | |
231 | return monotonicTimeToIntegerMilliseconds(timing->responseEnd()); |
232 | } |
233 | |
234 | unsigned long long PerformanceTiming::domLoading() const |
235 | { |
236 | const DocumentTiming* timing = documentTiming(); |
237 | if (!timing) |
238 | return fetchStart(); |
239 | |
240 | return monotonicTimeToIntegerMilliseconds(timing->domLoading); |
241 | } |
242 | |
243 | unsigned long long PerformanceTiming::domInteractive() const |
244 | { |
245 | const DocumentTiming* timing = documentTiming(); |
246 | if (!timing) |
247 | return 0; |
248 | |
249 | return monotonicTimeToIntegerMilliseconds(timing->domInteractive); |
250 | } |
251 | |
252 | unsigned long long PerformanceTiming::domContentLoadedEventStart() const |
253 | { |
254 | const DocumentTiming* timing = documentTiming(); |
255 | if (!timing) |
256 | return 0; |
257 | |
258 | return monotonicTimeToIntegerMilliseconds(timing->domContentLoadedEventStart); |
259 | } |
260 | |
261 | unsigned long long PerformanceTiming::domContentLoadedEventEnd() const |
262 | { |
263 | const DocumentTiming* timing = documentTiming(); |
264 | if (!timing) |
265 | return 0; |
266 | |
267 | return monotonicTimeToIntegerMilliseconds(timing->domContentLoadedEventEnd); |
268 | } |
269 | |
270 | unsigned long long PerformanceTiming::domComplete() const |
271 | { |
272 | const DocumentTiming* timing = documentTiming(); |
273 | if (!timing) |
274 | return 0; |
275 | |
276 | return monotonicTimeToIntegerMilliseconds(timing->domComplete); |
277 | } |
278 | |
279 | unsigned long long PerformanceTiming::loadEventStart() const |
280 | { |
281 | LoadTiming* timing = loadTiming(); |
282 | if (!timing) |
283 | return 0; |
284 | |
285 | return monotonicTimeToIntegerMilliseconds(timing->loadEventStart()); |
286 | } |
287 | |
288 | unsigned long long PerformanceTiming::loadEventEnd() const |
289 | { |
290 | LoadTiming* timing = loadTiming(); |
291 | if (!timing) |
292 | return 0; |
293 | |
294 | return monotonicTimeToIntegerMilliseconds(timing->loadEventEnd()); |
295 | } |
296 | |
297 | DocumentLoader* PerformanceTiming::documentLoader() const |
298 | { |
299 | auto* frame = this->frame(); |
300 | if (!frame) |
301 | return nullptr; |
302 | |
303 | return frame->loader().documentLoader(); |
304 | } |
305 | |
306 | const DocumentTiming* PerformanceTiming::documentTiming() const |
307 | { |
308 | auto* frame = this->frame(); |
309 | if (!frame) |
310 | return nullptr; |
311 | |
312 | Document* document = frame->document(); |
313 | if (!document) |
314 | return nullptr; |
315 | |
316 | return &document->timing(); |
317 | } |
318 | |
319 | LoadTiming* PerformanceTiming::loadTiming() const |
320 | { |
321 | DocumentLoader* loader = documentLoader(); |
322 | if (!loader) |
323 | return nullptr; |
324 | |
325 | return &loader->timing(); |
326 | } |
327 | |
328 | unsigned long long PerformanceTiming::resourceLoadTimeRelativeToFetchStart(Seconds delta) const |
329 | { |
330 | ASSERT(delta >= 0_ms); |
331 | |
332 | LoadTiming* timing = loadTiming(); |
333 | if (!timing) |
334 | return 0; |
335 | |
336 | WallTime fetchStart = timing->monotonicTimeToPseudoWallTime(timing->fetchStart()); |
337 | WallTime combined = fetchStart + delta; |
338 | Seconds reduced = Performance::reduceTimeResolution(combined.secondsSinceEpoch()); |
339 | return static_cast<unsigned long long>(reduced.milliseconds()); |
340 | } |
341 | |
342 | unsigned long long PerformanceTiming::monotonicTimeToIntegerMilliseconds(MonotonicTime timeStamp) const |
343 | { |
344 | ASSERT(timeStamp.secondsSinceEpoch().seconds() >= 0); |
345 | |
346 | LoadTiming* timing = loadTiming(); |
347 | if (!timing) |
348 | return 0; |
349 | |
350 | WallTime wallTime = timing->monotonicTimeToPseudoWallTime(timeStamp); |
351 | Seconds reduced = Performance::reduceTimeResolution(wallTime.secondsSinceEpoch()); |
352 | return static_cast<unsigned long long>(reduced.milliseconds()); |
353 | } |
354 | |
355 | } // namespace WebCore |
356 | |