WebCore/page/Timing.cpp
changeset 0 4f2f89ce4247
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/WebCore/page/Timing.cpp	Fri Sep 17 09:02:29 2010 +0300
@@ -0,0 +1,299 @@
+/*
+ * 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 "Timing.h"
+
+#if ENABLE(WEB_TIMING)
+
+#include "DocumentLoader.h"
+#include "Frame.h"
+#include "ResourceLoadTiming.h"
+#include "ResourceResponse.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 (skewedTime >= upperBound)
+        return upperBound;
+#else
+    ASSERT_UNUSED(lowerBound, skewedTime >= lowerBound);
+    ASSERT_UNUSED(upperBound, skewedTime <= upperBound);
+#endif
+
+    return skewedTime;
+}
+
+Timing::Timing(Frame* frame)
+    : m_frame(frame)
+{
+}
+
+Frame* Timing::frame() const
+{
+    return m_frame;
+}
+
+void Timing::disconnectFrame()
+{
+    m_frame = 0;
+}
+
+unsigned long long Timing::navigationStart() const
+{
+    DocumentLoadTiming* timing = documentLoadTiming();
+    if (!timing)
+        return 0;
+
+    return toIntegerMilliseconds(timing->navigationStart);
+}
+
+unsigned long long Timing::unloadEventEnd() const
+{
+    DocumentLoadTiming* timing = documentLoadTiming();
+    if (!timing)
+        return 0;
+
+    return toIntegerMilliseconds(timing->unloadEventEnd);
+}
+    
+unsigned long long Timing::redirectStart() const
+{
+    DocumentLoadTiming* timing = documentLoadTiming();
+    if (!timing)
+        return 0;
+
+    return toIntegerMilliseconds(timing->redirectStart);
+}
+    
+unsigned long long Timing::redirectEnd() const
+{
+    DocumentLoadTiming* timing = documentLoadTiming();
+    if (!timing)
+        return 0;
+
+    return toIntegerMilliseconds(timing->redirectEnd);
+}
+
+unsigned long long Timing::fetchStart() const
+{
+    DocumentLoadTiming* timing = documentLoadTiming();
+    if (!timing)
+        return 0;
+
+    return toIntegerMilliseconds(timing->fetchStart);
+}
+
+unsigned long long Timing::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 Timing::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 Timing::connectStart() const
+{
+    ResourceLoadTiming* timing = resourceLoadTiming();
+    if (!timing)
+        return 0;
+
+    // This will be -1 when a new connection is not established.
+    // Rather than exposing a special value that indicates no new connection, we "backfill" with domainLookupEnd.
+    int connectStart = timing->connectStart;
+    if (connectStart < 0)
+        return domainLookupEnd();
+
+    return resourceLoadTimeRelativeToAbsolute(connectStart);
+}
+
+unsigned long long Timing::connectEnd() const
+{
+    ResourceLoadTiming* timing = resourceLoadTiming();
+    if (!timing)
+        return 0;
+
+    // This will be -1 when a new connection is not established.
+    // Rather than exposing a special value that indicates no new connection, we "backfill" with connectStart.
+    int connectEnd = timing->connectEnd;
+    if (connectEnd < 0)
+        return connectStart();
+
+    return resourceLoadTimeRelativeToAbsolute(connectEnd);
+}
+
+unsigned long long Timing::requestStart() const
+{
+    ResourceLoadTiming* timing = resourceLoadTiming();
+    if (!timing)
+        return 0;
+
+    ASSERT(timing->sendStart >= 0);
+    return resourceLoadTimeRelativeToAbsolute(timing->sendStart);
+}
+
+unsigned long long Timing::requestEnd() const
+{
+    ResourceLoadTiming* timing = resourceLoadTiming();
+    if (!timing)
+        return 0;
+
+    ASSERT(timing->sendEnd >= 0);
+    return resourceLoadTimeRelativeToAbsolute(timing->sendEnd);
+}
+
+unsigned long long Timing::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 Timing::responseEnd() const
+{
+    DocumentLoadTiming* timing = documentLoadTiming();
+    if (!timing)
+        return 0;
+
+    return toIntegerMilliseconds(timing->responseEnd);
+}
+
+unsigned long long Timing::loadEventStart() const
+{
+    DocumentLoadTiming* timing = documentLoadTiming();
+    if (!timing)
+        return 0;
+
+    return toIntegerMilliseconds(timing->loadEventStart);
+}
+
+unsigned long long Timing::loadEventEnd() const
+{
+    DocumentLoadTiming* timing = documentLoadTiming();
+    if (!timing)
+        return 0;
+
+    return toIntegerMilliseconds(timing->loadEventEnd);
+}
+
+DocumentLoader* Timing::documentLoader() const
+{
+    if (!m_frame)
+        return 0;
+
+    return m_frame->loader()->documentLoader();
+}
+
+DocumentLoadTiming* Timing::documentLoadTiming() const
+{
+    DocumentLoader* loader = documentLoader();
+    if (!loader)
+        return 0;
+
+    return loader->timing();
+}
+
+ResourceLoadTiming* Timing::resourceLoadTiming() const
+{
+    DocumentLoader* loader = documentLoader();
+    if (!loader)
+        return 0;
+
+    return loader->response().resourceLoadTiming();
+}
+
+unsigned long long Timing::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 - (resourceTiming->receiveHeadersEnd / 1000.0));
+    return toIntegerMilliseconds(baseTime) + relativeSeconds;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_TIMING)