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