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 "core/timing/PerformanceTiming.h"
33
34 #include "core/dom/Document.h"
35 #include "core/dom/DocumentTiming.h"
36 #include "core/frame/Frame.h"
37 #include "core/loader/DocumentLoadTiming.h"
38 #include "core/loader/DocumentLoader.h"
39 #include "core/loader/FrameLoader.h"
40 #include "platform/network/ResourceLoadTiming.h"
41 #include "platform/network/ResourceResponse.h"
42
43 namespace WebCore {
44
toIntegerMilliseconds(double seconds)45 static unsigned long long toIntegerMilliseconds(double seconds)
46 {
47 ASSERT(seconds >= 0);
48 return static_cast<unsigned long long>(seconds * 1000.0);
49 }
50
PerformanceTiming(Frame * frame)51 PerformanceTiming::PerformanceTiming(Frame* frame)
52 : DOMWindowProperty(frame)
53 {
54 ScriptWrappable::init(this);
55 }
56
navigationStart() const57 unsigned long long PerformanceTiming::navigationStart() const
58 {
59 DocumentLoadTiming* timing = documentLoadTiming();
60 if (!timing)
61 return 0;
62
63 return monotonicTimeToIntegerMilliseconds(timing->navigationStart());
64 }
65
unloadEventStart() const66 unsigned long long PerformanceTiming::unloadEventStart() const
67 {
68 DocumentLoadTiming* timing = documentLoadTiming();
69 if (!timing)
70 return 0;
71
72 if (timing->hasCrossOriginRedirect() || !timing->hasSameOriginAsPreviousDocument())
73 return 0;
74
75 return monotonicTimeToIntegerMilliseconds(timing->unloadEventStart());
76 }
77
unloadEventEnd() const78 unsigned long long PerformanceTiming::unloadEventEnd() const
79 {
80 DocumentLoadTiming* timing = documentLoadTiming();
81 if (!timing)
82 return 0;
83
84 if (timing->hasCrossOriginRedirect() || !timing->hasSameOriginAsPreviousDocument())
85 return 0;
86
87 return monotonicTimeToIntegerMilliseconds(timing->unloadEventEnd());
88 }
89
redirectStart() const90 unsigned long long PerformanceTiming::redirectStart() const
91 {
92 DocumentLoadTiming* timing = documentLoadTiming();
93 if (!timing)
94 return 0;
95
96 if (timing->hasCrossOriginRedirect())
97 return 0;
98
99 return monotonicTimeToIntegerMilliseconds(timing->redirectStart());
100 }
101
redirectEnd() const102 unsigned long long PerformanceTiming::redirectEnd() const
103 {
104 DocumentLoadTiming* timing = documentLoadTiming();
105 if (!timing)
106 return 0;
107
108 if (timing->hasCrossOriginRedirect())
109 return 0;
110
111 return monotonicTimeToIntegerMilliseconds(timing->redirectEnd());
112 }
113
fetchStart() const114 unsigned long long PerformanceTiming::fetchStart() const
115 {
116 DocumentLoadTiming* timing = documentLoadTiming();
117 if (!timing)
118 return 0;
119
120 return monotonicTimeToIntegerMilliseconds(timing->fetchStart());
121 }
122
domainLookupStart() const123 unsigned long long PerformanceTiming::domainLookupStart() const
124 {
125 ResourceLoadTiming* timing = resourceLoadTiming();
126 if (!timing)
127 return fetchStart();
128
129 // This will be zero when a DNS request is not performed.
130 // Rather than exposing a special value that indicates no DNS, we "backfill" with fetchStart.
131 double dnsStart = timing->dnsStart;
132 if (dnsStart == 0.0)
133 return fetchStart();
134
135 return monotonicTimeToIntegerMilliseconds(dnsStart);
136 }
137
domainLookupEnd() const138 unsigned long long PerformanceTiming::domainLookupEnd() const
139 {
140 ResourceLoadTiming* timing = resourceLoadTiming();
141 if (!timing)
142 return domainLookupStart();
143
144 // This will be zero when a DNS request is not performed.
145 // Rather than exposing a special value that indicates no DNS, we "backfill" with domainLookupStart.
146 double dnsEnd = timing->dnsEnd;
147 if (dnsEnd == 0.0)
148 return domainLookupStart();
149
150 return monotonicTimeToIntegerMilliseconds(dnsEnd);
151 }
152
connectStart() const153 unsigned long long PerformanceTiming::connectStart() const
154 {
155 DocumentLoader* loader = documentLoader();
156 if (!loader)
157 return domainLookupEnd();
158
159 ResourceLoadTiming* timing = loader->response().resourceLoadTiming();
160 if (!timing)
161 return domainLookupEnd();
162
163 // connectStart will be zero when a network request is not made.
164 // Rather than exposing a special value that indicates no new connection, we "backfill" with domainLookupEnd.
165 double connectStart = timing->connectStart;
166 if (connectStart == 0.0 || loader->response().connectionReused())
167 return domainLookupEnd();
168
169 // ResourceLoadTiming's connect phase includes DNS, however Navigation Timing's
170 // connect phase should not. So if there is DNS time, trim it from the start.
171 if (timing->dnsEnd > 0.0 && timing->dnsEnd > connectStart)
172 connectStart = timing->dnsEnd;
173
174 return monotonicTimeToIntegerMilliseconds(connectStart);
175 }
176
connectEnd() const177 unsigned long long PerformanceTiming::connectEnd() const
178 {
179 DocumentLoader* loader = documentLoader();
180 if (!loader)
181 return connectStart();
182
183 ResourceLoadTiming* timing = loader->response().resourceLoadTiming();
184 if (!timing)
185 return connectStart();
186
187 // connectEnd will be zero when a network request is not made.
188 // Rather than exposing a special value that indicates no new connection, we "backfill" with connectStart.
189 double connectEnd = timing->connectEnd;
190 if (connectEnd == 0.0 || loader->response().connectionReused())
191 return connectStart();
192
193 return monotonicTimeToIntegerMilliseconds(connectEnd);
194 }
195
secureConnectionStart() const196 unsigned long long PerformanceTiming::secureConnectionStart() const
197 {
198 DocumentLoader* loader = documentLoader();
199 if (!loader)
200 return 0;
201
202 ResourceLoadTiming* timing = loader->response().resourceLoadTiming();
203 if (!timing)
204 return 0;
205
206 double sslStart = timing->sslStart;
207 if (sslStart == 0.0)
208 return 0;
209
210 return monotonicTimeToIntegerMilliseconds(sslStart);
211 }
212
requestStart() const213 unsigned long long PerformanceTiming::requestStart() const
214 {
215 ResourceLoadTiming* timing = resourceLoadTiming();
216
217 if (!timing || timing->sendStart == 0.0)
218 return connectEnd();
219
220 return monotonicTimeToIntegerMilliseconds(timing->sendStart);
221 }
222
responseStart() const223 unsigned long long PerformanceTiming::responseStart() const
224 {
225 ResourceLoadTiming* timing = resourceLoadTiming();
226 if (!timing || timing->receiveHeadersEnd == 0.0)
227 return requestStart();
228
229 // FIXME: Response start needs to be the time of the first received byte.
230 // However, the ResourceLoadTiming API currently only supports the time
231 // the last header byte was received. For many responses with reasonable
232 // sized cookies, the HTTP headers fit into a single packet so this time
233 // is basically equivalent. But for some responses, particularly those with
234 // headers larger than a single packet, this time will be too late.
235 return monotonicTimeToIntegerMilliseconds(timing->receiveHeadersEnd);
236 }
237
responseEnd() const238 unsigned long long PerformanceTiming::responseEnd() const
239 {
240 DocumentLoadTiming* timing = documentLoadTiming();
241 if (!timing)
242 return 0;
243
244 return monotonicTimeToIntegerMilliseconds(timing->responseEnd());
245 }
246
domLoading() const247 unsigned long long PerformanceTiming::domLoading() const
248 {
249 const DocumentTiming* timing = documentTiming();
250 if (!timing)
251 return fetchStart();
252
253 return monotonicTimeToIntegerMilliseconds(timing->domLoading);
254 }
255
domInteractive() const256 unsigned long long PerformanceTiming::domInteractive() const
257 {
258 const DocumentTiming* timing = documentTiming();
259 if (!timing)
260 return 0;
261
262 return monotonicTimeToIntegerMilliseconds(timing->domInteractive);
263 }
264
domContentLoadedEventStart() const265 unsigned long long PerformanceTiming::domContentLoadedEventStart() const
266 {
267 const DocumentTiming* timing = documentTiming();
268 if (!timing)
269 return 0;
270
271 return monotonicTimeToIntegerMilliseconds(timing->domContentLoadedEventStart);
272 }
273
domContentLoadedEventEnd() const274 unsigned long long PerformanceTiming::domContentLoadedEventEnd() const
275 {
276 const DocumentTiming* timing = documentTiming();
277 if (!timing)
278 return 0;
279
280 return monotonicTimeToIntegerMilliseconds(timing->domContentLoadedEventEnd);
281 }
282
domComplete() const283 unsigned long long PerformanceTiming::domComplete() const
284 {
285 const DocumentTiming* timing = documentTiming();
286 if (!timing)
287 return 0;
288
289 return monotonicTimeToIntegerMilliseconds(timing->domComplete);
290 }
291
loadEventStart() const292 unsigned long long PerformanceTiming::loadEventStart() const
293 {
294 DocumentLoadTiming* timing = documentLoadTiming();
295 if (!timing)
296 return 0;
297
298 return monotonicTimeToIntegerMilliseconds(timing->loadEventStart());
299 }
300
loadEventEnd() const301 unsigned long long PerformanceTiming::loadEventEnd() const
302 {
303 DocumentLoadTiming* timing = documentLoadTiming();
304 if (!timing)
305 return 0;
306
307 return monotonicTimeToIntegerMilliseconds(timing->loadEventEnd());
308 }
309
documentLoader() const310 DocumentLoader* PerformanceTiming::documentLoader() const
311 {
312 if (!m_frame)
313 return 0;
314
315 return m_frame->loader().documentLoader();
316 }
317
documentTiming() const318 const DocumentTiming* PerformanceTiming::documentTiming() const
319 {
320 if (!m_frame)
321 return 0;
322
323 Document* document = m_frame->document();
324 if (!document)
325 return 0;
326
327 return document->timing();
328 }
329
documentLoadTiming() const330 DocumentLoadTiming* PerformanceTiming::documentLoadTiming() const
331 {
332 DocumentLoader* loader = documentLoader();
333 if (!loader)
334 return 0;
335
336 return loader->timing();
337 }
338
resourceLoadTiming() const339 ResourceLoadTiming* PerformanceTiming::resourceLoadTiming() const
340 {
341 DocumentLoader* loader = documentLoader();
342 if (!loader)
343 return 0;
344
345 return loader->response().resourceLoadTiming();
346 }
347
monotonicTimeToIntegerMilliseconds(double monotonicSeconds) const348 unsigned long long PerformanceTiming::monotonicTimeToIntegerMilliseconds(double monotonicSeconds) const
349 {
350 ASSERT(monotonicSeconds >= 0);
351 const DocumentLoadTiming* timing = documentLoadTiming();
352 ASSERT(timing);
353 return toIntegerMilliseconds(timing->monotonicTimeToPseudoWallTime(monotonicSeconds));
354 }
355
356 } // namespace WebCore
357