diff options
Diffstat (limited to 'Source/WebCore/page/PerformanceTiming.cpp')
-rw-r--r-- | Source/WebCore/page/PerformanceTiming.cpp | 403 |
1 files changed, 403 insertions, 0 deletions
diff --git a/Source/WebCore/page/PerformanceTiming.cpp b/Source/WebCore/page/PerformanceTiming.cpp new file mode 100644 index 0000000..fb8bfb7 --- /dev/null +++ b/Source/WebCore/page/PerformanceTiming.cpp @@ -0,0 +1,403 @@ +/* + * Copyright (C) 2010 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "PerformanceTiming.h" + +#if ENABLE(WEB_TIMING) + +#include "DocumentLoadTiming.h" +#include "DocumentLoader.h" +#include "DocumentTiming.h" +#include "Frame.h" +#include "ResourceLoadTiming.h" +#include "ResourceResponse.h" +#include <wtf/CurrentTime.h> + +namespace WebCore { + +static unsigned long long toIntegerMilliseconds(double seconds) +{ + ASSERT(seconds >= 0); + return static_cast<unsigned long long>(seconds * 1000.0); +} + +static double getPossiblySkewedTimeInKnownRange(double skewedTime, double lowerBound, double upperBound) +{ +#if PLATFORM(CHROMIUM) + // The chromium port's currentTime() implementation only syncs with the + // system clock every 60 seconds. So it is possible for timing marks + // collected in different threads or processes to have a small skew. + // FIXME: It may be possible to add a currentTimeFromSystemTime() method + // that eliminates the skew. + if (skewedTime <= lowerBound) + return lowerBound; + + if (upperBound <= 0.0) + upperBound = currentTime(); + + if (skewedTime >= upperBound) + return upperBound; +#else + ASSERT_UNUSED(lowerBound, skewedTime >= lowerBound); + ASSERT_UNUSED(upperBound, skewedTime <= upperBound); +#endif + + return skewedTime; +} + +PerformanceTiming::PerformanceTiming(Frame* frame) + : m_frame(frame) +{ +} + +Frame* PerformanceTiming::frame() const +{ + return m_frame; +} + +void PerformanceTiming::disconnectFrame() +{ + m_frame = 0; +} + +unsigned long long PerformanceTiming::navigationStart() const +{ + DocumentLoadTiming* timing = documentLoadTiming(); + if (!timing) + return 0; + + return toIntegerMilliseconds(timing->navigationStart); +} + +unsigned long long PerformanceTiming::unloadEventStart() const +{ + DocumentLoadTiming* timing = documentLoadTiming(); + if (!timing) + return 0; + + if (!timing->hasSameOriginAsPreviousDocument) + return 0; + + return toIntegerMilliseconds(timing->unloadEventStart); +} + +unsigned long long PerformanceTiming::unloadEventEnd() const +{ + DocumentLoadTiming* timing = documentLoadTiming(); + if (!timing) + return 0; + + if (!timing->hasSameOriginAsPreviousDocument) + return 0; + + return toIntegerMilliseconds(timing->unloadEventEnd); +} + +unsigned long long PerformanceTiming::redirectStart() const +{ + DocumentLoadTiming* timing = documentLoadTiming(); + if (!timing) + return 0; + + if (timing->hasCrossOriginRedirect) + return 0; + + return toIntegerMilliseconds(timing->redirectStart); +} + +unsigned long long PerformanceTiming::redirectEnd() const +{ + DocumentLoadTiming* timing = documentLoadTiming(); + if (!timing) + return 0; + + if (timing->hasCrossOriginRedirect) + return 0; + + return toIntegerMilliseconds(timing->redirectEnd); +} + +unsigned long long PerformanceTiming::fetchStart() const +{ + DocumentLoadTiming* timing = documentLoadTiming(); + if (!timing) + return 0; + + return toIntegerMilliseconds(timing->fetchStart); +} + +unsigned long long PerformanceTiming::domainLookupStart() const +{ + ResourceLoadTiming* timing = resourceLoadTiming(); + if (!timing) + return 0; + + // This will be -1 when a DNS request is not performed. + // Rather than exposing a special value that indicates no DNS, we "backfill" with fetchStart. + int dnsStart = timing->dnsStart; + if (dnsStart < 0) + return fetchStart(); + + return resourceLoadTimeRelativeToAbsolute(dnsStart); +} + +unsigned long long PerformanceTiming::domainLookupEnd() const +{ + ResourceLoadTiming* timing = resourceLoadTiming(); + if (!timing) + return 0; + + // This will be -1 when a DNS request is not performed. + // Rather than exposing a special value that indicates no DNS, we "backfill" with domainLookupStart. + int dnsEnd = timing->dnsEnd; + if (dnsEnd < 0) + return domainLookupStart(); + + return resourceLoadTimeRelativeToAbsolute(dnsEnd); +} + +unsigned long long PerformanceTiming::connectStart() const +{ + DocumentLoader* loader = documentLoader(); + if (!loader) + return 0; + + ResourceLoadTiming* timing = loader->response().resourceLoadTiming(); + if (!timing) + return 0; + + // connectStart will be -1 when a network request is not made. + // Rather than exposing a special value that indicates no new connection, we "backfill" with domainLookupEnd. + int connectStart = timing->connectStart; + if (connectStart < 0 || loader->response().connectionReused()) + return domainLookupEnd(); + + // ResourceLoadTiming's connect phase includes DNS, however Navigation Timing's + // connect phase should not. So if there is DNS time, trim it from the start. + if (timing->dnsEnd >= 0 && timing->dnsEnd > connectStart) + connectStart = timing->dnsEnd; + + return resourceLoadTimeRelativeToAbsolute(connectStart); +} + +unsigned long long PerformanceTiming::connectEnd() const +{ + DocumentLoader* loader = documentLoader(); + if (!loader) + return 0; + + ResourceLoadTiming* timing = loader->response().resourceLoadTiming(); + if (!timing) + return 0; + + // connectEnd will be -1 when a network request is not made. + // Rather than exposing a special value that indicates no new connection, we "backfill" with connectStart. + int connectEnd = timing->connectEnd; + if (connectEnd < 0 || loader->response().connectionReused()) + return connectStart(); + + return resourceLoadTimeRelativeToAbsolute(connectEnd); +} + +unsigned long long PerformanceTiming::sslHandshakeStart() const +{ + DocumentLoader* loader = documentLoader(); + if (!loader) + return 0; + + ResourceLoadTiming* timing = loader->response().resourceLoadTiming(); + if (!timing) + return 0; + + int sslStart = timing->sslStart; + if (sslStart < 0) + return 0; + + return resourceLoadTimeRelativeToAbsolute(sslStart); +} + +unsigned long long PerformanceTiming::requestStart() const +{ + ResourceLoadTiming* timing = resourceLoadTiming(); + if (!timing) + return 0; + + ASSERT(timing->sendStart >= 0); + return resourceLoadTimeRelativeToAbsolute(timing->sendStart); +} + +unsigned long long PerformanceTiming::responseStart() const +{ + ResourceLoadTiming* timing = resourceLoadTiming(); + if (!timing) + return 0; + + // FIXME: Response start needs to be the time of the first received byte. + // However, the ResourceLoadTiming API currently only supports the time + // the last header byte was received. For many responses with reasonable + // sized cookies, the HTTP headers fit into a single packet so this time + // is basically equivalent. But for some responses, particularly those with + // headers larger than a single packet, this time will be too late. + ASSERT(timing->receiveHeadersEnd >= 0); + return resourceLoadTimeRelativeToAbsolute(timing->receiveHeadersEnd); +} + +unsigned long long PerformanceTiming::responseEnd() const +{ + DocumentLoadTiming* timing = documentLoadTiming(); + if (!timing) + return 0; + + return toIntegerMilliseconds(timing->responseEnd); +} + +unsigned long long PerformanceTiming::domLoading() const +{ + const DocumentTiming* timing = documentTiming(); + if (!timing) + return 0; + + return toIntegerMilliseconds(timing->domLoading); +} + +unsigned long long PerformanceTiming::domInteractive() const +{ + const DocumentTiming* timing = documentTiming(); + if (!timing) + return 0; + + return toIntegerMilliseconds(timing->domInteractive); +} + +unsigned long long PerformanceTiming::domContentLoadedEventStart() const +{ + const DocumentTiming* timing = documentTiming(); + if (!timing) + return 0; + + return toIntegerMilliseconds(timing->domContentLoadedEventStart); +} + +unsigned long long PerformanceTiming::domContentLoadedEventEnd() const +{ + const DocumentTiming* timing = documentTiming(); + if (!timing) + return 0; + + return toIntegerMilliseconds(timing->domContentLoadedEventEnd); +} + +unsigned long long PerformanceTiming::domComplete() const +{ + const DocumentTiming* timing = documentTiming(); + if (!timing) + return 0; + + return toIntegerMilliseconds(timing->domComplete); +} + +unsigned long long PerformanceTiming::loadEventStart() const +{ + DocumentLoadTiming* timing = documentLoadTiming(); + if (!timing) + return 0; + + return toIntegerMilliseconds(timing->loadEventStart); +} + +unsigned long long PerformanceTiming::loadEventEnd() const +{ + DocumentLoadTiming* timing = documentLoadTiming(); + if (!timing) + return 0; + + return toIntegerMilliseconds(timing->loadEventEnd); +} + +DocumentLoader* PerformanceTiming::documentLoader() const +{ + if (!m_frame) + return 0; + + return m_frame->loader()->documentLoader(); +} + +const DocumentTiming* PerformanceTiming::documentTiming() const +{ + if (!m_frame) + return 0; + + Document* document = m_frame->document(); + if (!document) + return 0; + + return document->timing(); +} + +DocumentLoadTiming* PerformanceTiming::documentLoadTiming() const +{ + DocumentLoader* loader = documentLoader(); + if (!loader) + return 0; + + return loader->timing(); +} + +ResourceLoadTiming* PerformanceTiming::resourceLoadTiming() const +{ + DocumentLoader* loader = documentLoader(); + if (!loader) + return 0; + + return loader->response().resourceLoadTiming(); +} + +unsigned long long PerformanceTiming::resourceLoadTimeRelativeToAbsolute(int relativeSeconds) const +{ + ASSERT(relativeSeconds >= 0); + ResourceLoadTiming* resourceTiming = resourceLoadTiming(); + ASSERT(resourceTiming); + DocumentLoadTiming* documentTiming = documentLoadTiming(); + ASSERT(documentTiming); + + // The ResourceLoadTiming API's requestTime is the base time to which all + // other marks are relative. So to get an absolute time, we must add it to + // the relative marks. + // + // Since ResourceLoadTimings came from the network platform layer, we must + // check them for skew because they may be from another thread/process. + double baseTime = getPossiblySkewedTimeInKnownRange(resourceTiming->requestTime, documentTiming->fetchStart, documentTiming->responseEnd); + return toIntegerMilliseconds(baseTime) + relativeSeconds; +} + +} // namespace WebCore + +#endif // ENABLE(WEB_TIMING) |