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 #if ENABLE(WEB_TIMING)
35
36 #include "Document.h"
37 #include "DocumentLoadTiming.h"
38 #include "DocumentLoader.h"
39 #include "DocumentTiming.h"
40 #include "Frame.h"
41 #include "ResourceLoadTiming.h"
42 #include "ResourceResponse.h"
43 #include <wtf/CurrentTime.h>
44
45 namespace WebCore {
46
toIntegerMilliseconds(double seconds)47 static unsigned long long toIntegerMilliseconds(double seconds)
48 {
49 ASSERT(seconds >= 0);
50 return static_cast<unsigned long long>(seconds * 1000.0);
51 }
52
getPossiblySkewedTimeInKnownRange(double skewedTime,double lowerBound,double upperBound)53 static double getPossiblySkewedTimeInKnownRange(double skewedTime, double lowerBound, double upperBound)
54 {
55 #if PLATFORM(CHROMIUM)
56 // The chromium port's currentTime() implementation only syncs with the
57 // system clock every 60 seconds. So it is possible for timing marks
58 // collected in different threads or processes to have a small skew.
59 // FIXME: It may be possible to add a currentTimeFromSystemTime() method
60 // that eliminates the skew.
61 if (skewedTime <= lowerBound)
62 return lowerBound;
63
64 if (upperBound <= 0.0)
65 upperBound = currentTime();
66
67 if (skewedTime >= upperBound)
68 return upperBound;
69 #else
70 ASSERT_UNUSED(lowerBound, skewedTime >= lowerBound);
71 ASSERT_UNUSED(upperBound, skewedTime <= upperBound);
72 #endif
73
74 return skewedTime;
75 }
76
PerformanceTiming(Frame * frame)77 PerformanceTiming::PerformanceTiming(Frame* frame)
78 : m_frame(frame)
79 {
80 }
81
frame() const82 Frame* PerformanceTiming::frame() const
83 {
84 return m_frame;
85 }
86
disconnectFrame()87 void PerformanceTiming::disconnectFrame()
88 {
89 m_frame = 0;
90 }
91
navigationStart() const92 unsigned long long PerformanceTiming::navigationStart() const
93 {
94 DocumentLoadTiming* timing = documentLoadTiming();
95 if (!timing)
96 return 0;
97
98 if (timing->hasCrossOriginRedirect)
99 return 0;
100
101 return toIntegerMilliseconds(timing->navigationStart);
102 }
103
unloadEventStart() const104 unsigned long long PerformanceTiming::unloadEventStart() const
105 {
106 DocumentLoadTiming* timing = documentLoadTiming();
107 if (!timing)
108 return 0;
109
110 if (timing->hasCrossOriginRedirect || !timing->hasSameOriginAsPreviousDocument)
111 return 0;
112
113 return toIntegerMilliseconds(timing->unloadEventStart);
114 }
115
unloadEventEnd() const116 unsigned long long PerformanceTiming::unloadEventEnd() const
117 {
118 DocumentLoadTiming* timing = documentLoadTiming();
119 if (!timing)
120 return 0;
121
122 if (timing->hasCrossOriginRedirect || !timing->hasSameOriginAsPreviousDocument)
123 return 0;
124
125 return toIntegerMilliseconds(timing->unloadEventEnd);
126 }
127
redirectStart() const128 unsigned long long PerformanceTiming::redirectStart() const
129 {
130 DocumentLoadTiming* timing = documentLoadTiming();
131 if (!timing)
132 return 0;
133
134 if (timing->hasCrossOriginRedirect)
135 return 0;
136
137 return toIntegerMilliseconds(timing->redirectStart);
138 }
139
redirectEnd() const140 unsigned long long PerformanceTiming::redirectEnd() const
141 {
142 DocumentLoadTiming* timing = documentLoadTiming();
143 if (!timing)
144 return 0;
145
146 if (timing->hasCrossOriginRedirect)
147 return 0;
148
149 return toIntegerMilliseconds(timing->redirectEnd);
150 }
151
fetchStart() const152 unsigned long long PerformanceTiming::fetchStart() const
153 {
154 DocumentLoadTiming* timing = documentLoadTiming();
155 if (!timing)
156 return 0;
157
158 return toIntegerMilliseconds(timing->fetchStart);
159 }
160
domainLookupStart() const161 unsigned long long PerformanceTiming::domainLookupStart() const
162 {
163 ResourceLoadTiming* timing = resourceLoadTiming();
164 if (!timing)
165 return fetchStart();
166
167 // This will be -1 when a DNS request is not performed.
168 // Rather than exposing a special value that indicates no DNS, we "backfill" with fetchStart.
169 int dnsStart = timing->dnsStart;
170 if (dnsStart < 0)
171 return fetchStart();
172
173 return resourceLoadTimeRelativeToAbsolute(dnsStart);
174 }
175
domainLookupEnd() const176 unsigned long long PerformanceTiming::domainLookupEnd() const
177 {
178 ResourceLoadTiming* timing = resourceLoadTiming();
179 if (!timing)
180 return domainLookupStart();
181
182 // This will be -1 when a DNS request is not performed.
183 // Rather than exposing a special value that indicates no DNS, we "backfill" with domainLookupStart.
184 int dnsEnd = timing->dnsEnd;
185 if (dnsEnd < 0)
186 return domainLookupStart();
187
188 return resourceLoadTimeRelativeToAbsolute(dnsEnd);
189 }
190
connectStart() const191 unsigned long long PerformanceTiming::connectStart() const
192 {
193 DocumentLoader* loader = documentLoader();
194 if (!loader)
195 return domainLookupEnd();
196
197 ResourceLoadTiming* timing = loader->response().resourceLoadTiming();
198 if (!timing)
199 return domainLookupEnd();
200
201 // connectStart will be -1 when a network request is not made.
202 // Rather than exposing a special value that indicates no new connection, we "backfill" with domainLookupEnd.
203 int connectStart = timing->connectStart;
204 if (connectStart < 0 || loader->response().connectionReused())
205 return domainLookupEnd();
206
207 // ResourceLoadTiming's connect phase includes DNS, however Navigation Timing's
208 // connect phase should not. So if there is DNS time, trim it from the start.
209 if (timing->dnsEnd >= 0 && timing->dnsEnd > connectStart)
210 connectStart = timing->dnsEnd;
211
212 return resourceLoadTimeRelativeToAbsolute(connectStart);
213 }
214
connectEnd() const215 unsigned long long PerformanceTiming::connectEnd() const
216 {
217 DocumentLoader* loader = documentLoader();
218 if (!loader)
219 return connectStart();
220
221 ResourceLoadTiming* timing = loader->response().resourceLoadTiming();
222 if (!timing)
223 return connectStart();
224
225 // connectEnd will be -1 when a network request is not made.
226 // Rather than exposing a special value that indicates no new connection, we "backfill" with connectStart.
227 int connectEnd = timing->connectEnd;
228 if (connectEnd < 0 || loader->response().connectionReused())
229 return connectStart();
230
231 return resourceLoadTimeRelativeToAbsolute(connectEnd);
232 }
233
secureConnectionStart() const234 unsigned long long PerformanceTiming::secureConnectionStart() const
235 {
236 DocumentLoader* loader = documentLoader();
237 if (!loader)
238 return 0;
239
240 ResourceLoadTiming* timing = loader->response().resourceLoadTiming();
241 if (!timing)
242 return 0;
243
244 int sslStart = timing->sslStart;
245 if (sslStart < 0)
246 return 0;
247
248 return resourceLoadTimeRelativeToAbsolute(sslStart);
249 }
250
requestStart() const251 unsigned long long PerformanceTiming::requestStart() const
252 {
253 ResourceLoadTiming* timing = resourceLoadTiming();
254 if (!timing)
255 return connectEnd();
256
257 ASSERT(timing->sendStart >= 0);
258 return resourceLoadTimeRelativeToAbsolute(timing->sendStart);
259 }
260
responseStart() const261 unsigned long long PerformanceTiming::responseStart() const
262 {
263 ResourceLoadTiming* timing = resourceLoadTiming();
264 if (!timing)
265 return requestStart();
266
267 // FIXME: Response start needs to be the time of the first received byte.
268 // However, the ResourceLoadTiming API currently only supports the time
269 // the last header byte was received. For many responses with reasonable
270 // sized cookies, the HTTP headers fit into a single packet so this time
271 // is basically equivalent. But for some responses, particularly those with
272 // headers larger than a single packet, this time will be too late.
273 ASSERT(timing->receiveHeadersEnd >= 0);
274 return resourceLoadTimeRelativeToAbsolute(timing->receiveHeadersEnd);
275 }
276
responseEnd() const277 unsigned long long PerformanceTiming::responseEnd() const
278 {
279 DocumentLoadTiming* timing = documentLoadTiming();
280 if (!timing)
281 return 0;
282
283 return toIntegerMilliseconds(timing->responseEnd);
284 }
285
domLoading() const286 unsigned long long PerformanceTiming::domLoading() const
287 {
288 const DocumentTiming* timing = documentTiming();
289 if (!timing)
290 return fetchStart();
291
292 return toIntegerMilliseconds(timing->domLoading);
293 }
294
domInteractive() const295 unsigned long long PerformanceTiming::domInteractive() const
296 {
297 const DocumentTiming* timing = documentTiming();
298 if (!timing)
299 return 0;
300
301 return toIntegerMilliseconds(timing->domInteractive);
302 }
303
domContentLoadedEventStart() const304 unsigned long long PerformanceTiming::domContentLoadedEventStart() const
305 {
306 const DocumentTiming* timing = documentTiming();
307 if (!timing)
308 return 0;
309
310 return toIntegerMilliseconds(timing->domContentLoadedEventStart);
311 }
312
domContentLoadedEventEnd() const313 unsigned long long PerformanceTiming::domContentLoadedEventEnd() const
314 {
315 const DocumentTiming* timing = documentTiming();
316 if (!timing)
317 return 0;
318
319 return toIntegerMilliseconds(timing->domContentLoadedEventEnd);
320 }
321
domComplete() const322 unsigned long long PerformanceTiming::domComplete() const
323 {
324 const DocumentTiming* timing = documentTiming();
325 if (!timing)
326 return 0;
327
328 return toIntegerMilliseconds(timing->domComplete);
329 }
330
loadEventStart() const331 unsigned long long PerformanceTiming::loadEventStart() const
332 {
333 DocumentLoadTiming* timing = documentLoadTiming();
334 if (!timing)
335 return 0;
336
337 return toIntegerMilliseconds(timing->loadEventStart);
338 }
339
loadEventEnd() const340 unsigned long long PerformanceTiming::loadEventEnd() const
341 {
342 DocumentLoadTiming* timing = documentLoadTiming();
343 if (!timing)
344 return 0;
345
346 return toIntegerMilliseconds(timing->loadEventEnd);
347 }
348
documentLoader() const349 DocumentLoader* PerformanceTiming::documentLoader() const
350 {
351 if (!m_frame)
352 return 0;
353
354 return m_frame->loader()->documentLoader();
355 }
356
documentTiming() const357 const DocumentTiming* PerformanceTiming::documentTiming() const
358 {
359 if (!m_frame)
360 return 0;
361
362 Document* document = m_frame->document();
363 if (!document)
364 return 0;
365
366 return document->timing();
367 }
368
documentLoadTiming() const369 DocumentLoadTiming* PerformanceTiming::documentLoadTiming() const
370 {
371 DocumentLoader* loader = documentLoader();
372 if (!loader)
373 return 0;
374
375 return loader->timing();
376 }
377
resourceLoadTiming() const378 ResourceLoadTiming* PerformanceTiming::resourceLoadTiming() const
379 {
380 DocumentLoader* loader = documentLoader();
381 if (!loader)
382 return 0;
383
384 return loader->response().resourceLoadTiming();
385 }
386
resourceLoadTimeRelativeToAbsolute(int relativeSeconds) const387 unsigned long long PerformanceTiming::resourceLoadTimeRelativeToAbsolute(int relativeSeconds) const
388 {
389 ASSERT(relativeSeconds >= 0);
390 ResourceLoadTiming* resourceTiming = resourceLoadTiming();
391 ASSERT(resourceTiming);
392 DocumentLoadTiming* documentTiming = documentLoadTiming();
393 ASSERT(documentTiming);
394
395 // The ResourceLoadTiming API's requestTime is the base time to which all
396 // other marks are relative. So to get an absolute time, we must add it to
397 // the relative marks.
398 //
399 // Since ResourceLoadTimings came from the network platform layer, we must
400 // check them for skew because they may be from another thread/process.
401 double baseTime = getPossiblySkewedTimeInKnownRange(resourceTiming->requestTime, documentTiming->fetchStart, documentTiming->responseEnd);
402 return toIntegerMilliseconds(baseTime) + relativeSeconds;
403 }
404
405 } // namespace WebCore
406
407 #endif // ENABLE(WEB_TIMING)
408