Bug 576006 - Implement timing interface for channels
authorChristian Biesinger <cbiesinger@gmail.com>
Sat, 21 May 2011 03:03:36 -0700
changeset 69830 17b04197ed58fc0ff4c4db67491ad8c97a06d971
parent 69829 6672e37207b89055a16a78671408f346bfebe2e2
child 69836 b370698fb55c133b989b743a65174a5062fbd197
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
bugs576006
milestone6.0a1
Bug 576006 - Implement timing interface for channels r=jduell sr=bz
embedding/components/webbrowserpersist/src/nsWebBrowserPersist.cpp
netwerk/base/public/Makefile.in
netwerk/base/public/nsISocketTransport.idl
netwerk/base/public/nsITimedChannel.idl
netwerk/base/src/nsSocketTransport2.cpp
netwerk/locales/en-US/necko.properties
netwerk/protocol/ftp/nsFtpConnectionThread.cpp
netwerk/protocol/http/HttpBaseChannel.cpp
netwerk/protocol/http/HttpBaseChannel.h
netwerk/protocol/http/TimingStruct.h
netwerk/protocol/http/nsHttp.h
netwerk/protocol/http/nsHttpChannel.cpp
netwerk/protocol/http/nsHttpChannel.h
netwerk/protocol/http/nsHttpTransaction.cpp
netwerk/protocol/http/nsHttpTransaction.h
netwerk/test/TestProtocols.cpp
toolkit/components/console/hudservice/HUDService.jsm
--- a/embedding/components/webbrowserpersist/src/nsWebBrowserPersist.cpp
+++ b/embedding/components/webbrowserpersist/src/nsWebBrowserPersist.cpp
@@ -1016,16 +1016,17 @@ NS_IMETHODIMP nsWebBrowserPersist::OnSta
 {
     if (mProgressListener)
     {
         // We need to filter out non-error error codes.
         // Is the only NS_SUCCEEDED value NS_OK?
         switch ( status )
         {
         case NS_NET_STATUS_RESOLVING_HOST:
+        case NS_NET_STATUS_RESOLVED_HOST:
         case NS_NET_STATUS_BEGIN_FTP_TRANSACTION:
         case NS_NET_STATUS_END_FTP_TRANSACTION:
         case NS_NET_STATUS_CONNECTING_TO:
         case NS_NET_STATUS_CONNECTED_TO:
         case NS_NET_STATUS_SENDING_TO:
         case NS_NET_STATUS_RECEIVING_FROM:
         case NS_NET_STATUS_WAITING_FOR:
         case nsITransport::STATUS_READING:
--- a/netwerk/base/public/Makefile.in
+++ b/netwerk/base/public/Makefile.in
@@ -107,16 +107,17 @@ XPIDLSRCS	= \
 		nsIRequestObserverProxy.idl \
 		nsISecurityInfoProvider.idl \
 		nsIStreamListenerTee.idl \
 		nsISimpleStreamListener.idl \
 		nsIStreamTransportService.idl \
 		nsIStreamLoader.idl \
 		nsISyncStreamListener.idl \
 		nsISystemProxySettings.idl \
+		nsITimedChannel.idl \
 		nsIUnicharStreamLoader.idl \
 		nsIUploadChannel2.idl \
 		nsIStandardURL.idl \
 		nsINestedURI.idl \
 		nsIURLParser.idl \
 		nsIURIChecker.idl \
 		nsISecurityEventSink.idl \
 		nsISecretDecoderRing.idl \
--- a/netwerk/base/public/nsISocketTransport.idl
+++ b/netwerk/base/public/nsISocketTransport.idl
@@ -128,16 +128,17 @@ interface nsISocketTransport : nsITransp
      * The values of these status codes must never change.
      *
      * The status codes appear in near-chronological order (not in numeric
      * order).  STATUS_RESOLVING may be skipped if the host does not need to be
      * resolved.  STATUS_WAITING_FOR is an optional status code, which the impl
      * of this interface may choose not to generate.
      */
     const unsigned long STATUS_RESOLVING      = 0x804b0003;
+    const unsigned long STATUS_RESOLVED       = 0x804b000b;
     const unsigned long STATUS_CONNECTING_TO  = 0x804b0007;
     const unsigned long STATUS_CONNECTED_TO   = 0x804b0004;
     const unsigned long STATUS_SENDING_TO     = 0x804b0005;
     const unsigned long STATUS_WAITING_FOR    = 0x804b000a;
     const unsigned long STATUS_RECEIVING_FROM = 0x804b0006;
 
     /**
      * connectionFlags is a bitmask that can be used to modify underlying 
@@ -174,14 +175,15 @@ interface nsISocketTransport : nsITransp
 
 };
 
 %{C++
 /**
  * #define's for compatibility
  */
 #define NS_NET_STATUS_RESOLVING_HOST nsISocketTransport::STATUS_RESOLVING
+#define NS_NET_STATUS_RESOLVED_HOST  nsISocketTransport::STATUS_RESOLVED
 #define NS_NET_STATUS_CONNECTED_TO   nsISocketTransport::STATUS_CONNECTED_TO
 #define NS_NET_STATUS_SENDING_TO     nsISocketTransport::STATUS_SENDING_TO
 #define NS_NET_STATUS_RECEIVING_FROM nsISocketTransport::STATUS_RECEIVING_FROM
 #define NS_NET_STATUS_CONNECTING_TO  nsISocketTransport::STATUS_CONNECTING_TO
 #define NS_NET_STATUS_WAITING_FOR    nsISocketTransport::STATUS_WAITING_FOR
 %}
new file mode 100644
--- /dev/null
+++ b/netwerk/base/public/nsITimedChannel.idl
@@ -0,0 +1,85 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla networking code.
+ *
+ * The Initial Developer of the Original Code is
+ * Google Inc.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Christian Biesinger <cbiesinger@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsISupports.idl"
+%{C++
+namespace mozilla {
+class TimeStamp;
+}
+%}
+
+native TimeStamp(mozilla::TimeStamp);
+
+// All properties return zero if the value is not available
+[scriptable, uuid(c259b593-a9bf-4d08-8149-ef89e1977dc4)]
+interface nsITimedChannel : nsISupports {
+  // Set this attribute to true to enable collection of timing data.
+  // channelCreationTime will be available even with this attribute set to
+  // false.
+  attribute boolean timingEnabled;
+
+  [notxpcom] readonly attribute TimeStamp channelCreation;
+  [notxpcom] readonly attribute TimeStamp asyncOpen;
+
+  // The following are only set when the document is not (only) read from the
+  // cache
+  [notxpcom] readonly attribute TimeStamp domainLookupStart;
+  [notxpcom] readonly attribute TimeStamp domainLookupEnd;
+  [notxpcom] readonly attribute TimeStamp connectStart;
+  [notxpcom] readonly attribute TimeStamp connectEnd;
+  [notxpcom] readonly attribute TimeStamp requestStart;
+  [notxpcom] readonly attribute TimeStamp responseStart;
+  [notxpcom] readonly attribute TimeStamp responseEnd;
+
+  // The following are only set if the document is (partially) read from the
+  // cache
+  [notxpcom] readonly attribute TimeStamp cacheReadStart;
+  [notxpcom] readonly attribute TimeStamp cacheReadEnd;
+
+  // All following are PRTime versions of the above.
+  readonly attribute PRTime channelCreationTime;
+  readonly attribute PRTime asyncOpenTime;
+  readonly attribute PRTime domainLookupStartTime;
+  readonly attribute PRTime domainLookupEndTime;
+  readonly attribute PRTime connectStartTime;
+  readonly attribute PRTime connectEndTime;
+  readonly attribute PRTime requestStartTime;
+  readonly attribute PRTime responseStartTime;
+  readonly attribute PRTime responseEndTime;
+  readonly attribute PRTime cacheReadStartTime;
+  readonly attribute PRTime cacheReadEndTime;
+};
--- a/netwerk/base/src/nsSocketTransport2.cpp
+++ b/netwerk/base/src/nsSocketTransport2.cpp
@@ -938,24 +938,22 @@ nsSocketTransport::ResolveHost()
     if (NS_FAILED(rv)) return rv;
 
     mResolving = PR_TRUE;
 
     PRUint32 dnsFlags = 0;
     if (mConnectionFlags & nsSocketTransport::BYPASS_CACHE)
         dnsFlags = nsIDNSService::RESOLVE_BYPASS_CACHE;
 
+    SendStatus(STATUS_RESOLVING);
     rv = dns->AsyncResolve(SocketHost(), dnsFlags, this, nsnull,
                            getter_AddRefs(mDNSRequest));
     if (NS_SUCCEEDED(rv)) {
         SOCKET_LOG(("  advancing to STATE_RESOLVING\n"));
         mState = STATE_RESOLVING;
-        // only report that we are resolving if we are still resolving...
-        if (mResolving)
-            SendStatus(STATUS_RESOLVING);
     }
     return rv;
 }
 
 nsresult
 nsSocketTransport::BuildSocket(PRFileDesc *&fd, PRBool &proxyTransparent, PRBool &usingSSL)
 {
     SOCKET_LOG(("nsSocketTransport::BuildSocket [this=%x]\n", this));
@@ -1434,16 +1432,19 @@ nsSocketTransport::OnSocketEvent(PRUint3
         //
         if (mState == STATE_CLOSED)
             mCondition = ResolveHost();
         else
             SOCKET_LOG(("  ignoring redundant event\n"));
         break;
 
     case MSG_DNS_LOOKUP_COMPLETE:
+        if (mDNSRequest)  // only send this if we actually resolved anything
+            SendStatus(STATUS_RESOLVED);
+
         SOCKET_LOG(("  MSG_DNS_LOOKUP_COMPLETE\n"));
         mDNSRequest = 0;
         if (param) {
             mDNSRecord = static_cast<nsIDNSRecord *>(param);
             mDNSRecord->GetNextAddr(SocketPort(), &mNetAddr);
         }
         // status contains DNS lookup status
         if (NS_FAILED(status)) {
--- a/netwerk/locales/en-US/necko.properties
+++ b/netwerk/locales/en-US/necko.properties
@@ -43,16 +43,17 @@
 3=Looking up %1$S…
 4=Connected to %1$S…
 5=Sending request to %1$S…
 6=Transferring data from %1$S…
 7=Connecting to %1$S…
 8=Read %1$S 
 9=Wrote %1$S
 10=Waiting for %1$S…
+11=Looked up %1$S…
 
 27=Beginning FTP transaction…
 28=Finished FTP transaction
 
 UnsupportedFTPServer=The FTP server %1$S is currently unsupported.
 RepostFormData=This web page is being redirected to a new location. Would you like to resend the form data you have typed to the new location?
 
 # Directory listing strings
--- a/netwerk/protocol/ftp/nsFtpConnectionThread.cpp
+++ b/netwerk/protocol/ftp/nsFtpConnectionThread.cpp
@@ -2010,16 +2010,17 @@ nsFtpState::OnTransportStatus(nsITranspo
                               PRUint64 progress, PRUint64 progressMax)
 {
     // Mix signals from both the control and data connections.
 
     // Ignore data transfer events on the control connection.
     if (mControlConnection && transport == mControlConnection->Transport()) {
         switch (status) {
         case NS_NET_STATUS_RESOLVING_HOST:
+        case NS_NET_STATUS_RESOLVED_HOST:
         case NS_NET_STATUS_CONNECTING_TO:
         case NS_NET_STATUS_CONNECTED_TO:
             break;
         default:
             return NS_OK;
         }
     }
 
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -43,16 +43,17 @@
 #include "mozilla/net/HttpBaseChannel.h"
 
 #include "nsHttpHandler.h"
 #include "nsMimeTypes.h"
 #include "nsNetUtil.h"
 
 #include "nsICachingChannel.h"
 #include "nsISeekableStream.h"
+#include "nsITimedChannel.h"
 #include "nsIEncodedChannel.h"
 #include "nsIResumableChannel.h"
 #include "nsIApplicationCacheChannel.h"
 #include "nsEscape.h"
 #include "nsPrintfCString.h"
 
 #include "prnetdb.h"
 
@@ -73,16 +74,17 @@ HttpBaseChannel::HttpBaseChannel()
   , mResponseHeadersModified(PR_FALSE)
   , mAllowPipelining(PR_TRUE)
   , mForceAllowThirdPartyCookie(PR_FALSE)
   , mUploadStreamHasHeaders(PR_FALSE)
   , mInheritApplicationCache(PR_TRUE)
   , mChooseApplicationCache(PR_FALSE)
   , mLoadedFromApplicationCache(PR_FALSE)
   , mChannelIsForDownload(PR_FALSE)
+  , mTimingEnabled(PR_FALSE)
   , mRedirectedCachekeys(nsnull)
 {
   LOG(("Creating HttpBaseChannel @%x\n", this));
 
   // grab a reference to the handler to ensure that it doesn't go away.
   NS_ADDREF(gHttpHandler);
 }
 
@@ -1485,16 +1487,21 @@ HttpBaseChannel::SetupReplacementChannel
     // We purposely avoid transfering mChooseApplicationCache.
   }
 
   // transfer any properties
   nsCOMPtr<nsIWritablePropertyBag> bag(do_QueryInterface(newChannel));
   if (bag)
     mPropertyHash.EnumerateRead(CopyProperties, bag.get());
 
+  // transfer timed channel enabled status
+  nsCOMPtr<nsITimedChannel> timed(do_QueryInterface(newChannel));
+  if (timed)
+    timed->SetTimingEnabled(mTimingEnabled);
+
   return NS_OK;
 }
 
 //------------------------------------------------------------------------------
 
 }  // namespace net
 }  // namespace mozilla
 
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -269,16 +269,18 @@ protected:
   PRUint32                          mResponseHeadersModified    : 1;
   PRUint32                          mAllowPipelining            : 1;
   PRUint32                          mForceAllowThirdPartyCookie : 1;
   PRUint32                          mUploadStreamHasHeaders     : 1;
   PRUint32                          mInheritApplicationCache    : 1;
   PRUint32                          mChooseApplicationCache     : 1;
   PRUint32                          mLoadedFromApplicationCache : 1;
   PRUint32                          mChannelIsForDownload       : 1;
+  // True if timing collection is enabled
+  PRUint32                          mTimingEnabled : 1;
 
   nsTArray<nsCString>              *mRedirectedCachekeys;
 };
 
 
 } // namespace net
 } // namespace mozilla
 
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/TimingStruct.h
@@ -0,0 +1,57 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla networking code.
+ *
+ * The Initial Developer of the Original Code is
+ * Google Inc.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Christian Biesinger <cbiesinger@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+
+#ifndef TimingStruct_h_
+#define TimingStruct_h_
+
+#include "mozilla/TimeStamp.h"
+
+struct TimingStruct {
+  mozilla::TimeStamp channelCreation;
+  mozilla::TimeStamp asyncOpen;
+
+  mozilla::TimeStamp domainLookupStart;
+  mozilla::TimeStamp domainLookupEnd;
+  mozilla::TimeStamp connectStart;
+  mozilla::TimeStamp connectEnd;
+  mozilla::TimeStamp requestStart;
+  mozilla::TimeStamp responseStart;
+  mozilla::TimeStamp responseEnd;
+};
+
+#endif
--- a/netwerk/protocol/http/nsHttp.h
+++ b/netwerk/protocol/http/nsHttp.h
@@ -118,16 +118,19 @@ typedef PRUint8 nsHttpVersion;
 // a transaction with this caps flag will, upon opening a new connection,
 // bypass the local DNS cache
 #define NS_HTTP_REFRESH_DNS          (1<<3)
 
 // a transaction with this caps flag will not pass SSL client-certificates
 // to the server (see bug #466080), but is may also be used for other things
 #define NS_HTTP_LOAD_ANONYMOUS       (1<<4)
 
