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 "core/page/PerformanceTiming.h"
     33 
     34 #include "core/dom/Document.h"
     35 #include "core/dom/DocumentTiming.h"
     36 #include "core/loader/DocumentLoadTiming.h"
     37 #include "core/loader/DocumentLoader.h"
     38 #include "core/loader/FrameLoader.h"
     39 #include "core/page/Frame.h"
     40 #include "core/platform/network/ResourceLoadTiming.h"
     41 #include "core/platform/network/ResourceResponse.h"
     42 
     43 namespace WebCore {
     44 
     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 
     51 PerformanceTiming::PerformanceTiming(Frame* frame)
     52     : DOMWindowProperty(frame)
     53 {
     54     ScriptWrappable::init(this);
     55 }
     56 
     57 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 
     66 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 
     78 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 
     90 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 
    102 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 
    114 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 
    123 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 
    138 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 
    153 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 
    177 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 
    196 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 
    213 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 
    223 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 
    238 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 
    247 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 
    256 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 
    265 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 
    274 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 
    283 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 
    292 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 
    301 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 
    310 DocumentLoader* PerformanceTiming::documentLoader() const
    311 {
    312     if (!m_frame)
    313         return 0;
    314 
    315     return m_frame->loader()->documentLoader();
    316 }
    317 
    318 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 
    330 DocumentLoadTiming* PerformanceTiming::documentLoadTiming() const
    331 {
    332     DocumentLoader* loader = documentLoader();
    333     if (!loader)
    334         return 0;
    335 
    336     return loader->timing();
    337 }
    338 
    339 ResourceLoadTiming* PerformanceTiming::resourceLoadTiming() const
    340 {
    341     DocumentLoader* loader = documentLoader();
    342     if (!loader)
    343         return 0;
    344 
    345     return loader->response().resourceLoadTiming();
    346 }
    347 
    348 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