2 * Copyright (C) 2010 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
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
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.
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.
32 #include "PerformanceTiming.h"
34 #if ENABLE(WEB_TIMING)
37 #include "DocumentLoadTiming.h"
38 #include "DocumentLoader.h"
39 #include "DocumentTiming.h"
41 #include "ResourceLoadTiming.h"
42 #include "ResourceResponse.h"
43 #include <wtf/CurrentTime.h>
47 static unsigned long long toIntegerMilliseconds(double seconds)
50 return static_cast<unsigned long long>(seconds * 1000.0);
53 static double getPossiblySkewedTimeInKnownRange(double skewedTime, double lowerBound, double upperBound)
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)
64 if (upperBound <= 0.0)
65 upperBound = currentTime();
67 if (skewedTime >= upperBound)
70 ASSERT_UNUSED(lowerBound, skewedTime >= lowerBound);
71 ASSERT_UNUSED(upperBound, skewedTime <= upperBound);
77 PerformanceTiming::PerformanceTiming(Frame* frame)
82 Frame* PerformanceTiming::frame() const
87 void PerformanceTiming::disconnectFrame()
92 unsigned long long PerformanceTiming::navigationStart() const
94 DocumentLoadTiming* timing = documentLoadTiming();
98 return toIntegerMilliseconds(timing->navigationStart);
101 unsigned long long PerformanceTiming::unloadEventStart() const
103 DocumentLoadTiming* timing = documentLoadTiming();
107 if (timing->hasCrossOriginRedirect || !timing->hasSameOriginAsPreviousDocument)
110 return toIntegerMilliseconds(timing->unloadEventStart);
113 unsigned long long PerformanceTiming::unloadEventEnd() const
115 DocumentLoadTiming* timing = documentLoadTiming();
119 if (timing->hasCrossOriginRedirect || !timing->hasSameOriginAsPreviousDocument)
122 return toIntegerMilliseconds(timing->unloadEventEnd);
125 unsigned long long PerformanceTiming::redirectStart() const
127 DocumentLoadTiming* timing = documentLoadTiming();
131 if (timing->hasCrossOriginRedirect)
134 return toIntegerMilliseconds(timing->redirectStart);
137 unsigned long long PerformanceTiming::redirectEnd() const
139 DocumentLoadTiming* timing = documentLoadTiming();
143 if (timing->hasCrossOriginRedirect)
146 return toIntegerMilliseconds(timing->redirectEnd);
149 unsigned long long PerformanceTiming::fetchStart() const
151 DocumentLoadTiming* timing = documentLoadTiming();
155 return toIntegerMilliseconds(timing->fetchStart);
158 unsigned long long PerformanceTiming::domainLookupStart() const
160 ResourceLoadTiming* timing = resourceLoadTiming();
164 // This will be -1 when a DNS request is not performed.
165 // Rather than exposing a special value that indicates no DNS, we "backfill" with fetchStart.
166 int dnsStart = timing->dnsStart;
170 return resourceLoadTimeRelativeToAbsolute(dnsStart);
173 unsigned long long PerformanceTiming::domainLookupEnd() const
175 ResourceLoadTiming* timing = resourceLoadTiming();
177 return domainLookupStart();
179 // This will be -1 when a DNS request is not performed.
180 // Rather than exposing a special value that indicates no DNS, we "backfill" with domainLookupStart.
181 int dnsEnd = timing->dnsEnd;
183 return domainLookupStart();
185 return resourceLoadTimeRelativeToAbsolute(dnsEnd);
188 unsigned long long PerformanceTiming::connectStart() const
190 DocumentLoader* loader = documentLoader();
192 return domainLookupEnd();
194 ResourceLoadTiming* timing = loader->response().resourceLoadTiming();
196 return domainLookupEnd();
198 // connectStart will be -1 when a network request is not made.
199 // Rather than exposing a special value that indicates no new connection, we "backfill" with domainLookupEnd.
200 int connectStart = timing->connectStart;
201 if (connectStart < 0 || loader->response().connectionReused())
202 return domainLookupEnd();
204 // ResourceLoadTiming's connect phase includes DNS, however Navigation Timing's
205 // connect phase should not. So if there is DNS time, trim it from the start.
206 if (timing->dnsEnd >= 0 && timing->dnsEnd > connectStart)
207 connectStart = timing->dnsEnd;
209 return resourceLoadTimeRelativeToAbsolute(connectStart);
212 unsigned long long PerformanceTiming::connectEnd() const
214 DocumentLoader* loader = documentLoader();
216 return connectStart();
218 ResourceLoadTiming* timing = loader->response().resourceLoadTiming();
220 return connectStart();
222 // connectEnd will be -1 when a network request is not made.
223 // Rather than exposing a special value that indicates no new connection, we "backfill" with connectStart.
224 int connectEnd = timing->connectEnd;
225 if (connectEnd < 0 || loader->response().connectionReused())
226 return connectStart();
228 return resourceLoadTimeRelativeToAbsolute(connectEnd);
231 unsigned long long PerformanceTiming::secureConnectionStart() const
233 DocumentLoader* loader = documentLoader();
237 ResourceLoadTiming* timing = loader->response().resourceLoadTiming();
241 int sslStart = timing->sslStart;
245 return resourceLoadTimeRelativeToAbsolute(sslStart);
248 unsigned long long PerformanceTiming::requestStart() const
250 ResourceLoadTiming* timing = resourceLoadTiming();
254 ASSERT(timing->sendStart >= 0);
255 return resourceLoadTimeRelativeToAbsolute(timing->sendStart);
258 unsigned long long PerformanceTiming::responseStart() const
260 ResourceLoadTiming* timing = resourceLoadTiming();
262 return requestStart();
264 // FIXME: Response start needs to be the time of the first received byte.
265 // However, the ResourceLoadTiming API currently only supports the time
266 // the last header byte was received. For many responses with reasonable
267 // sized cookies, the HTTP headers fit into a single packet so this time
268 // is basically equivalent. But for some responses, particularly those with
269 // headers larger than a single packet, this time will be too late.
270 ASSERT(timing->receiveHeadersEnd >= 0);
271 return resourceLoadTimeRelativeToAbsolute(timing->receiveHeadersEnd);
274 unsigned long long PerformanceTiming::responseEnd() const
276 DocumentLoadTiming* timing = documentLoadTiming();
280 return toIntegerMilliseconds(timing->responseEnd);
283 unsigned long long PerformanceTiming::domLoading() const
285 const DocumentTiming* timing = documentTiming();
289 return toIntegerMilliseconds(timing->domLoading);
292 unsigned long long PerformanceTiming::domInteractive() const
294 const DocumentTiming* timing = documentTiming();
298 return toIntegerMilliseconds(timing->domInteractive);
301 unsigned long long PerformanceTiming::domContentLoadedEventStart() const
303 const DocumentTiming* timing = documentTiming();
307 return toIntegerMilliseconds(timing->domContentLoadedEventStart);
310 unsigned long long PerformanceTiming::domContentLoadedEventEnd() const
312 const DocumentTiming* timing = documentTiming();
316 return toIntegerMilliseconds(timing->domContentLoadedEventEnd);
319 unsigned long long PerformanceTiming::domComplete() const
321 const DocumentTiming* timing = documentTiming();
325 return toIntegerMilliseconds(timing->domComplete);
328 unsigned long long PerformanceTiming::loadEventStart() const
330 DocumentLoadTiming* timing = documentLoadTiming();
334 return toIntegerMilliseconds(timing->loadEventStart);
337 unsigned long long PerformanceTiming::loadEventEnd() const
339 DocumentLoadTiming* timing = documentLoadTiming();
343 return toIntegerMilliseconds(timing->loadEventEnd);
346 DocumentLoader* PerformanceTiming::documentLoader() const
351 return m_frame->loader()->documentLoader();
354 const DocumentTiming* PerformanceTiming::documentTiming() const
359 Document* document = m_frame->document();
363 return document->timing();
366 DocumentLoadTiming* PerformanceTiming::documentLoadTiming() const
368 DocumentLoader* loader = documentLoader();
372 return loader->timing();
375 ResourceLoadTiming* PerformanceTiming::resourceLoadTiming() const
377 DocumentLoader* loader = documentLoader();
381 return loader->response().resourceLoadTiming();
384 unsigned long long PerformanceTiming::resourceLoadTimeRelativeToAbsolute(int relativeSeconds) const
386 ASSERT(relativeSeconds >= 0);
387 ResourceLoadTiming* resourceTiming = resourceLoadTiming();
388 ASSERT(resourceTiming);
389 DocumentLoadTiming* documentTiming = documentLoadTiming();
390 ASSERT(documentTiming);
392 // The ResourceLoadTiming API's requestTime is the base time to which all
393 // other marks are relative. So to get an absolute time, we must add it to
394 // the relative marks.
396 // Since ResourceLoadTimings came from the network platform layer, we must
397 // check them for skew because they may be from another thread/process.
398 double baseTime = getPossiblySkewedTimeInKnownRange(resourceTiming->requestTime, documentTiming->fetchStart, documentTiming->responseEnd);
399 return toIntegerMilliseconds(baseTime) + relativeSeconds;
402 } // namespace WebCore
404 #endif // ENABLE(WEB_TIMING)