+// a transaction with this caps flag keeps timing information
+#define NS_HTTP_TIMING_ENABLED       (1<<5)
+
 //-----------------------------------------------------------------------------
 // some default values
 //-----------------------------------------------------------------------------
 
 // hard upper limit on the number of requests that can be pipelined
 #define NS_HTTP_MAX_PIPELINED_REQUESTS 8 
 
 #define NS_HTTP_DEFAULT_PORT  80
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -40,17 +40,19 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
+#ifdef MOZ_IPC
 #include "base/basictypes.h"
+#endif 
 
 #include "nsHttpChannel.h"
 #include "nsHttpHandler.h"
 #include "nsIApplicationCacheService.h"
 #include "nsIApplicationCacheContainer.h"
 #include "nsIAuthInformation.h"
 #include "nsIStringBundle.h"
 #include "nsIIDNService.h"
@@ -63,16 +65,17 @@
 #include "prnetdb.h"
 #include "nsEscape.h"
 #include "nsStreamUtils.h"
 #include "nsIOService.h"
 #include "nsICacheService.h"
 #include "nsDNSPrefetch.h"
 #include "nsChannelClassifier.h"
 #include "nsIRedirectResultListener.h"
+#include "mozilla/TimeStamp.h"
 
 // True if the local cache should be bypassed when processing a request.
 #define BYPASS_LOCAL_CACHE(loadFlags) \
         (loadFlags & (nsIRequest::LOAD_BYPASS_CACHE | \
                       nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE))
 
 static NS_DEFINE_CID(kStreamListenerTeeCID, NS_STREAMLISTENERTEE_CID);
 
@@ -129,16 +132,18 @@ nsHttpChannel::nsHttpChannel()
     , mFallbackChannel(PR_FALSE)
     , mTracingEnabled(PR_TRUE)
     , mCustomConditionalRequest(PR_FALSE)
     , mFallingBack(PR_FALSE)
     , mWaitingForRedirectCallback(PR_FALSE)
     , mRequestTimeInitialized(PR_FALSE)
 {
     LOG(("Creating nsHttpChannel [this=%p]\n", this));
+    mChannelCreationTime = PR_Now();
+    mChannelCreationTimestamp = mozilla::TimeStamp::Now();
     // Subfields of unions cannot be targeted in an initializer list
     mSelfAddr.raw.family = PR_AF_UNSPEC;
     mPeerAddr.raw.family = PR_AF_UNSPEC;
 }
 
 nsHttpChannel::~nsHttpChannel()
 {
     LOG(("Destroying nsHttpChannel [this=%p]\n", this));
@@ -658,16 +663,19 @@ nsHttpChannel::SetupTransaction()
     mTransaction = new nsHttpTransaction();
     if (!mTransaction)
         return NS_ERROR_OUT_OF_MEMORY;
 
     // See bug #466080. Transfer LOAD_ANONYMOUS flag to socket-layer.
     if (mLoadFlags & LOAD_ANONYMOUS)
         mCaps |= NS_HTTP_LOAD_ANONYMOUS;
 
+    if (mTimingEnabled)
+        mCaps |= NS_HTTP_TIMING_ENABLED;
+
     mConnectionInfo->SetAnonymous((mLoadFlags & LOAD_ANONYMOUS) != 0);
 
     if (mUpgradeProtocolCallback) {
         mRequestHead.SetHeader(nsHttp::Upgrade, mUpgradeProtocol, PR_FALSE);
         mRequestHead.SetHeader(nsHttp::Connection,
                                nsDependentCString(nsHttp::Upgrade.get()),
                                PR_TRUE);
         mCaps |=  NS_HTTP_STICKY_CONNECTION;
@@ -2852,16 +2860,19 @@ nsHttpChannel::ReadFromCache()
     rv = nsInputStreamPump::Create(getter_AddRefs(mCachePump),
                                    stream, PRInt64(-1), PRInt64(-1), 0, 0,
                                    PR_TRUE);
     if (NS_FAILED(rv)) return rv;
 
     rv = mCachePump->AsyncRead(this, mListenerContext);
     if (NS_FAILED(rv)) return rv;
 
+    if (mTimingEnabled)
+        mCacheReadStart = mozilla::TimeStamp::Now();
+
     PRUint32 suspendCount = mSuspendCount;
     while (suspendCount--)
         mCachePump->Suspend();
 
     return NS_OK;
 }
 
 void
@@ -3540,16 +3551,17 @@ NS_INTERFACE_MAP_BEGIN(nsHttpChannel)
     NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
     NS_INTERFACE_MAP_ENTRY(nsIProtocolProxyCallback)
     NS_INTERFACE_MAP_ENTRY(nsIProxiedChannel)
     NS_INTERFACE_MAP_ENTRY(nsIHttpAuthenticableChannel)
     NS_INTERFACE_MAP_ENTRY(nsITraceableChannel)
     NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheContainer)
     NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheChannel)
     NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback)
+    NS_INTERFACE_MAP_ENTRY(nsITimedChannel)
 NS_INTERFACE_MAP_END_INHERITING(HttpBaseChannel)
 
 //-----------------------------------------------------------------------------
 // nsHttpChannel::nsIRequest
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 nsHttpChannel::Cancel(nsresult status)
@@ -3637,16 +3649,19 @@ nsHttpChannel::AsyncOpen(nsIStreamListen
     NS_ENSURE_TRUE(!mIsPending, NS_ERROR_IN_PROGRESS);
     NS_ENSURE_TRUE(!mWasOpened, NS_ERROR_ALREADY_OPENED);
 
     nsresult rv;
 
     if (mCanceled)
         return mStatus;
 
+    if (mTimingEnabled)
+        mAsyncOpenTime = mozilla::TimeStamp::Now();
+
     rv = NS_CheckPortSafety(mURI);
     if (NS_FAILED(rv))
         return rv;
 
     if (!(mConnectionInfo && mConnectionInfo->UsingHttpProxy())) {
         // Start a DNS lookup very early in case the real open is queued the DNS can 
         // happen in parallel. Do not do so in the presence of an HTTP proxy as 
         // all lookups other than for the proxy itself are done by the proxy.
@@ -3781,16 +3796,147 @@ nsHttpChannel::GetProxyInfo(nsIProxyInfo
     else {
         *result = mConnectionInfo->ProxyInfo();
         NS_IF_ADDREF(*result);
     }
     return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
+// nsHttpChannel::nsITimedChannel
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+nsHttpChannel::SetTimingEnabled(PRBool enabled) {
+    mTimingEnabled = enabled;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHttpChannel::GetTimingEnabled(PRBool* _retval) {
+    *_retval = mTimingEnabled;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHttpChannel::GetChannelCreation(mozilla::TimeStamp* _retval) {
+    *_retval = mChannelCreationTimestamp;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHttpChannel::GetAsyncOpen(mozilla::TimeStamp* _retval) {
+    *_retval = mAsyncOpenTime;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHttpChannel::GetDomainLookupStart(mozilla::TimeStamp* _retval) {
+    if (mTransaction)
+        *_retval = mTransaction->Timings().domainLookupStart;
+    else
+        *_retval = mTransactionTimings.domainLookupStart;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHttpChannel::GetDomainLookupEnd(mozilla::TimeStamp* _retval) {
+    if (mTransaction)
+        *_retval = mTransaction->Timings().domainLookupEnd;
+    else
+        *_retval = mTransactionTimings.domainLookupEnd;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHttpChannel::GetConnectStart(mozilla::TimeStamp* _retval) {
+    if (mTransaction)
+        *_retval = mTransaction->Timings().connectStart;
+    else
+        *_retval = mTransactionTimings.connectStart;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHttpChannel::GetConnectEnd(mozilla::TimeStamp* _retval) {
+    if (mTransaction)
+        *_retval = mTransaction->Timings().connectEnd;
+    else
+        *_retval = mTransactionTimings.connectEnd;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHttpChannel::GetRequestStart(mozilla::TimeStamp* _retval) {
+    if (mTransaction)
+        *_retval = mTransaction->Timings().requestStart;
+    else
+        *_retval = mTransactionTimings.requestStart;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHttpChannel::GetResponseStart(mozilla::TimeStamp* _retval) {
+    if (mTransaction)
+        *_retval = mTransaction->Timings().responseStart;
+    else
+        *_retval = mTransactionTimings.responseStart;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHttpChannel::GetResponseEnd(mozilla::TimeStamp* _retval) {
+    if (mTransaction)
+        *_retval = mTransaction->Timings().responseEnd;
+    else
+        *_retval = mTransactionTimings.responseEnd;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHttpChannel::GetCacheReadStart(mozilla::TimeStamp* _retval) {
+    *_retval = mCacheReadStart;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsHttpChannel::GetCacheReadEnd(mozilla::TimeStamp* _retval) {
+    *_retval = mCacheReadEnd;
+    return NS_OK;
+}
+
+#define IMPL_TIMING_ATTR(name)                                 \
+NS_IMETHODIMP                                                  \
+nsHttpChannel::Get##name##Time(PRTime* _retval) {              \
+    mozilla::TimeStamp stamp;                                  \
+    Get##name(&stamp);                                         \
+    if (stamp.IsNull()) {                                      \
+        *_retval = 0;                                          \
+        return NS_OK;                                          \
+    }                                                          \
+    *_retval = mChannelCreationTime +                          \
+        (stamp - mChannelCreationTimestamp).ToSeconds() * 1e6; \
+    return NS_OK;                                              \
+}
+
+IMPL_TIMING_ATTR(ChannelCreation)
+IMPL_TIMING_ATTR(AsyncOpen)
+IMPL_TIMING_ATTR(DomainLookupStart)
+IMPL_TIMING_ATTR(DomainLookupEnd)
+IMPL_TIMING_ATTR(ConnectStart)
+IMPL_TIMING_ATTR(ConnectEnd)
+IMPL_TIMING_ATTR(RequestStart)
+IMPL_TIMING_ATTR(ResponseStart)
+IMPL_TIMING_ATTR(ResponseEnd)
+IMPL_TIMING_ATTR(CacheReadStart)
+IMPL_TIMING_ATTR(CacheReadEnd)
+
+#undef IMPL_TIMING_ATTR
+
+//-----------------------------------------------------------------------------
 // nsHttpChannel::nsIHttpAuthenticableChannel
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 nsHttpChannel::GetIsSSL(PRBool *aIsSSL)
 {
     *aIsSSL = mConnectionInfo->UsingSSL();
     return NS_OK;
@@ -3976,16 +4122,20 @@ nsHttpChannel::ContinueOnStartRequest3(n
 }
 
 NS_IMETHODIMP
 nsHttpChannel::OnStopRequest(nsIRequest *request, nsISupports *ctxt, nsresult status)
 {
     LOG(("nsHttpChannel::OnStopRequest [this=%p request=%p status=%x]\n",
         this, request, status));
 
+    if (mTimingEnabled && request == mCachePump) {
+        mCacheReadEnd = mozilla::TimeStamp::Now();
+    }
+
      // allow content to be cached if it was loaded successfully (bug #482935)
      PRBool contentComplete = NS_SUCCEEDED(status);
 
     // honor the cancelation status even if the underlying transaction completed.
     if (mCanceled || NS_FAILED(mStatus))
         status = mStatus;
 
     if (mCachedContentIsPartial) {
@@ -4032,16 +4182,17 @@ nsHttpChannel::OnStopRequest(nsIRequest 
                 conn = nsnull;
         }
 
         nsRefPtr<nsAHttpConnection> stickyConn;
         if (mCaps & NS_HTTP_STICKY_CONNECTION)
             stickyConn = mTransaction->Connection();
         
         // at this point, we're done with the transaction
+        mTransactionTimings = mTransaction->Timings();
         mTransaction = nsnull;
         mTransactionPump = 0;
 
         // handle auth retry...
         if (authRetry) {
             mAuthRetryPending = PR_FALSE;
             status = DoAuthRetry(conn);
             if (NS_SUCCEEDED(status))
--- a/netwerk/protocol/http/nsHttpChannel.h
+++ b/netwerk/protocol/http/nsHttpChannel.h
@@ -59,16 +59,18 @@
 #include "nsIResumableChannel.h"
 #include "nsIProtocolProxyCallback.h"
 #include "nsICancelable.h"
 #include "nsIHttpAuthenticableChannel.h"
 #include "nsITraceableChannel.h"
 #include "nsIHttpChannelAuthProvider.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
 #include "nsICryptoHash.h"
+#include "nsITimedChannel.h"
+#include "TimingStruct.h"
 
 class nsAHttpConnection;
 class AutoRedirectVetoNotifier;
 
 using namespace mozilla::net;
 
 //-----------------------------------------------------------------------------
 // nsHttpChannel
@@ -79,31 +81,33 @@ class nsHttpChannel : public HttpBaseCha
                     , public nsICachingChannel
                     , public nsICacheListener
                     , public nsITransportEventSink
                     , public nsIProtocolProxyCallback
                     , public nsIHttpAuthenticableChannel
                     , public nsITraceableChannel
                     , public nsIApplicationCacheChannel
                     , public nsIAsyncVerifyRedirectCallback
+                    , public nsITimedChannel
 {
 public:
     NS_DECL_ISUPPORTS_INHERITED
     NS_DECL_NSIREQUESTOBSERVER
     NS_DECL_NSISTREAMLISTENER
     NS_DECL_NSICACHEINFOCHANNEL
     NS_DECL_NSICACHINGCHANNEL
     NS_DECL_NSICACHELISTENER
     NS_DECL_NSITRANSPORTEVENTSINK
     NS_DECL_NSIPROTOCOLPROXYCALLBACK
     NS_DECL_NSIPROXIEDCHANNEL
     NS_DECL_NSITRACEABLECHANNEL
     NS_DECL_NSIAPPLICATIONCACHECONTAINER
     NS_DECL_NSIAPPLICATIONCACHECHANNEL
     NS_DECL_NSIASYNCVERIFYREDIRECTCALLBACK
+    NS_DECL_NSITIMEDCHANNEL
 
     // nsIHttpAuthenticableChannel. We can't use
     // NS_DECL_NSIHTTPAUTHENTICABLECHANNEL because it duplicates cancel() and
     // others.
     NS_IMETHOD GetIsSSL(PRBool *aIsSSL);
     NS_IMETHOD GetProxyMethodIsConnect(PRBool *aProxyMethodIsConnect);
     NS_IMETHOD GetServerResponseHeader(nsACString & aServerResponseHeader);
     NS_IMETHOD GetProxyChallenges(nsACString & aChallenges);
@@ -346,14 +350,23 @@ private:
     // True if mRequestTime has been set. In such a case it is safe to update
     // the cache entry's expiration time. Otherwise, it is not(see bug 567360).
     PRUint32                          mRequestTimeInitialized : 1;
 
     nsTArray<nsContinueRedirectionFunc> mRedirectFuncStack;
 
     nsCOMPtr<nsICryptoHash>        mHasher;
 
+    PRTime                            mChannelCreationTime;
+    mozilla::TimeStamp                mChannelCreationTimestamp;
+    mozilla::TimeStamp                mAsyncOpenTime;
+    mozilla::TimeStamp                mCacheReadStart;
+    mozilla::TimeStamp                mCacheReadEnd;
+    // copied from the transaction before we null out mTransaction
+    // so that the timing can still be queried from OnStopRequest
+    TimingStruct                      mTransactionTimings;
+
     nsresult WaitForRedirectCallback();
     void PushRedirectAsyncFunc(nsContinueRedirectionFunc func);
     void PopRedirectAsyncFunc(nsContinueRedirectionFunc func);
 };
 
 #endif // nsHttpChannel_h__
--- a/netwerk/protocol/http/nsHttpTransaction.cpp
+++ b/netwerk/protocol/http/nsHttpTransaction.cpp
@@ -355,16 +355,28 @@ nsHttpTransaction::GetSecurityCallbacks(
 
 void
 nsHttpTransaction::OnTransportStatus(nsITransport* transport,
                                      nsresult status, PRUint64 progress)
 {
     LOG(("nsHttpTransaction::OnSocketStatus [this=%x status=%x progress=%llu]\n",
         this, status, progress));
 
+    if (TimingEnabled()) {
+        if (status == nsISocketTransport::STATUS_RESOLVING) {
+            mTimings.domainLookupStart = mozilla::TimeStamp::Now();
+        } else if (status == nsISocketTransport::STATUS_RESOLVED) {
+            mTimings.domainLookupEnd = mozilla::TimeStamp::Now();
+        } else if (status == nsISocketTransport::STATUS_CONNECTING_TO) {
+            mTimings.connectStart = mozilla::TimeStamp::Now();
+        } else if (status == nsISocketTransport::STATUS_CONNECTED_TO) {
+            mTimings.connectEnd = mozilla::TimeStamp::Now();
+        }
+    }
+
     if (!mTransportSink)
         return;
 
     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
 
     // Need to do this before the STATUS_RECEIVING_FROM check below, to make
     // sure that the activity distributor gets told about all status events.
     if (mActivityDistributor) {
@@ -445,16 +457,20 @@ nsHttpTransaction::ReadRequestSegment(ns
                                       PRUint32 offset,
                                       PRUint32 count,
                                       PRUint32 *countRead)
 {
     nsHttpTransaction *trans = (nsHttpTransaction *) closure;
     nsresult rv = trans->mReader->OnReadSegment(buf, count, countRead);
     if (NS_FAILED(rv)) return rv;
 
+    if (trans->TimingEnabled() && trans->mTimings.requestStart.IsNull()) {
+        // First data we're sending -> this is requestStart
+        trans->mTimings.requestStart = mozilla::TimeStamp::Now();
+    }
     trans->mSentData = PR_TRUE;
     return NS_OK;
 }
 
 nsresult
 nsHttpTransaction::ReadSegments(nsAHttpSegmentReader *reader,
                                 PRUint32 count, PRUint32 *countRead)
 {
@@ -504,16 +520,20 @@ nsHttpTransaction::WritePipeSegment(nsIO
                                     PRUint32 count,
                                     PRUint32 *countWritten)
 {
     nsHttpTransaction *trans = (nsHttpTransaction *) closure;
 
     if (trans->mTransactionDone)
         return NS_BASE_STREAM_CLOSED; // stop iterating
 
+    if (trans->TimingEnabled() && trans->mTimings.responseStart.IsNull()) {
+        trans->mTimings.responseStart = mozilla::TimeStamp::Now();
+    }
+
     nsresult rv;
     //
     // OK, now let the caller fill this segment with data.
     //
     rv = trans->mWriter->OnWriteSegment(buf, count, countWritten);
     if (NS_FAILED(rv)) return rv; // caller didn't want to write anything
 
     NS_ASSERTION(*countWritten > 0, "bad writer");
@@ -566,16 +586,18 @@ nsHttpTransaction::Close(nsresult reason
 
     NS_ASSERTION(PR_GetCurrentThread() == gSocketThread, "wrong thread");
 
     if (mClosed) {
         LOG(("  already closed\n"));
         return;
     }
 
+    mTimings.responseEnd = mozilla::TimeStamp::Now();
+
     if (mActivityDistributor) {
         // report the reponse is complete if not already reported
         if (!mResponseIsComplete)
             mActivityDistributor->ObserveActivity(
                 mChannel,
                 NS_HTTP_ACTIVITY_TYPE_HTTP_TRANSACTION,
                 NS_HTTP_ACTIVITY_SUBTYPE_RESPONSE_COMPLETE,
                 PR_Now(),
--- a/netwerk/protocol/http/nsHttpTransaction.h
+++ b/netwerk/protocol/http/nsHttpTransaction.h
@@ -47,16 +47,17 @@
 
 #include "nsIPipe.h"
 #include "nsIInputStream.h"
 #include "nsIOutputStream.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsISocketTransportService.h"
 #include "nsITransport.h"
 #include "nsIEventTarget.h"
+#include "TimingStruct.h"
 
 //-----------------------------------------------------------------------------
 
 class nsHttpTransaction;
 class nsHttpRequestHead;
 class nsHttpResponseHead;
 class nsHttpChunkedDecoder;
 class nsIHttpActivityObserver;
@@ -129,16 +130,18 @@ public:
     PRBool ResponseIsComplete() { return mResponseIsComplete; }
 
     PRBool    SSLConnectFailed() { return mSSLConnectFailed; }
 
     // These methods may only be used by the connection manager.
     void    SetPriority(PRInt32 priority) { mPriority = priority; }
     PRInt32    Priority()                 { return mPriority; }
 
+    const TimingStruct& Timings() const { return mTimings; }
+
 private:
     nsresult Restart();
     char    *LocateHttpStart(char *buf, PRUint32 len,
                              PRBool aAllowPartialMatch);
     void     ParseLine(char *line);
     nsresult ParseLineSegment(char *seg, PRUint32 len);
     nsresult ParseHead(char *, PRUint32 count, PRUint32 *countRead);
     nsresult HandleContentStart();
@@ -146,16 +149,18 @@ private:
     nsresult ProcessData(char *, PRUint32, PRUint32 *);
     void     DeleteSelfOnConsumerThread();
 
     static NS_METHOD ReadRequestSegment(nsIInputStream *, void *, const char *,
                                         PRUint32, PRUint32, PRUint32 *);
     static NS_METHOD WritePipeSegment(nsIOutputStream *, void *, char *,
                                       PRUint32, PRUint32, PRUint32 *);
 
+    PRBool TimingEnabled() const { return mCaps & NS_HTTP_TIMING_ENABLED; }
+
 private:
     nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
     nsCOMPtr<nsITransportEventSink> mTransportSink;
     nsCOMPtr<nsIEventTarget>        mConsumerTarget;
     nsCOMPtr<nsISupports>           mSecurityInfo;
     nsCOMPtr<nsIAsyncInputStream>   mPipeIn;
     nsCOMPtr<nsIAsyncOutputStream>  mPipeOut;
 
@@ -183,16 +188,18 @@ private:
     // up to MAX_INVALID_RESPONSE_BODY_SZ bytes when looking for the next
     // response header to deal with servers that actually sent a response
     // body where they should not have. This member tracks how many bytes have
     // so far been skipped.
     PRUint32                        mInvalidResponseBytesRead;
 
     nsHttpChunkedDecoder           *mChunkedDecoder;
 
+    TimingStruct                    mTimings;
+
     nsresult                        mStatus;
 
     PRInt16                         mPriority;
 
     PRUint16                        mRestartCount;        // the number of times this transaction has been restarted
     PRUint8                         mCaps;
 
     // state flags, all logically boolean, but not packed together into a
--- a/netwerk/test/TestProtocols.cpp
+++ b/netwerk/test/TestProtocols.cpp
@@ -74,16 +74,17 @@
 #include "nsIInterfaceRequestor.h" 
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIDNSService.h" 
 #include "nsIAuthPrompt.h"
 #include "nsIPrefService.h"
 #include "nsIPrefBranch.h"
 #include "nsIPropertyBag2.h"
 #include "nsIWritablePropertyBag2.h"
+#include "nsITimedChannel.h"
 #include "nsChannelProperties.h"
 
 #include "nsISimpleEnumerator.h"
 #include "nsStringAPI.h"
 #include "nsNetUtil.h"
 #include "prlog.h"
 #include "prtime.h"
 
@@ -150,16 +151,49 @@ SetPACFile(const char* pacURL)
     prefs->SetCharPref("network.proxy.autoconfig_url", pacURL);
     prefs->SetIntPref("network.proxy.type", 2); // PAC file
   }
   LOG(("connecting using PAC file %s\n", pacURL));
   return NS_OK;
 }
 
 //-----------------------------------------------------------------------------
+// Timing information
+//-----------------------------------------------------------------------------
+
+void PrintTimingInformation(nsITimedChannel* channel) {
+#define PRINT_VALUE(property)                                              \
+    {                                                                      \
+        PRTime value;                                                      \
+        channel->Get##property(&value);                                    \
+        if (value) {                                                       \
+          PRExplodedTime exploded;                                         \
+          PR_ExplodeTime(value, PR_LocalTimeParameters, &exploded);        \
+          char buf[256];                                                   \
+          PR_FormatTime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &exploded); \
+          LOG(("  " #property ":\t%s (%i usec)", buf, exploded.tm_usec));  \
+        } else {                                                           \
+          LOG(("  " #property ":\t0"));                                    \
+        }                                                                  \
+    }
+    LOG(("Timing data:"));
+    PRINT_VALUE(ChannelCreationTime)
+    PRINT_VALUE(AsyncOpenTime)
+    PRINT_VALUE(DomainLookupStartTime)
+    PRINT_VALUE(DomainLookupEndTime)
+    PRINT_VALUE(ConnectStartTime)
+    PRINT_VALUE(ConnectEndTime)
+    PRINT_VALUE(RequestStartTime)
+    PRINT_VALUE(ResponseStartTime)
+    PRINT_VALUE(ResponseEndTime)
+    PRINT_VALUE(CacheReadStartTime)
+    PRINT_VALUE(CacheReadEndTime)
+}
+
+//-----------------------------------------------------------------------------
 // HeaderVisitor
 //-----------------------------------------------------------------------------
 
 class HeaderVisitor : public nsIHttpHeaderVisitor
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIHTTPHEADERVISITOR
@@ -540,16 +574,20 @@ InputTestConsumer::OnStopRequest(nsIRequ
     LOG(("\tTime to read: %.3f seconds.\n", readTime));
     LOG(("\tRead: %lld bytes.\n", info->mBytesRead));
     if (info->mBytesRead == PRInt64(0)) {
     } else if (readTime > 0.0) {
       LOG(("\tThroughput: %.0f bps.\n", (PRFloat64)(info->mBytesRead*PRInt64(8))/readTime));
     } else {
       LOG(("\tThroughput: REAL FAST!!\n"));
     }
+
+    nsCOMPtr<nsITimedChannel> timed(do_QueryInterface(request));
+    if (timed)
+        PrintTimingInformation(timed);
   } else {
     LOG(("\nFinished loading: UNKNOWN URL. Status Code: %x\n", aStatus));
   }
 
   if (--gKeepRunning == 0)
     QuitPumpingEvents();
   return NS_OK;
 }
@@ -626,16 +664,20 @@ nsresult StartLoadingURL(const char* aUr
                            nsnull,     // loadGroup
                            callbacks); // notificationCallbacks
         NS_RELEASE(callbacks);
         if (NS_FAILED(rv)) {
             LOG(("ERROR: NS_OpenURI failed for %s [rv=%x]\n", aUrlString, rv));
             return rv;
         }
 
+        nsCOMPtr<nsITimedChannel> timed(do_QueryInterface(pChannel));
+        if (timed)
+            timed->SetTimingEnabled(PR_TRUE);
+
         nsCOMPtr<nsIWritablePropertyBag2> props = do_QueryInterface(pChannel);
         if (props) {
             rv = props->SetPropertyAsInterface(NS_LITERAL_STRING("test.foo"),
                                                pURL);
             if (NS_SUCCEEDED(rv))
                 LOG(("set prop 'test.foo'\n"));
         }
 
--- a/toolkit/components/console/hudservice/HUDService.jsm
+++ b/toolkit/components/console/hudservice/HUDService.jsm
@@ -2508,16 +2508,17 @@ HUD_SERVICE.prototype =
         0x5001: "REQUEST_HEADER",
         0x5002: "REQUEST_BODY_SENT",
         0x5003: "RESPONSE_START",
         0x5004: "RESPONSE_HEADER",
         0x5005: "RESPONSE_COMPLETE",
         0x5006: "TRANSACTION_CLOSE",
 
         0x804b0003: "STATUS_RESOLVING",
+        0x804b000b: "STATUS_RESOLVED",
         0x804b0007: "STATUS_CONNECTING_TO",
         0x804b0004: "STATUS_CONNECTED_TO",
         0x804b0005: "STATUS_SENDING_TO",
         0x804b000a: "STATUS_WAITING_FOR",
         0x804b0006: "STATUS_RECEIVING_FROM"
       }
     };