bug 570341 - Initial implementation of web timing specification
authorIgor Bazarny <igor.bazarny@gmail.com>
Mon, 23 May 2011 14:19:36 -0700
changeset 70155 1a645ea075fc9fba940b2c03baef039834ed7e07
parent 70154 85d824b348302ab2bfa153aef4586378a815bc56
child 70156 f7fc3beccae7c00ddf8500fede6c64259669284c
child 70160 c5995eec40f3314b4cb61ce65a5636ca2355cde5
push idunknown
push userunknown
push dateunknown
bugs570341
milestone6.0a1
bug 570341 - Initial implementation of web timing specification r=smaug sr=biesi
content/base/public/nsIDocument.h
content/base/src/Makefile.in
content/base/src/nsDocument.cpp
content/base/src/nsDocument.h
content/html/content/src/Makefile.in
content/html/document/src/Makefile.in
content/svg/document/src/Makefile.in
content/xbl/src/Makefile.in
content/xml/document/src/Makefile.in
content/xul/content/src/Makefile.in
content/xul/document/src/Makefile.in
docshell/base/Makefile.in
docshell/base/nsDocShell.cpp
docshell/base/nsDocShell.h
docshell/test/Makefile.in
docshell/test/bug570341_recordevents.html
docshell/test/test_bug570341.html
dom/base/Makefile.in
dom/base/nsDOMClassInfo.cpp
dom/base/nsDOMClassInfoClasses.h
dom/base/nsDOMNavigationTiming.cpp
dom/base/nsDOMNavigationTiming.h
dom/base/nsGlobalWindow.cpp
dom/base/nsGlobalWindow.h
dom/base/nsPerformance.cpp
dom/base/nsPerformance.h
dom/interfaces/base/Makefile.in
dom/interfaces/base/domstubs.idl
dom/interfaces/base/nsIDOMPerformance.idl
dom/interfaces/base/nsIDOMPerformanceNavigation.idl
dom/interfaces/base/nsIDOMPerformanceTiming.idl
dom/interfaces/base/nsIDOMWindowInternal.idl
js/src/xpconnect/src/Makefile.in
layout/base/Makefile.in
layout/base/nsDocumentViewer.cpp
layout/base/nsIDocumentViewer.h
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -105,32 +105,33 @@ class nsIDocumentObserver;
 class nsBindingManager;
 class nsIDOMNodeList;
 class mozAutoSubtreeModified;
 struct JSObject;
 class nsFrameLoader;
 class nsIBoxObject;
 class imgIRequest;
 class nsISHEntry;
+class nsDOMNavigationTiming;
 
 namespace mozilla {
 namespace css {
 class Loader;
 } // namespace css
 
 namespace dom {
 class Link;
 class Element;
 } // namespace dom
 } // namespace mozilla
 
 
 #define NS_IDOCUMENT_IID      \
-{ 0x26ef6218, 0xcd5e, 0x4953,  \
- { 0xbb, 0x57, 0xb8, 0x50, 0x29, 0xa1, 0xae, 0x40 } }
+{ 0x2c6ad63f, 0xb7b9, 0x42f8, \
+ { 0xbd, 0xde, 0x76, 0x0a, 0x83, 0xe3, 0xb0, 0x49 } }
 
 // Flag for AddStyleSheet().
 #define NS_STYLESHEET_FROM_CATALOG                (1 << 0)
 
 // Document states
 
 // RTL locale: specific to the XUL localedir attribute
 #define NS_DOCUMENT_STATE_RTL_LOCALE              NS_DEFINE_EVENT_STATE_MACRO(0)
@@ -1514,16 +1515,21 @@ public:
   virtual nsresult RemoveImage(imgIRequest* aImage) = 0;
 
   // Makes the images on this document locked/unlocked. By default, the locking
   // state is unlocked/false.
   virtual nsresult SetImageLockingState(PRBool aLocked) = 0;
 
   virtual nsresult GetStateObject(nsIVariant** aResult) = 0;
 
+  virtual nsDOMNavigationTiming* GetNavigationTiming() const = 0;
+
+  virtual nsresult SetNavigationTiming(nsDOMNavigationTiming* aTiming) = 0;
+
+
 protected:
   ~nsIDocument()
   {
     // XXX The cleanup of mNodeInfoManager (calling DropDocumentReference and
     //     releasing it) happens in the nsDocument destructor. We'd prefer to
     //     do it here but nsNodeInfoManager is a concrete class that we don't
     //     want to expose to users of the nsIDocument API outside of Gecko.
   }
--- a/content/base/src/Makefile.in
+++ b/content/base/src/Makefile.in
@@ -206,11 +206,12 @@ INCLUDES	+= \
 		-I$(srcdir)/../../../layout/style \
 		-I$(srcdir)/../../../dom/base \
 		-I$(srcdir)/../../xml/document/src \
 		-I$(topsrcdir)/xpcom/io \
 		-I$(topsrcdir)/dom/ipc \
 		-I$(topsrcdir)/js/src/xpconnect/src \
 		-I$(topsrcdir)/caps/include \
 		-I$(topsrcdir)/netwerk/base/src \
+		-I$(topsrcdir)/xpcom/ds \
 		$(NULL)
 
 DEFINES += -D_IMPL_NS_LAYOUT
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -178,16 +178,17 @@ static NS_DEFINE_CID(kDOMEventGroupCID, 
 #include "nsEscape.h"
 #ifdef MOZ_MEDIA
 #include "nsHTMLMediaElement.h"
 #endif // MOZ_MEDIA
 
 #include "mozAutoDocUpdate.h"
 #include "nsGlobalWindow.h"
 #include "mozilla/dom/indexedDB/IndexedDatabaseManager.h"
+#include "nsDOMNavigationTiming.h"
 
 #ifdef MOZ_SMIL
 #include "nsSMILAnimationController.h"
 #include "imgIContainer.h"
 #include "nsSVGUtils.h"
 #endif // MOZ_SMIL
 
 #include "nsRefreshDriver.h"
@@ -4098,24 +4099,32 @@ void
 nsDocument::DispatchContentLoadedEvents()
 {
   NS_TIME_FUNCTION;
   // If you add early returns from this method, make sure you're
   // calling UnblockOnload properly.
   
   // Unpin references to preloaded images
   mPreloadingImages.Clear();
+
+  if (mTiming) {
+    mTiming->NotifyDOMContentLoadedStart(nsIDocument::GetDocumentURI());
+  }
     
   // Fire a DOM event notifying listeners that this document has been
   // loaded (excluding images and other loads initiated by this
   // document).
   nsContentUtils::DispatchTrustedEvent(this, static_cast<nsIDocument*>(this),
                                        NS_LITERAL_STRING("DOMContentLoaded"),
                                        PR_TRUE, PR_TRUE);
 
+  if (mTiming) {
+    mTiming->NotifyDOMContentLoadedEnd(nsIDocument::GetDocumentURI());
+  }
+
   // If this document is a [i]frame, fire a DOMFrameContentLoaded
   // event on all parent documents notifying that the HTML (excluding
   // other external files such as images and stylesheets) in a frame
   // has finished loading.
 
   // target_frame is the [i]frame element that will be used as the
   // target for the event. It's the [i]frame whose content is done
   // loading.
@@ -7690,16 +7699,36 @@ nsDocument::CloneDocHelper(nsDocument* c
   clone->mBaseTarget = mBaseTarget;
   return NS_OK;
 }
 
 void
 nsDocument::SetReadyStateInternal(ReadyState rs)
 {
   mReadyState = rs;
+  if (mTiming) {
+    switch (rs) {
+      case READYSTATE_LOADING:
+        mTiming->NotifyDOMLoading(nsIDocument::GetDocumentURI());
+        break;
+      case READYSTATE_INTERACTIVE:
+        mTiming->NotifyDOMInteractive(nsIDocument::GetDocumentURI());
+        break;
+      case READYSTATE_COMPLETE:
+        mTiming->NotifyDOMComplete(nsIDocument::GetDocumentURI());
+        break;
+      default:
+        NS_WARNING("Unexpected ReadyState value");
+        break;
+    }
+  }
+  // At the time of loading start, we don't have timing object, record time.
+  if (READYSTATE_LOADING == rs) {
+    mLoadingTimeStamp = mozilla::TimeStamp::Now();
+  }
 
   nsRefPtr<nsPLDOMEvent> plevent =
     new nsPLDOMEvent(this, NS_LITERAL_STRING("readystatechange"), PR_FALSE, PR_FALSE); 
   if (plevent) {
     plevent->RunDOMEventWhenSafe();
   }
 }
 
@@ -8158,16 +8187,32 @@ nsDocument::GetStateObject(nsIVariant** 
       DeserializeToVariant(cx, getter_AddRefs(mStateObjectCached));
   }
 
   NS_IF_ADDREF(*aState = mStateObjectCached);
   
   return NS_OK;
 }
 
+nsDOMNavigationTiming*
+nsDocument::GetNavigationTiming() const
+{
+  return mTiming;
+}
+
+nsresult
+nsDocument::SetNavigationTiming(nsDOMNavigationTiming* aTiming)
+{
+  mTiming = aTiming;
+  if (!mLoadingTimeStamp.IsNull() && mTiming) {
+    mTiming->SetDOMLoadingTimeStamp(nsIDocument::GetDocumentURI(), mLoadingTimeStamp);
+  }
+  return NS_OK;
+}
+
 nsresult
 nsDocument::AddImage(imgIRequest* aImage)
 {
   NS_ENSURE_ARG_POINTER(aImage);
 
   // See if the image is already in the hashtable. If it is, get the old count.
   PRUint32 oldCount = 0;
   mImageTracker.Get(aImage, &oldCount);
@@ -8378,9 +8423,8 @@ nsDocument::CreateTouchList(nsIVariant* 
       }
       nsMemory::Free(rawArray);
     }
   }
 
   *aRetVal = retval.forget().get();
   return NS_OK;
 }
-
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -101,16 +101,18 @@
 #include "nsILoadContext.h"
 #include "nsIProgressEventSink.h"
 #include "nsISecurityEventSink.h"
 #include "nsIChannelEventSink.h"
 #include "imgIRequest.h"
 #include "nsIDOMDOMImplementation.h"
 #include "nsIDOMTouchEvent.h"
 
+#include "TimeStamp.h"
+
 #define XML_DECLARATION_BITS_DECLARATION_EXISTS   (1 << 0)
 #define XML_DECLARATION_BITS_ENCODING_EXISTS      (1 << 1)
 #define XML_DECLARATION_BITS_STANDALONE_EXISTS    (1 << 2)
 #define XML_DECLARATION_BITS_STANDALONE_YES       (1 << 3)
 
 
 class nsIEventListenerManager;
 class nsDOMStyleSheetList;
@@ -122,16 +124,17 @@ class nsIRadioVisitor;
 class nsIFormControl;
 struct nsRadioGroupStruct;
 class nsOnloadBlocker;
 class nsUnblockOnloadEvent;
 class nsChildContentList;
 class nsXMLEventsManager;
 class nsHTMLStyleSheet;
 class nsHTMLCSSStyleSheet;
+class nsDOMNavigationTiming;
 
 /**
  * Right now our identifier map entries contain information for 'name'
  * and 'id' mappings of a given string. This is so that
  * nsHTMLDocument::ResolveName only has to do one hash lookup instead
  * of two. It's not clear whether this still matters for performance.
  * 
  * We also store the document.all result list here. This is mainly so that
@@ -956,16 +959,20 @@ public:
   virtual Element *LookupImageElement(const nsAString& aElementId);
 
   virtual NS_HIDDEN_(nsresult) AddImage(imgIRequest* aImage);
   virtual NS_HIDDEN_(nsresult) RemoveImage(imgIRequest* aImage);
   virtual NS_HIDDEN_(nsresult) SetImageLockingState(PRBool aLocked);
 
   virtual nsresult GetStateObject(nsIVariant** aResult);
 
+  virtual nsDOMNavigationTiming* GetNavigationTiming() const;
+
+  virtual nsresult SetNavigationTiming(nsDOMNavigationTiming* aTiming);
+
 protected:
   friend class nsNodeUtils;
 
   /**
    * Check that aId is not empty and log a message to the console
    * service if it is.
    * @returns PR_TRUE if aId looks correct, PR_FALSE otherwise.
    */
@@ -1090,16 +1097,19 @@ protected:
    * 2) Removals from the DOM affect the table immediately
    * 3) Additions to the DOM always update existing entries for names, and add
    *    new ones for IDs.
    */
   nsTHashtable<nsIdentifierMapEntry> mIdentifierMap;
 
   nsClassHashtable<nsStringHashKey, nsRadioGroupStruct> mRadioGroups;
 
+  // Recorded time of change to 'loading' state.
+  mozilla::TimeStamp mLoadingTimeStamp;
+
   // True if the document has been detached from its content viewer.
   PRPackedBool mIsGoingAway:1;
   // True if the document is being destroyed.
   PRPackedBool mInDestructor:1;
 
   // True if this document has ever had an HTML or SVG <title> element
   // bound to it
   PRPackedBool mMayHaveTitleElement:1;
@@ -1148,16 +1158,17 @@ protected:
   // any.  This can change during the lifetime of the document.
   nsCOMPtr<nsIApplicationCache> mApplicationCache;
 
   nsCOMPtr<nsIContent> mFirstBaseNodeWithHref;
 
   nsEventStates mDocumentState;
   nsEventStates mGotDocumentState;
 
+  nsRefPtr<nsDOMNavigationTiming> mTiming;
 private:
   friend class nsUnblockOnloadEvent;
 
   void PostUnblockOnloadEvent();
   void DoUnblockOnload();
 
   nsresult CheckFrameOptions();
   nsresult InitCSP();
--- a/content/html/content/src/Makefile.in
+++ b/content/html/content/src/Makefile.in
@@ -140,11 +140,12 @@ INCLUDES	+= \
 		-I$(srcdir)/../../../../layout/style \
 		-I$(srcdir)/../../../../layout/tables \
 		-I$(srcdir)/../../../../layout/xul/base/src \
 		-I$(srcdir)/../../../../layout/generic \
 		-I$(srcdir)/../../../../dom/base \
 		-I$(srcdir)/../../../../editor/libeditor/base \
 		-I$(srcdir)/../../../../editor/libeditor/text \
 		-I$(srcdir) \
+		-I$(topsrcdir)/xpcom/ds \
 		$(NULL)
 
 DEFINES += -D_IMPL_NS_LAYOUT
--- a/content/html/document/src/Makefile.in
+++ b/content/html/document/src/Makefile.in
@@ -72,11 +72,12 @@ include $(topsrcdir)/config/rules.mk
 INCLUDES	+= \
 		-I$(srcdir)/../../../base/src \
 		-I$(srcdir)/../../../events/src \
 		-I$(srcdir)/../../content/src \
 		-I$(srcdir)/../../../../layout/style \
 		-I$(srcdir)/../../../../dom/base \
 		-I$(srcdir)/../../../../xpcom/io \
 		-I$(srcdir)/../../../../caps/include \
+		-I$(topsrcdir)/xpcom/ds \
 		$(NULL)
 
 DEFINES += -D_IMPL_NS_LAYOUT
--- a/content/svg/document/src/Makefile.in
+++ b/content/svg/document/src/Makefile.in
@@ -58,11 +58,12 @@ include $(topsrcdir)/config/rules.mk
 INCLUDES	+= \
 		-I$(srcdir) \
 		-I$(srcdir)/../../../xml/document/src \
 		-I$(srcdir)/../../../html/base/src \
 		-I$(srcdir)/../../../html/document/src \
 		-I$(srcdir)/../../../../layout/style \
 		-I$(srcdir)/../../../base/src \
 		-I$(srcdir)/../../../events/src \
+		-I$(topsrcdir)/xpcom/ds \
 		$(NULL)
 
 DEFINES += -D_IMPL_NS_LAYOUT
--- a/content/xbl/src/Makefile.in
+++ b/content/xbl/src/Makefile.in
@@ -83,11 +83,12 @@ LOCAL_INCLUDES	= \
 		-I$(srcdir)/../../html/base/src \
 		-I$(srcdir)/../../html/document/src \
 		-I$(srcdir)/../../xml/document/src \
 		-I$(srcdir)/../../xul/content/src \
 		-I$(srcdir)/../../xul/document/src \
 		-I$(srcdir)/../../events/src \
 		-I$(srcdir)/../../../layout/style \
 		-I$(srcdir)/../../../dom/base \
+		-I$(topsrcdir)/xpcom/ds \
 		$(NULL)
 
 DEFINES += -D_IMPL_NS_LAYOUT
--- a/content/xml/document/src/Makefile.in
+++ b/content/xml/document/src/Makefile.in
@@ -64,11 +64,12 @@ LOCAL_INCLUDES	= \
 		-I$(srcdir)/../../../xsl/document/src \
 		-I$(srcdir)/../../../html/document/src \
 		-I$(srcdir)/../../../../layout/style \
 		-I$(srcdir)/../../../base/src \
 		-I$(srcdir)/../../../xul/content/src \
 		-I$(srcdir)/../../../events/src \
 		-I$(srcdir)/../../../../dom/base \
 		-I$(srcdir)/../../../../caps/include \
+		-I$(topsrcdir)/xpcom/ds \
 		$(NULL)
 
 DEFINES += -D_IMPL_NS_LAYOUT
--- a/content/xul/content/src/Makefile.in
+++ b/content/xul/content/src/Makefile.in
@@ -68,11 +68,12 @@ LOCAL_INCLUDES	= \
 	-I$(srcdir)/../../templates/src \
 	-I$(srcdir)/../../../xml/content/src \
 	-I$(srcdir)/../../../base/src \
 	-I$(srcdir)/../../../xml/document/src \
 	-I$(srcdir)/../../../../layout/style \
 	-I$(srcdir)/../../../html/content/src \
 	-I$(srcdir)/../../../events/src \
 	-I$(srcdir)/../../../xbl/src \
+	-I$(topsrcdir)/xpcom/ds \
 	$(NULL)
 
 DEFINES += -D_IMPL_NS_LAYOUT
--- a/content/xul/document/src/Makefile.in
+++ b/content/xul/document/src/Makefile.in
@@ -70,11 +70,12 @@ LOCAL_INCLUDES	= -I$(srcdir)/../../../ba
 		  -I$(srcdir)/../../templates/src \
 		  -I$(srcdir)/../../../../layout/base \
 		  -I$(srcdir)/../../../../layout/generic \
 		  -I$(srcdir)/../../../../layout/style \
 		  -I$(srcdir)/../../../../layout/xul/base/src \
 		  -I$(srcdir)/../../../xml/document/src \
 		  -I$(srcdir)/../../../xbl/src \
 		  -I$(srcdir)/../../../events/src \
+		  -I$(topsrcdir)/xpcom/ds \
 		  $(NULL)
 
 DEFINES += -D_IMPL_NS_LAYOUT
--- a/docshell/base/Makefile.in
+++ b/docshell/base/Makefile.in
@@ -113,11 +113,13 @@ CPPSRCS = \
 # we don't want the shared lib, but we want to force the creation of a
 # static lib.
 FORCE_STATIC_LIB = 1
 
 include $(topsrcdir)/config/rules.mk
 
 LOCAL_INCLUDES += \
   -I$(srcdir)/../shistory/src \
+  -I$(topsrcdir)/dom/base \
   -I$(topsrcdir)/layout/base \
   -I$(topsrcdir)/js/src/xpconnect/src \
+  -I$(topsrcdir)/xpcom/ds \
   $(NULL)
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -230,16 +230,18 @@ static NS_DEFINE_CID(kAppShellCID, NS_AP
 #endif
 
 #include "nsContentErrors.h"
 #include "nsIChannelPolicy.h"
 #include "nsIContentSecurityPolicy.h"
 
 #include "nsXULAppAPI.h"
 
+#include "nsDOMNavigationTiming.h"
+
 using namespace mozilla;
 
 // Number of documents currently loading
 static PRInt32 gNumberOfDocumentsLoading = 0;
 
 // Global count of existing docshells.
 static PRInt32 gDocShellCount = 0;
 
@@ -669,16 +671,55 @@ DispatchPings(nsIContent *content, nsIUR
     return;
 
   info.numPings = 0;
   info.referrer = referrer;
 
   ForEachPing(content, SendPing, &info);
 }
 
+static nsDOMPerformanceNavigationType
+ConvertLoadTypeToNavigationType(PRUint32 aLoadType)
+{
+    nsDOMPerformanceNavigationType result = nsIDOMPerformanceNavigation::TYPE_RESERVED;
+    switch (aLoadType) {
+    case LOAD_NORMAL:
+    case LOAD_NORMAL_EXTERNAL:
+    case LOAD_NORMAL_BYPASS_CACHE:
+    case LOAD_NORMAL_BYPASS_PROXY:
+    case LOAD_NORMAL_BYPASS_PROXY_AND_CACHE:
+    case LOAD_LINK:
+        result = nsIDOMPerformanceNavigation::TYPE_NAVIGATE;
+        break;
+    case LOAD_HISTORY:
+        result = nsIDOMPerformanceNavigation::TYPE_BACK_FORWARD;
+        break;
+    case LOAD_RELOAD_NORMAL:
+    case LOAD_RELOAD_CHARSET_CHANGE:
+    case LOAD_RELOAD_BYPASS_CACHE:
+    case LOAD_RELOAD_BYPASS_PROXY:
+    case LOAD_RELOAD_BYPASS_PROXY_AND_CACHE:
+        result = nsIDOMPerformanceNavigation::TYPE_RELOAD;
+        break;
+    case LOAD_NORMAL_REPLACE:
+    case LOAD_STOP_CONTENT:
+    case LOAD_STOP_CONTENT_AND_REPLACE:
+    case LOAD_REFRESH:
+    case LOAD_BYPASS_HISTORY:
+    case LOAD_ERROR_PAGE:
+    case LOAD_PUSHSTATE:
+        result = nsIDOMPerformanceNavigation::TYPE_RESERVED;
+        break;
+    default:
+        NS_NOTREACHED("Unexpected load type value");
+    }
+
+    return result;
+}
+
 static nsISHEntry* GetRootSHEntry(nsISHEntry *entry);
 
 //*****************************************************************************
 //***    nsDocShell: Object Management
 //*****************************************************************************
 
 static PRUint64 gDocshellIDCounter = 0;
 
@@ -1515,18 +1556,26 @@ NS_IMETHODIMP
 nsDocShell::FirePageHideNotification(PRBool aIsUnload)
 {
     if (mContentViewer && !mFiredUnloadEvent) {
         // Keep an explicit reference since calling PageHide could release
         // mContentViewer
         nsCOMPtr<nsIContentViewer> kungFuDeathGrip(mContentViewer);
         mFiredUnloadEvent = PR_TRUE;
 
+        if (mTiming) {
+            mTiming->NotifyUnloadEventStart();
+        }
+
         mContentViewer->PageHide(aIsUnload);
 
+        if (mTiming) {
+            mTiming->NotifyUnloadEventEnd();
+        }
+
         nsAutoTArray<nsCOMPtr<nsIDocShell>, 8> kids;
         PRInt32 i, n = mChildList.Count();
         kids.SetCapacity(n);
         for (i = 0; i < n; i++) {
             kids.AppendElement(do_QueryInterface(ChildAt(i)));
         }
 
         n = kids.Length();
@@ -1538,16 +1587,33 @@ nsDocShell::FirePageHideNotification(PRB
         // Now make sure our editor, if any, is detached before we go
         // any farther.
         DetachEditorFromWindow();
     }
 
     return NS_OK;
 }
 
+nsresult
+nsDocShell::MaybeInitTiming()
+{
+    if (mTiming) {
+        return NS_OK;
+    }
+
+    PRBool enabled;
+    nsresult rv = mPrefs->GetBoolPref("dom.enable_performance", &enabled);
+    if (NS_SUCCEEDED(rv) && enabled) {
+      mTiming = new nsDOMNavigationTiming();
+      mTiming->NotifyNavigationStart();
+    }
+    return NS_OK;
+}
+
+
 //
 // Bug 13871: Prevent frameset spoofing
 //
 // This routine answers: 'Is origin's document from same domain as
 // target's document?'
 //
 // file: uris are considered the same domain for the purpose of
 // frame navigation regardless of script accessibility (bug 420425)
@@ -5819,25 +5885,32 @@ nsDocShell::OnProgressChange(nsIWebProgr
 
 NS_IMETHODIMP
 nsDocShell::OnStateChange(nsIWebProgress * aProgress, nsIRequest * aRequest,
                           PRUint32 aStateFlags, nsresult aStatus)
 {
     nsresult rv;
 
     if ((~aStateFlags & (STATE_START | STATE_IS_NETWORK)) == 0) {
+        // Save timing statistics.
+        nsCOMPtr<nsIChannel> channel(do_QueryInterface(aRequest));
+        // Make sure timing is created
+        rv = MaybeInitTiming();
+        nsCOMPtr<nsIURI> uri;
+        channel->GetURI(getter_AddRefs(uri));
+        if (mTiming) {
+          mTiming->NotifyFetchStart(uri, ConvertLoadTypeToNavigationType(mLoadType));
+        }
+
         nsCOMPtr<nsIWyciwygChannel>  wcwgChannel(do_QueryInterface(aRequest));
         nsCOMPtr<nsIWebProgress> webProgress =
             do_QueryInterface(GetAsSupports(this));
 
         // Was the wyciwyg document loaded on this docshell?
         if (wcwgChannel && !mLSHE && (mItemType == typeContent) && aProgress == webProgress.get()) {
-            nsCOMPtr<nsIURI> uri;
-            wcwgChannel->GetURI(getter_AddRefs(uri));
-        
             PRBool equalUri = PR_TRUE;
             // Store the wyciwyg url in session history, only if it is
             // being loaded fresh for the first time. We don't want 
             // multiple entries for successive loads
             if (mCurrentURI &&
                 NS_SUCCEEDED(uri->Equals(mCurrentURI, &equalUri)) &&
                 !equalUri) {
 
@@ -5950,16 +6023,23 @@ nsDocShell::OnRedirectStateChange(nsICha
         return; // not a toplevel document
 
     nsCOMPtr<nsIURI> oldURI, newURI;
     aOldChannel->GetURI(getter_AddRefs(oldURI));
     aNewChannel->GetURI(getter_AddRefs(newURI));
     if (!oldURI || !newURI) {
         return;
     }
+    // On session restore we get a redirect from page to itself. Don't count it.
+    PRBool equals = PR_FALSE;
+    if (mTiming &&
+        !(mLoadType == LOAD_HISTORY &&
+          NS_SUCCEEDED(newURI->Equals(oldURI, &equals)) && equals)) {
+        mTiming->NotifyRedirect(oldURI, newURI);
+    }
 
     // Below a URI visit is saved (see AddURIVisit method doc).
     // The visit chain looks something like:
     //   ...
     //   Site N - 1
     //                =>  Site N
     //   (redirect to =>) Site N + 1 (we are here!)
 
@@ -6033,17 +6113,20 @@ nsDocShell::EndPageLoad(nsIWebProgress *
                         nsIChannel * aChannel, nsresult aStatus)
 {
     if(!aChannel)
         return NS_ERROR_NULL_POINTER;
     
     nsCOMPtr<nsIURI> url;
     nsresult rv = aChannel->GetURI(getter_AddRefs(url));
     if (NS_FAILED(rv)) return rv;
-  
+
+    // Timing is picked up by the window, we don't need it anymore
+    mTiming = nsnull;
+
     // clean up reload state for meta charset
     if (eCharsetReloadRequested == mCharsetReloadState)
         mCharsetReloadState = eCharsetReloadStopOrigional;
     else 
         mCharsetReloadState = eCharsetReloadInit;
 
     // Save a pointer to the currently-loading history entry.
     // nsDocShell::EndPageLoad will clear mLSHE, but we may need this history
@@ -6462,27 +6545,38 @@ nsDocShell::CreateAboutBlankContentViewe
   nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
   
   if (mContentViewer) {
     // We've got a content viewer already. Make sure the user
     // permits us to discard the current document and replace it
     // with about:blank. And also ensure we fire the unload events
     // in the current document.
 
+    // Make sure timing is created. Unload gets fired first for
+    // document loaded from the session history.
+    rv = MaybeInitTiming();
+    if (mTiming) {
+      mTiming->NotifyBeforeUnload();
+    }
+
     PRBool okToUnload;
     rv = mContentViewer->PermitUnload(PR_FALSE, &okToUnload);
 
     if (NS_SUCCEEDED(rv) && !okToUnload) {
       // The user chose not to unload the page, interrupt the load.
       return NS_ERROR_FAILURE;
     }
 
     mSavingOldViewer = aTryToSaveOldPresentation && 
                        CanSavePresentation(LOAD_NORMAL, nsnull, nsnull);
 
+    if (mTiming) {
+      mTiming->NotifyUnloadAccepted(mCurrentURI);
+    }
+
     // Make sure to blow away our mLoadingURI just in case.  No loads
     // from inside this pagehide.
     mLoadingURI = nsnull;
     
     // Stop any in-progress loading, so that we don't accidentally trigger any
     // PageShow notifications from Embed() interrupting our loading below.
     Stop();
 
@@ -7692,16 +7786,22 @@ nsDocShell::SetupNewViewer(nsIContentVie
 
     mContentViewer = aNewViewer;
 
     nsCOMPtr<nsIWidget> widget;
     NS_ENSURE_SUCCESS(GetMainWidget(getter_AddRefs(widget)), NS_ERROR_FAILURE);
 
     nsIntRect bounds(x, y, cx, cy);
 
+    nsCOMPtr<nsIDocumentViewer> docviewer =
+        do_QueryInterface(mContentViewer);
+    if (docviewer) {
+        docviewer->SetNavigationTiming(mTiming);
+    }
+
     if (NS_FAILED(mContentViewer->Init(widget, bounds))) {
         mContentViewer = nsnull;
         NS_ERROR("ContentViewer Initialization failed");
         return NS_ERROR_FAILURE;
     }
 
     // If we have old state to copy, set the old state onto the new content
     // viewer
@@ -7724,19 +7824,16 @@ nsDocShell::SetupNewViewer(nsIContentVie
         NS_ENSURE_SUCCESS(newMUDV->SetFullZoom(pageZoom),
                           NS_ERROR_FAILURE);
         NS_ENSURE_SUCCESS(newMUDV->SetAuthorStyleDisabled(styleDisabled),
                           NS_ERROR_FAILURE);
     }
 
     // Stuff the bgcolor from the old pres shell into the new
     // pres shell. This improves page load continuity.
-    nsCOMPtr<nsIDocumentViewer> docviewer =
-        do_QueryInterface(mContentViewer);
-
     if (docviewer) {
         nsCOMPtr<nsIPresShell> shell;
         docviewer->GetPresShell(getter_AddRefs(shell));
 
         if (shell) {
             shell->SetCanvasBackground(bgcolor);
         }
     }
@@ -8453,29 +8550,37 @@ nsDocShell::InternalLoad(nsIURI * aURI,
     }
     
     // mContentViewer->PermitUnload can destroy |this| docShell, which
     // causes the next call of CanSavePresentation to crash. 
     // Hold onto |this| until we return, to prevent a crash from happening. 
     // (bug#331040)
     nsCOMPtr<nsIDocShell> kungFuDeathGrip(this);
 
+    rv = MaybeInitTiming();
+    if (mTiming) {
+      mTiming->NotifyBeforeUnload();
+    }
     // Check if the page doesn't want to be unloaded. The javascript:
     // protocol handler deals with this for javascript: URLs.
     if (!bIsJavascript && mContentViewer) {
         PRBool okToUnload;
         rv = mContentViewer->PermitUnload(PR_FALSE, &okToUnload);
 
         if (NS_SUCCEEDED(rv) && !okToUnload) {
             // The user chose not to unload the page, interrupt the
             // load.
             return NS_OK;
         }
     }
 
+    if (mTiming) {
+      mTiming->NotifyUnloadAccepted(mCurrentURI);
+    }
+
     // Check for saving the presentation here, before calling Stop().
     // This is necessary so that we can catch any pending requests.
     // Since the new request has not been created yet, we pass null for the
     // new request parameter.
     // Also pass nsnull for the document, since it doesn't affect the return
     // value for our purposes here.
     PRBool savePresentation = CanSavePresentation(aLoadType, nsnull, nsnull);
 
@@ -11676,17 +11781,16 @@ nsDocShell::GetPrintPreview(nsIWebBrowse
   nsCOMPtr<nsIWebBrowserPrint> result = do_QueryInterface(print);
   result.forget(aPrintPreview);
   return NS_OK;
 #else
   return NS_ERROR_NOT_IMPLEMENTED;
 #endif
 }
 
-
 #ifdef DEBUG
 unsigned long nsDocShell::gNumberOfDocShells = 0;
 #endif
 
 NS_IMETHODIMP
 nsDocShell::GetCanExecuteScripts(PRBool *aResult)
 {
   NS_ENSURE_ARG_POINTER(aResult);
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -114,16 +114,17 @@
 #include "nsIClipboardCommands.h"
 #include "nsICommandManager.h"
 #include "nsCRT.h"
 
 class nsDocShell;
 class nsIController;
 class OnLinkClickEvent;
 class nsIScrollableFrame;
+class nsDOMNavigationTiming;
 
 /* load commands were moved to nsIDocShell.h */
 /* load types were moved to nsDocShellLoadTypes.h */
 
 /* internally used ViewMode types */
 enum ViewMode {
     viewNormal = 0x0,
     viewSource = 0x1
@@ -679,16 +680,18 @@ protected:
 
     nsIChannel* GetCurrentDocChannel();
 protected:
     // Override the parent setter from nsDocLoader
     virtual nsresult SetDocLoaderParent(nsDocLoader * aLoader);
 
     void ClearFrameHistory(nsISHEntry* aEntry);
 
+    nsresult MaybeInitTiming();
+
     // Event type dispatched by RestorePresentation
     class RestorePresentationEvent : public nsRunnable {
     public:
         NS_DECL_NSIRUNNABLE
         RestorePresentationEvent(nsDocShell *ds) : mDocShell(ds) {}
         void Revoke() { mDocShell = nsnull; }
     private:
         nsRefPtr<nsDocShell> mDocShell;
@@ -832,16 +835,18 @@ protected:
     PRPackedBool               mDynamicallyCreated;
 #ifdef DEBUG
     PRPackedBool               mInEnsureScriptEnv;
 #endif
     PRUint64                   mHistoryID;
 
     static nsIURIFixup *sURIFixup;
 
+    nsRefPtr<nsDOMNavigationTiming> mTiming;
+
 #ifdef DEBUG
 private:
     // We're counting the number of |nsDocShells| to help find leaks
     static unsigned long gNumberOfDocShells;
 #endif /* DEBUG */
 
 public:
     class InterfaceRequestorProxy : public nsIInterfaceRequestor {
--- a/docshell/test/Makefile.in
+++ b/docshell/test/Makefile.in
@@ -99,16 +99,18 @@ include $(topsrcdir)/config/rules.mk
 		file_bug634834.html \
 		test_bug637644.html \
 		test_bug640387_1.html \
 		test_bug640387_2.html \
 		file_bug640387.html \
 		test_framedhistoryframes.html \
 		test_windowedhistoryframes.html \
 		historyframes.html \
+		test_bug570341.html \
+		bug570341_recordevents.html \
 		$(NULL)
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa)
 _TEST_FILES += \
 		test_bug511449.html \
 		file_bug511449.html \
 		$(NULL)
 endif
new file mode 100644
--- /dev/null
+++ b/docshell/test/bug570341_recordevents.html
@@ -0,0 +1,25 @@
+<html>
+<head>
+<script>
+    var start = Date.now();
+    window._testing_js_start = Math.floor(start);
+    window._testing_js_start_fine = start;
+    window['_testing_js_after_' + document.readyState] = Math.floor(start);
+    document.addEventListener('DOMContentLoaded',
+      function () {
+        window._testing_evt_DOMContentLoaded = Math.floor(Date.now());
+        window._testing_evt_DOMContentLoaded_fine = Date.now();
+      }, true);
+    document.addEventListener('readystatechange', function(){
+      window['_testing_evt_DOM_' + document.readyState] = Math.floor(Date.now());
+      window['_testing_evt_DOM_' + document.readyState + '_fine'] = Date.now();
+    }, true);
+    function recordLoad() {
+      window._testing_evt_load = Math.floor(Date.now());
+      window._testing_evt_load_fine = Date.now();
+    }
+</script>
+</head>
+<body onload="recordLoad()">This document collects time
+for events related to the page load progress.</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/docshell/test/test_bug570341.html
@@ -0,0 +1,149 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=570341
+-->
+<head>
+  <title>Test for Bug 570341</title>
+  <script type="application/javascript" src="/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+<script>
+  var start = Date.now();
+  var moments = {};
+
+  var unload = 0;
+  var wasEnabled = true;
+
+  function collectMoments() {
+    var win = frames[0];
+    var timing = (win.performance && win.performance.timing) || {};
+    for (var p in timing) {
+      moments[p] = timing[p];
+    }
+    for (var p in win) {
+      if (p.substring(0,9) == '_testing_') {
+        moments[p.substring(9)] = win[p];
+      }
+    }
+    moments['evt_unload'] = Math.floor(unload);
+    moments['evt_unload_fine'] = unload;
+    return moments;
+  }
+
+  function showSequence(node){
+    while(node.firstChild) {
+      node.removeChild(node.firstChild);
+    }
+    var sequence = [];
+    for (var p in moments) {
+      sequence.push(p);
+    }
+    sequence.sort(function(a, b){
+      return moments[a] - moments[b];
+    });
+    table = document.createElement('table');
+    node.appendChild(table);
+    row = document.createElement('tr');
+    table.appendChild(row);
+    cell = document.createElement('td');
+    row.appendChild(cell);
+    cell.appendChild(document.createTextNode('start'));
+    cell = document.createElement('td');
+    row.appendChild(cell);
+    cell.appendChild(document.createTextNode(start));
+    for (var i = 0; i < sequence.length; ++i) {
+      var prop = sequence[i];
+      row = document.createElement('tr');
+      table.appendChild(row);
+      cell = document.createElement('td');
+      row.appendChild(cell);
+      cell.appendChild(document.createTextNode(prop));
+      cell = document.createElement('td');
+      row.appendChild(cell);
+      cell.appendChild(document.createTextNode(moments[prop]));
+    }
+  }
+
+  function checkValues(){
+    collectMoments();
+
+    var sequences = [
+      ['navigationStart', 'unloadEventStart', 'evt_unload' , 'unloadEventEnd'],
+      ['navigationStart', 'fetchStart', 'domainLookupStart', 'domainLookupEnd',
+       'connectStart', 'connectEnd', 'requestStart', 'responseStart', 'responseEnd'],
+      ['responseStart', 'domLoading', 'domInteractive', 'domComplete'],
+      ['domContentLoadedEventStart', 'evt_DOMContentLoaded', 'domContentLoadedEventEnd',
+       'loadEventStart', 'evt_load', 'loadEventEnd']
+    ]
+
+    for (var i = 0; i < sequences.length; ++i) {
+      var seq = sequences[i];
+      for (var j = 0; j < seq.length; ++j) {
+        var prop = seq[j];
+        if (j > 0) {
+          var prevProp = seq[j-1];
+          ok(moments[prevProp] <= moments[prop],
+              ['Expected ', prevProp, ' to happen before ', prop,
+              ', got ', prevProp, ' = ', moments[prevProp],
+              ', ', prop, ' = ', moments[prop]].join(''));
+        }
+      }
+    }
+    try {
+      SpecialPowers.setBoolPref('dom.enable_performance', wasEnabled);
+    } catch (err) {
+      todo(false, 'Exception in SpecialPowers: ' + err + ' at ' + err.stack);
+    }
+
+    SimpleTest.finish()
+  }
+
+window.onload = function() {
+  try {
+    wasEnabled = SpecialPowers.getBoolPref('dom.enable_performance');
+  } catch (err) {
+    todo(false, 'Exception in SpecialPowers: ' + err + ' at ' + err.stack);
+  }
+  SpecialPowers.setBoolPref('dom.enable_performance', true);
+
+  var win = frames[0];
+  win.addEventListener('unload', function(){
+    unload = Date.now();
+  }, true);
+  frames[0].location = 'bug570341_recordevents.html'
+  var seenLoad = 0;
+  win.addEventListener('load', function (){
+    seenLoad = Date.now();
+  }, true);
+  var interval = setInterval(function () {
+    var stopPolling = (win.performance && win.performance.loadEventEnd) ||
+                      (seenLoad && Date.now() >= seenLoad + 200);
+    if (stopPolling) {
+      clearInterval(interval);
+      checkValues();
+    } else if (win._testing_evt_load) {
+      seenLoad = Date.now();
+    }
+  }, 100);
+	}
+</script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=570341">Mozilla Bug 570341</a>
+<div id="frames">
+<iframe name="child0" src="navigation/blank.html"></iframe>
+</div>
+<button type="button" onclick="showSequence(document.getElementById('display'))">
+    Show Events</button>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html>
--- a/dom/base/Makefile.in
+++ b/dom/base/Makefile.in
@@ -105,30 +105,33 @@ CPPSRCS =			\
 	nsPluginArray.cpp	\
 	nsWindowRoot.cpp	\
 	nsDOMClassInfo.cpp	\
 	nsScriptNameSpaceManager.cpp \
 	nsDOMScriptObjectFactory.cpp \
 	nsQueryContentEventResult.cpp \
 	nsContentPermissionHelper.cpp \
 	nsStructuredCloneContainer.cpp \
+	nsDOMNavigationTiming.cpp \
+	nsPerformance.cpp	\
 	$(NULL)
 
 include $(topsrcdir)/dom/dom-config.mk
 
 ifdef MOZ_JSDEBUGGER
 DEFINES += -DMOZ_JSDEBUGGER
 endif
 
 include $(topsrcdir)/config/config.mk
 include $(topsrcdir)/ipc/chromium/chromium-config.mk
 
 include $(topsrcdir)/config/rules.mk
 
 LOCAL_INCLUDES += \
 		-I$(srcdir)/../../js/src/xpconnect/src \
 		-I$(srcdir)/../../js/src/xpconnect/wrappers \
+		-I$(topsrcdir)/xpcom/ds \
 		$(NULL)
 
 ifdef MOZ_X11
 CXXFLAGS += $(TK_CFLAGS)
 LDFLAGS += $(TK_LIBS)
 endif
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -116,16 +116,19 @@
 #include "nsIDOMWindowInternal.h"
 #include "nsPIDOMWindow.h"
 #include "nsIDOMJSWindow.h"
 #include "nsIDOMWindowCollection.h"
 #include "nsIDOMHistory.h"
 #include "nsIDOMMediaList.h"
 #include "nsIDOMChromeWindow.h"
 #include "nsIDOMConstructor.h"
+#include "nsIDOMPerformanceTiming.h"
+#include "nsIDOMPerformanceNavigation.h"
+#include "nsIDOMPerformance.h"
 #include "nsClientRect.h"
 
 // DOM core includes
 #include "nsDOMError.h"
 #include "nsIDOMDOMException.h"
 #include "nsIDOMNode.h"
 #include "nsIDOM3Node.h"
 #include "nsIDOM3Attr.h"
@@ -690,16 +693,22 @@ static nsDOMClassInfoData sClassInfoData
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(MimeTypeArray, nsMimeTypeArraySH,
                            ARRAY_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(BarProp, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(History, nsHistorySH,
                            ARRAY_SCRIPTABLE_FLAGS |
                            nsIXPCScriptable::WANT_PRECREATE)
+  NS_DEFINE_CLASSINFO_DATA(PerformanceTiming, nsDOMGenericSH,
+                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
+  NS_DEFINE_CLASSINFO_DATA(PerformanceNavigation, nsDOMGenericSH,
+                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
+  NS_DEFINE_CLASSINFO_DATA(Performance, nsDOMGenericSH,
+                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(Screen, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(DOMPrototype, nsDOMConstructorSH,
                            DOM_BASE_SCRIPTABLE_FLAGS |
                            nsIXPCScriptable::WANT_PRECREATE |
                            nsIXPCScriptable::WANT_HASINSTANCE |
                            nsIXPCScriptable::DONT_ENUM_QUERY_INTERFACE)
   NS_DEFINE_CLASSINFO_DATA(DOMConstructor, nsDOMConstructorSH,
@@ -2392,36 +2401,63 @@ nsDOMClassInfo::Init()
   NS_ENSURE_SUCCESS(rv, rv);
 
   JSContext *cx = nsnull;
 
   rv = stack->GetSafeJSContext(&cx);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (nsGlobalWindow::HasIndexedDBSupport()) {
-    DOM_CLASSINFO_MAP_BEGIN(Window, nsIDOMWindow)
-      DOM_CLASSINFO_MAP_ENTRY(nsIDOMWindow)
-      DOM_CLASSINFO_MAP_ENTRY(nsIDOMJSWindow)
-      DOM_CLASSINFO_MAP_ENTRY(nsIDOMWindowInternal)
-      DOM_CLASSINFO_MAP_ENTRY(nsIDOMNSEventTarget)
-      DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
-      DOM_CLASSINFO_MAP_ENTRY(nsIDOMStorageWindow)
-      DOM_CLASSINFO_MAP_ENTRY(nsIDOMStorageIndexedDB)
-      DOM_CLASSINFO_MAP_ENTRY(nsIDOMWindow_2_0_BRANCH)
-    DOM_CLASSINFO_MAP_END
+    if (nsGlobalWindow::HasPerformanceSupport()) {
+      DOM_CLASSINFO_MAP_BEGIN(Window, nsIDOMWindow)
+        DOM_CLASSINFO_MAP_ENTRY(nsIDOMWindow)
+        DOM_CLASSINFO_MAP_ENTRY(nsIDOMJSWindow)
+        DOM_CLASSINFO_MAP_ENTRY(nsIDOMWindowInternal)
+        DOM_CLASSINFO_MAP_ENTRY(nsIDOMNSEventTarget)
+        DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
+        DOM_CLASSINFO_MAP_ENTRY(nsIDOMStorageWindow)
+        DOM_CLASSINFO_MAP_ENTRY(nsIDOMStorageIndexedDB)
+        DOM_CLASSINFO_MAP_ENTRY(nsIDOMWindow_2_0_BRANCH)
+        DOM_CLASSINFO_MAP_ENTRY(nsIDOMWindowPerformance)
+      DOM_CLASSINFO_MAP_END
+    } else {
+      DOM_CLASSINFO_MAP_BEGIN(Window, nsIDOMWindow)
+        DOM_CLASSINFO_MAP_ENTRY(nsIDOMWindow)
+        DOM_CLASSINFO_MAP_ENTRY(nsIDOMJSWindow)
+        DOM_CLASSINFO_MAP_ENTRY(nsIDOMWindowInternal)
+        DOM_CLASSINFO_MAP_ENTRY(nsIDOMNSEventTarget)
+        DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
+        DOM_CLASSINFO_MAP_ENTRY(nsIDOMStorageWindow)
+        DOM_CLASSINFO_MAP_ENTRY(nsIDOMStorageIndexedDB)
+        DOM_CLASSINFO_MAP_ENTRY(nsIDOMWindow_2_0_BRANCH)
+      DOM_CLASSINFO_MAP_END
+    }
   } else {
-    DOM_CLASSINFO_MAP_BEGIN(Window, nsIDOMWindow)
-      DOM_CLASSINFO_MAP_ENTRY(nsIDOMWindow)
-      DOM_CLASSINFO_MAP_ENTRY(nsIDOMJSWindow)
-      DOM_CLASSINFO_MAP_ENTRY(nsIDOMWindowInternal)
-      DOM_CLASSINFO_MAP_ENTRY(nsIDOMNSEventTarget)
-      DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
-      DOM_CLASSINFO_MAP_ENTRY(nsIDOMStorageWindow)
-      DOM_CLASSINFO_MAP_ENTRY(nsIDOMWindow_2_0_BRANCH)
-    DOM_CLASSINFO_MAP_END
+    if (nsGlobalWindow::HasPerformanceSupport()) {
+      DOM_CLASSINFO_MAP_BEGIN(Window, nsIDOMWindow)
+        DOM_CLASSINFO_MAP_ENTRY(nsIDOMWindow)
+        DOM_CLASSINFO_MAP_ENTRY(nsIDOMJSWindow)
+        DOM_CLASSINFO_MAP_ENTRY(nsIDOMWindowInternal)
+        DOM_CLASSINFO_MAP_ENTRY(nsIDOMNSEventTarget)
+        DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
+        DOM_CLASSINFO_MAP_ENTRY(nsIDOMStorageWindow)
+        DOM_CLASSINFO_MAP_ENTRY(nsIDOMWindow_2_0_BRANCH)
+        DOM_CLASSINFO_MAP_ENTRY(nsIDOMWindowPerformance)
+      DOM_CLASSINFO_MAP_END
+    } else {
+      DOM_CLASSINFO_MAP_BEGIN(Window, nsIDOMWindow)
+        DOM_CLASSINFO_MAP_ENTRY(nsIDOMWindow)
+        DOM_CLASSINFO_MAP_ENTRY(nsIDOMJSWindow)
+        DOM_CLASSINFO_MAP_ENTRY(nsIDOMWindowInternal)
+        DOM_CLASSINFO_MAP_ENTRY(nsIDOMNSEventTarget)
+        DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
+        DOM_CLASSINFO_MAP_ENTRY(nsIDOMStorageWindow)
+        DOM_CLASSINFO_MAP_ENTRY(nsIDOMWindow_2_0_BRANCH)
+      DOM_CLASSINFO_MAP_END
+    }
   }
 
   DOM_CLASSINFO_MAP_BEGIN(WindowUtils, nsIDOMWindowUtils)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMWindowUtils)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(Location, nsIDOMLocation)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMLocation)
@@ -2461,16 +2497,31 @@ nsDOMClassInfo::Init()
   DOM_CLASSINFO_MAP_BEGIN(BarProp, nsIDOMBarProp)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMBarProp)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(History, nsIDOMHistory)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMHistory)
   DOM_CLASSINFO_MAP_END
 
+  DOM_CLASSINFO_MAP_BEGIN_MAYBE_DISABLE(PerformanceTiming, nsIDOMPerformanceTiming,
+                                        !nsGlobalWindow::HasPerformanceSupport())
+    DOM_CLASSINFO_MAP_ENTRY(nsIDOMPerformanceTiming)
+  DOM_CLASSINFO_MAP_END
+
+  DOM_CLASSINFO_MAP_BEGIN_MAYBE_DISABLE(PerformanceNavigation, nsIDOMPerformanceNavigation,
+                                        !nsGlobalWindow::HasPerformanceSupport())
+    DOM_CLASSINFO_MAP_ENTRY(nsIDOMPerformanceNavigation)
+  DOM_CLASSINFO_MAP_END
+
+  DOM_CLASSINFO_MAP_BEGIN_MAYBE_DISABLE(Performance, nsIDOMPerformance,
+                                        !nsGlobalWindow::HasPerformanceSupport())
+    DOM_CLASSINFO_MAP_ENTRY(nsIDOMPerformance)
+  DOM_CLASSINFO_MAP_END
+
   DOM_CLASSINFO_MAP_BEGIN(Screen, nsIDOMScreen)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMScreen)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(DOMPrototype, nsIDOMDOMConstructor)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMDOMConstructor)
   DOM_CLASSINFO_MAP_END
 
--- a/dom/base/nsDOMClassInfoClasses.h
+++ b/dom/base/nsDOMClassInfoClasses.h
@@ -40,16 +40,19 @@ DOMCI_CLASS(Window)
 DOMCI_CLASS(Location)
 DOMCI_CLASS(Navigator)
 DOMCI_CLASS(Plugin)
 DOMCI_CLASS(PluginArray)
 DOMCI_CLASS(MimeType)
 DOMCI_CLASS(MimeTypeArray)
 DOMCI_CLASS(BarProp)
 DOMCI_CLASS(History)
+DOMCI_CLASS(PerformanceTiming)
+DOMCI_CLASS(PerformanceNavigation)
+DOMCI_CLASS(Performance)
 DOMCI_CLASS(Screen)
 DOMCI_CLASS(DOMPrototype)
 DOMCI_CLASS(DOMConstructor)
 
 // Core classes
 DOMCI_CLASS(XMLDocument)
 DOMCI_CLASS(DocumentType)
 DOMCI_CLASS(DOMImplementation)
new file mode 100644
--- /dev/null
+++ b/dom/base/nsDOMNavigationTiming.cpp
@@ -0,0 +1,420 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** 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 implementation of Web Timing draft specification
+ * http://dev.w3.org/2006/webapi/WebTiming/
+ *
+ * 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):
+ *   Sergey Novikov <sergeyn@google.com> (original author)
+ *   Igor Bazarny <igor.bazarny@gmail.com> (lots of improvements)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of 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 "nsDOMNavigationTiming.h"
+#include "nsCOMPtr.h"
+#include "nscore.h"
+#include "TimeStamp.h"
+#include "nsContentUtils.h"
+
+#include "nsIDOMEventTarget.h"
+#include "nsIDocument.h"
+#include "nsIScriptSecurityManager.h"
+
+nsDOMNavigationTiming::nsDOMNavigationTiming()
+{
+  Clear();
+}
+
+nsDOMNavigationTiming::~nsDOMNavigationTiming()
+{
+}
+
+void
+nsDOMNavigationTiming::Clear()
+{
+  mNavigationType = nsIDOMPerformanceNavigation::TYPE_RESERVED;
+  mNavigationStart = 0;
+  mFetchStart = 0;
+  mRedirectStart = 0;
+  mRedirectEnd = 0;
+  mBeforeUnloadStart = 0;
+  mUnloadStart = 0;
+  mUnloadEnd = 0;
+  mLoadEventStart = 0;
+  mLoadEventEnd = 0;
+  mDOMLoading = 0;
+  mDOMInteractive = 0;
+  mDOMContentLoadedEventStart = 0;
+  mDOMContentLoadedEventEnd = 0;
+  mDOMComplete = 0;
+  mRedirectCheck = NOT_CHECKED;
+}
+
+DOMTimeMilliSec nsDOMNavigationTiming::DurationFromStart(){
+  mozilla::TimeDuration duration = mozilla::TimeStamp::Now() - mNavigationStartTimeStamp;
+  return mNavigationStart + (int)(duration.ToMilliseconds() + 0.5);
+}
+
+void
+nsDOMNavigationTiming::NotifyNavigationStart()
+{
+  mNavigationStart = PR_Now() / PR_USEC_PER_MSEC;
+  mNavigationStartTimeStamp = mozilla::TimeStamp::Now();
+}
+
+void
+nsDOMNavigationTiming::NotifyFetchStart(nsIURI* aURI, nsDOMPerformanceNavigationType aNavigationType)
+{
+  mFetchStart = DurationFromStart();
+  mNavigationType = aNavigationType;
+  // At the unload event time we don't really know the loading uri.
+  // Need it for later check for unload timing access.
+  mLoadedURI = aURI;
+}
+
+void
+nsDOMNavigationTiming::NotifyRedirect(nsIURI* aOldURI, nsIURI* aNewURI)
+{
+  if (mRedirects.Count() == 0) {
+    mRedirectStart = mFetchStart;
+  }
+  mFetchStart = DurationFromStart();
+  mRedirectEnd = mFetchStart;
+
+  // At the unload event time we don't really know the loading uri.
+  // Need it for later check for unload timing access.
+  mLoadedURI = aNewURI;
+
+  mRedirects.AppendObject(aOldURI);
+}
+
+void
+nsDOMNavigationTiming::NotifyBeforeUnload()
+{
+  mBeforeUnloadStart = DurationFromStart();
+}
+
+void
+nsDOMNavigationTiming::NotifyUnloadAccepted(nsIURI* aOldURI)
+{
+  mUnloadStart = mBeforeUnloadStart;
+  mUnloadedURI = aOldURI;
+}
+
+void
+nsDOMNavigationTiming::NotifyUnloadEventStart()
+{
+  mUnloadStart = DurationFromStart();
+}
+
+void
+nsDOMNavigationTiming::NotifyUnloadEventEnd()
+{
+  mUnloadEnd = DurationFromStart();
+}
+
+void
+nsDOMNavigationTiming::NotifyLoadEventStart()
+{
+  mLoadEventStart = DurationFromStart();
+}
+
+void
+nsDOMNavigationTiming::NotifyLoadEventEnd()
+{
+  mLoadEventEnd = DurationFromStart();
+}
+
+PRBool
+nsDOMNavigationTiming::ReportRedirects()
+{
+  if (mRedirectCheck == NOT_CHECKED) {
+    if (mRedirects.Count() == 0) {
+      mRedirectCheck = NO_REDIRECTS;
+    } else {
+      mRedirectCheck = CHECK_PASSED;
+      nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
+      for (int i = mRedirects.Count() - 1; i >= 0; --i) {
+        nsIURI * curr = mRedirects[i];
+        nsresult rv = ssm->CheckSameOriginURI(curr, mLoadedURI, PR_FALSE);
+        if (!NS_SUCCEEDED(rv)) {
+          mRedirectCheck = CHECK_FAILED;
+          break;
+        }
+      }
+      // All we need to know is in mRedirectCheck now. Clear history.
+      mRedirects.Clear();
+    }
+  }
+  return mRedirectCheck == CHECK_PASSED;
+}
+
+void
+nsDOMNavigationTiming::SetDOMLoadingTimeStamp(nsIURI* aURI, mozilla::TimeStamp aValue)
+{
+  mLoadedURI = aURI;
+  mozilla::TimeDuration duration = aValue - mNavigationStartTimeStamp;
+  mDOMLoading = mNavigationStart + (int)(duration.ToMilliseconds() + 0.5);
+}
+
+void
+nsDOMNavigationTiming::NotifyDOMLoading(nsIURI* aURI)
+{
+  mLoadedURI = aURI;
+  mDOMLoading = DurationFromStart();
+}
+
+void
+nsDOMNavigationTiming::NotifyDOMInteractive(nsIURI* aURI)
+{
+  mLoadedURI = aURI;
+  mDOMInteractive = DurationFromStart();
+}
+
+void
+nsDOMNavigationTiming::NotifyDOMComplete(nsIURI* aURI)
+{
+  mLoadedURI = aURI;
+  mDOMComplete = DurationFromStart();
+}
+
+void
+nsDOMNavigationTiming::NotifyDOMContentLoadedStart(nsIURI* aURI)
+{
+  mLoadedURI = aURI;
+  mDOMContentLoadedEventStart = DurationFromStart();
+}
+
+void
+nsDOMNavigationTiming::NotifyDOMContentLoadedEnd(nsIURI* aURI)
+{
+  mLoadedURI = aURI;
+  mDOMContentLoadedEventEnd = DurationFromStart();
+}
+
+
+NS_IMPL_ADDREF(nsDOMNavigationTiming)
+NS_IMPL_RELEASE(nsDOMNavigationTiming)
+
+// QueryInterface implementation for nsDOMNavigationTiming
+NS_INTERFACE_MAP_BEGIN(nsDOMNavigationTiming)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMPerformanceTiming)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMPerformanceTiming)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMPerformanceNavigation)
+NS_INTERFACE_MAP_END
+
+NS_IMETHODIMP
+nsDOMNavigationTiming::GetType(
+    nsDOMPerformanceNavigationType* aNavigationType)
+{
+  *aNavigationType = mNavigationType;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMNavigationTiming::GetRedirectCount(PRUint16* aRedirectCount)
+{
+  *aRedirectCount = 0;
+  if (ReportRedirects()) {
+    *aRedirectCount = mRedirects.Count();
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMNavigationTiming::GetRedirectStart(DOMTimeMilliSec* aRedirectStart)
+{
+  *aRedirectStart = 0;
+  if (ReportRedirects()) {
+    *aRedirectStart = mRedirectStart;
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMNavigationTiming::GetRedirectEnd(DOMTimeMilliSec* aEnd)
+{
+  *aEnd = 0;
+  if (ReportRedirects()) {
+    *aEnd = mRedirectEnd;
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMNavigationTiming::GetNavigationStart(DOMTimeMilliSec* aNavigationStart)
+{
+  *aNavigationStart = mNavigationStart;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMNavigationTiming::GetUnloadEventStart(DOMTimeMilliSec* aStart)
+{
+  *aStart = 0;
+  nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
+  nsresult rv = ssm->CheckSameOriginURI(mLoadedURI, mUnloadedURI, PR_FALSE);
+  if (NS_SUCCEEDED(rv)) {
+    *aStart = mUnloadStart;
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMNavigationTiming::GetUnloadEventEnd(DOMTimeMilliSec* aEnd)
+{
+  *aEnd = 0;
+  nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
+  nsresult rv = ssm->CheckSameOriginURI(mLoadedURI, mUnloadedURI, PR_FALSE);
+  if (NS_SUCCEEDED(rv)) {
+    *aEnd = mUnloadEnd;
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMNavigationTiming::GetFetchStart(DOMTimeMilliSec* aStart)
+{
+  *aStart = mFetchStart;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMNavigationTiming::GetDomainLookupStart(DOMTimeMilliSec* aStart)
+{
+  // TODO: Implement me! (bug 659126)
+  *aStart = mFetchStart;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMNavigationTiming::GetDomainLookupEnd(DOMTimeMilliSec* aEnd)
+{
+  // TODO: Implement me! (bug 659126)
+  *aEnd = mFetchStart;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMNavigationTiming::GetConnectStart(DOMTimeMilliSec* aStart)
+{
+  // TODO: Implement me! (bug 659126)
+  *aStart = mFetchStart;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMNavigationTiming::GetConnectEnd(DOMTimeMilliSec* aEnd)
+{
+  // TODO: Implement me! (bug 659126)
+  *aEnd = mFetchStart;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMNavigationTiming::GetHandshakeStart(DOMTimeMilliSec* aStart)
+{
+  // TODO: Implement me! (bug 659126)
+  *aStart = mFetchStart;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMNavigationTiming::GetRequestStart(DOMTimeMilliSec* aStart)
+{
+  // TODO: Implement me! (bug 659126)
+  *aStart = mFetchStart;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMNavigationTiming::GetResponseStart(DOMTimeMilliSec* aStart)
+{
+  // TODO: Implement me! (bug 659126)
+  *aStart = mFetchStart;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMNavigationTiming::GetResponseEnd(DOMTimeMilliSec* aEnd)
+{
+  // TODO: Implement me! (bug 659126)
+  *aEnd = mFetchStart;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMNavigationTiming::GetDomLoading(DOMTimeMilliSec* aTime)
+{
+  *aTime = mDOMLoading;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMNavigationTiming::GetDomInteractive(DOMTimeMilliSec* aTime)
+{
+  *aTime = mDOMInteractive;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMNavigationTiming::GetDomContentLoadedEventStart(DOMTimeMilliSec* aStart)
+{
+  *aStart = mDOMContentLoadedEventStart;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMNavigationTiming::GetDomContentLoadedEventEnd(DOMTimeMilliSec* aEnd)
+{
+  *aEnd = mDOMContentLoadedEventEnd;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMNavigationTiming::GetDomComplete(DOMTimeMilliSec* aTime)
+{
+  *aTime = mDOMComplete;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMNavigationTiming::GetLoadEventStart(DOMTimeMilliSec* aStart)
+{
+  *aStart = mLoadEventStart;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMNavigationTiming::GetLoadEventEnd(DOMTimeMilliSec* aEnd)
+{
+  *aEnd = mLoadEventEnd;
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/dom/base/nsDOMNavigationTiming.h
@@ -0,0 +1,121 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** 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 implementation of Web Timing draft specification
+ * http://dev.w3.org/2006/webapi/WebTiming/
+ *
+ * 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):
+ *   Sergey Novikov <sergeyn@google.com> (original author)
+ *   Igor Bazarny <igor.bazarny@gmail.com> (lots of improvements)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of 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 nsDOMNavigationTiming_h___
+#define nsDOMNavigationTiming_h___
+
+#include "nsIDOMPerformanceTiming.h"
+#include "nsIDOMPerformanceNavigation.h"
+#include "nscore.h"
+#include "nsCOMPtr.h"
+#include "nsCOMArray.h"
+#include "TimeStamp.h"
+
+class nsDOMNavigationTimingClock;
+class nsIURI;
+class nsIDocument;
+
+class nsDOMNavigationTiming : public nsIDOMPerformanceTiming,
+                              public nsIDOMPerformanceNavigation
+{
+public:
+  nsDOMNavigationTiming();
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMPERFORMANCETIMING
+  NS_DECL_NSIDOMPERFORMANCENAVIGATION
+
+  void NotifyNavigationStart();
+  void NotifyFetchStart(nsIURI* aURI, nsDOMPerformanceNavigationType aNavigationType);
+  void NotifyRedirect(nsIURI* aOldURI, nsIURI* aNewURI);
+  void NotifyBeforeUnload();
+  void NotifyUnloadAccepted(nsIURI* aOldURI);
+  void NotifyUnloadEventStart();
+  void NotifyUnloadEventEnd();
+  void NotifyLoadEventStart();
+  void NotifyLoadEventEnd();
+
+  // Document changes state to 'loading' before connecting to timing
+  void SetDOMLoadingTimeStamp(nsIURI* aURI, mozilla::TimeStamp aValue);
+  void NotifyDOMLoading(nsIURI* aURI);
+  void NotifyDOMInteractive(nsIURI* aURI);
+  void NotifyDOMComplete(nsIURI* aURI);
+  void NotifyDOMContentLoadedStart(nsIURI* aURI);
+  void NotifyDOMContentLoadedEnd(nsIURI* aURI);
+
+private:
+  nsDOMNavigationTiming(const nsDOMNavigationTiming &){};
+  ~nsDOMNavigationTiming();
+
+  void Clear();
+  PRBool ReportRedirects();
+
+  nsCOMPtr<nsIURI> mUnloadedURI;
+  nsCOMPtr<nsIURI> mLoadedURI;
+  nsCOMArray<nsIURI> mRedirects;
+
+  typedef enum { NOT_CHECKED,
+                 CHECK_PASSED,
+                 NO_REDIRECTS,
+                 CHECK_FAILED} RedirectCheckState;
+  RedirectCheckState mRedirectCheck;
+
+  nsDOMPerformanceNavigationType mNavigationType;
+  DOMTimeMilliSec mNavigationStart;
+  mozilla::TimeStamp mNavigationStartTimeStamp;
+  DOMTimeMilliSec DurationFromStart();
+
+  DOMTimeMilliSec mFetchStart;
+  DOMTimeMilliSec mRedirectStart;
+  DOMTimeMilliSec mRedirectEnd;
+  DOMTimeMilliSec mBeforeUnloadStart;
+  DOMTimeMilliSec mUnloadStart;
+  DOMTimeMilliSec mUnloadEnd;
+  DOMTimeMilliSec mNavigationEnd;
+  DOMTimeMilliSec mLoadEventStart;
+  DOMTimeMilliSec mLoadEventEnd;
+
+  DOMTimeMilliSec mDOMLoading;
+  DOMTimeMilliSec mDOMInteractive;
+  DOMTimeMilliSec mDOMContentLoadedEventStart;
+  DOMTimeMilliSec mDOMContentLoadedEventEnd;
+  DOMTimeMilliSec mDOMComplete;
+};
+
+#endif /* nsDOMNavigationTiming_h___ */
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -47,16 +47,18 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "base/basictypes.h"
 
 // Local Includes
 #include "nsGlobalWindow.h"
 #include "nsScreen.h"
 #include "nsHistory.h"
+#include "nsPerformance.h"
+#include "nsDOMNavigationTiming.h"
 #include "nsBarProps.h"
 #include "nsDOMStorage.h"
 #include "nsDOMOfflineResourceList.h"
 #include "nsDOMError.h"
 
 // Helper Classes
 #include "nsXPIDLString.h"
 #include "nsJSUtils.h"
@@ -1112,16 +1114,17 @@ nsGlobalWindow::CleanUp(PRBool aIgnoreMo
   mScrollbars = nsnull;
   mLocation = nsnull;
   mHistory = nsnull;
   mFrames = nsnull;
   mApplicationCache = nsnull;
   mIndexedDB = nsnull;
   mPendingStorageEventsObsolete = nsnull;
 
+  mPerformance = nsnull;
 
   ClearControllers();
 
   mOpener = nsnull;             // Forces Release
   if (mContext) {
 #ifdef DEBUG
     nsCycleCollector_DEBUG_shouldBeFreed(mContext);
 #endif
@@ -1336,16 +1339,17 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY(nsIDOM3EventTarget)
   NS_INTERFACE_MAP_ENTRY(nsIDOMNSEventTarget)
   NS_INTERFACE_MAP_ENTRY(nsPIDOMWindow)
   NS_INTERFACE_MAP_ENTRY(nsIDOMStorageWindow)
   NS_INTERFACE_MAP_ENTRY(nsIDOMStorageIndexedDB)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
   NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
   NS_INTERFACE_MAP_ENTRY(nsIDOMWindow_2_0_BRANCH)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMWindowPerformance)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Window)
   OUTER_WINDOW_ONLY
     NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   END_OUTER_WINDOW_ONLY
 NS_INTERFACE_MAP_END
 
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGlobalWindow)
@@ -2715,17 +2719,16 @@ nsGlobalWindow::PostHandleEvent(nsEventC
       nsEvent event(NS_IS_TRUSTED_EVENT(aVisitor.mEvent), NS_LOAD);
       event.flags |= NS_EVENT_FLAG_CANT_BUBBLE;
 
       // Most of the time we could get a pres context to pass in here,
       // but not always (i.e. if this window is not shown there won't
       // be a pres context available). Since we're not firing a GUI
       // event we don't need a pres context anyway so we just pass
       // null as the pres context all the time here.
-
       nsEventDispatcher::Dispatch(content, nsnull, &event, nsnull, &status);
     }
   }
 
   return NS_OK;
 }
 
 nsresult
@@ -2946,16 +2949,35 @@ nsGlobalWindow::GetHistory(nsIDOMHistory
     }
   }
 
   NS_IF_ADDREF(*aHistory = mHistory);
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsGlobalWindow::GetPerformance(nsIDOMPerformance** aPerformance)
+{
+  FORWARD_TO_INNER(GetPerformance, (aPerformance), NS_ERROR_NOT_INITIALIZED);
+
+  *aPerformance = nsnull;
+
+  if (nsGlobalWindow::HasPerformanceSupport()) {
+    if (!mPerformance) {
+      nsRefPtr<nsDOMNavigationTiming> timing = mDoc->GetNavigationTiming();
+      if (timing) {
+        mPerformance = new nsPerformance(timing);
+      }
+    }
+    NS_IF_ADDREF(*aPerformance = mPerformance);
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsGlobalWindow::GetParent(nsIDOMWindow** aParent)
 {
   FORWARD_TO_OUTER(GetParent, (aParent), NS_ERROR_NOT_INITIALIZED);
 
   *aParent = nsnull;
   if (!mDocShell)
     return NS_OK;
 
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -133,16 +133,17 @@ class nsIDOMEvent;
 class nsIScrollableFrame;
 class nsIControllers;
 
 class nsBarProp;
 class nsLocation;
 class nsNavigator;
 class nsScreen;
 class nsHistory;
+class nsPerformance;
 class nsIDocShellLoadInfo;
 class WindowStateHolder;
 class nsGlobalWindowObserver;
 class nsGlobalWindow;
 class nsDummyJavaPluginOwner;
 class PostMessageEvent;
 class nsRunnable;
 
@@ -280,17 +281,18 @@ class nsGlobalWindow : public nsPIDOMWin
                        public nsIDOM3EventTarget,
                        public nsIDOMNSEventTarget,
                        public nsIDOMStorageWindow,
                        public nsIDOMStorageIndexedDB,
                        public nsSupportsWeakReference,
                        public nsIInterfaceRequestor,
                        public nsIDOMWindow_2_0_BRANCH,
                        public nsWrapperCache,
-                       public PRCListStr
+                       public PRCListStr,
+                       public nsIDOMWindowPerformance
 {
 public:
   friend class nsDOMMozURLProperty;
 
   typedef mozilla::TimeStamp TimeStamp;
   typedef mozilla::TimeDuration TimeDuration;
 
   // public methods
@@ -328,16 +330,19 @@ public:
   NS_DECL_NSIDOMWINDOW
 
   // nsIDOMWindow2
   NS_DECL_NSIDOMWINDOW2
 
   // nsIDOMWindowInternal
   NS_DECL_NSIDOMWINDOWINTERNAL
 
+  // nsIDOMWindowPerformance
+  NS_DECL_NSIDOMWINDOWPERFORMANCE
+
   // nsIDOMJSWindow
   NS_DECL_NSIDOMJSWINDOW
 
   // nsIDOMEventTarget
   NS_DECL_NSIDOMEVENTTARGET
 
   // nsIDOM3EventTarget
   NS_DECL_NSIDOM3EVENTTARGET
@@ -567,16 +572,24 @@ public:
   static nsGlobalWindow* GetOuterWindowWithId(PRUint64 aWindowID) {
     return sOuterWindowsById ? sOuterWindowsById->Get(aWindowID) : nsnull;
   }
 
   static bool HasIndexedDBSupport() {
     return nsContentUtils::GetBoolPref("indexedDB.feature.enabled", PR_TRUE);
   }
 
+  static bool HasPerformanceSupport() {
+#ifdef DEBUG
+    return nsContentUtils::GetBoolPref("dom.enable_performance", PR_TRUE);
+#else
+    return nsContentUtils::GetBoolPref("dom.enable_performance", PR_FALSE);
+#endif
+  }
+
 private:
   // Enable updates for the accelerometer.
   void EnableAccelerationUpdates();
 
   // Disables updates for the accelerometer.
   void DisableAccelerationUpdates();
 
 protected:
@@ -901,16 +914,17 @@ protected:
   nsCOMPtr<nsIScriptContext>    mContext;
   nsWeakPtr                     mOpener;
   nsCOMPtr<nsIControllers>      mControllers;
   nsCOMPtr<nsIArray>            mArguments;
   nsCOMPtr<nsIArray>            mArgumentsLast;
   nsCOMPtr<nsIPrincipal>        mArgumentsOrigin;
   nsRefPtr<nsNavigator>         mNavigator;
   nsRefPtr<nsScreen>            mScreen;
+  nsRefPtr<nsPerformance>       mPerformance;
   nsRefPtr<nsDOMWindowList>     mFrames;
   nsRefPtr<nsBarProp>           mMenubar;
   nsRefPtr<nsBarProp>           mToolbar;
   nsRefPtr<nsBarProp>           mLocationbar;
   nsRefPtr<nsBarProp>           mPersonalbar;
   nsRefPtr<nsBarProp>           mStatusbar;
   nsRefPtr<nsBarProp>           mScrollbars;
   nsCOMPtr<nsIWeakReference>    mWindowUtils;
new file mode 100644
--- /dev/null
+++ b/dom/base/nsPerformance.cpp
@@ -0,0 +1,276 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** 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 implementation of Web Timing draft specification
+ * http://dev.w3.org/2006/webapi/WebTiming/
+ *
+ * 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):
+ *   Sergey Novikov <sergeyn@google.com> (original author)
+ *   Igor Bazarny <igor.bazarny@gmail.com> (update to match bearly-final spec)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of 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 "nsPerformance.h"
+#include "nsCOMPtr.h"
+#include "nscore.h"
+#include "nsIDocShell.h"
+#include "nsDOMClassInfo.h"
+#include "nsDOMNavigationTiming.h"
+
+DOMCI_DATA(PerformanceTiming, nsPerformanceTiming)
+
+NS_IMPL_ADDREF(nsPerformanceTiming)
+NS_IMPL_RELEASE(nsPerformanceTiming)
+
+// QueryInterface implementation for nsPerformanceTiming
+NS_INTERFACE_MAP_BEGIN(nsPerformanceTiming)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMPerformanceTiming)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMPerformanceTiming)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(PerformanceTiming)
+NS_INTERFACE_MAP_END
+
+nsPerformanceTiming::nsPerformanceTiming(nsDOMNavigationTiming* aData)
+{
+  NS_ASSERTION(aData, "Timing data should be provided");
+  mData = aData;
+}
+
+nsPerformanceTiming::~nsPerformanceTiming()
+{
+}
+
+NS_IMETHODIMP
+nsPerformanceTiming::GetNavigationStart(DOMTimeMilliSec* aTime)
+{
+  return mData->GetNavigationStart(aTime);
+}
+
+NS_IMETHODIMP
+nsPerformanceTiming::GetUnloadEventStart(DOMTimeMilliSec* aTime)
+{
+  return mData->GetUnloadEventStart(aTime);
+}
+
+NS_IMETHODIMP
+nsPerformanceTiming::GetUnloadEventEnd(DOMTimeMilliSec* aTime)
+{
+  return mData->GetUnloadEventEnd(aTime);
+}
+
+NS_IMETHODIMP
+nsPerformanceTiming::GetRedirectStart(DOMTimeMilliSec* aTime)
+{
+  return mData->GetRedirectStart(aTime);
+}
+
+NS_IMETHODIMP
+nsPerformanceTiming::GetRedirectEnd(DOMTimeMilliSec* aTime)
+{
+  return mData->GetRedirectEnd(aTime);
+}
+
+NS_IMETHODIMP
+nsPerformanceTiming::GetFetchStart(DOMTimeMilliSec* aTime)
+{
+  return mData->GetFetchStart(aTime);
+}
+
+NS_IMETHODIMP
+nsPerformanceTiming::GetDomainLookupStart(DOMTimeMilliSec* aTime)
+{
+  return mData->GetDomainLookupStart(aTime);
+}
+
+NS_IMETHODIMP
+nsPerformanceTiming::GetDomainLookupEnd(DOMTimeMilliSec* aTime)
+{
+  return mData->GetDomainLookupEnd(aTime);
+}
+
+NS_IMETHODIMP
+nsPerformanceTiming::GetConnectStart(DOMTimeMilliSec* aTime)
+{
+  return mData->GetConnectStart(aTime);
+}
+
+NS_IMETHODIMP
+nsPerformanceTiming::GetConnectEnd(DOMTimeMilliSec* aTime)
+{
+  return mData->GetConnectEnd(aTime);
+}
+
+NS_IMETHODIMP
+nsPerformanceTiming::GetHandshakeStart(DOMTimeMilliSec* aTime)
+{
+  return mData->GetHandshakeStart(aTime);
+}
+
+NS_IMETHODIMP
+nsPerformanceTiming::GetRequestStart(DOMTimeMilliSec* aTime)
+{
+  return mData->GetRequestStart(aTime);
+}
+
+NS_IMETHODIMP
+nsPerformanceTiming::GetResponseStart(DOMTimeMilliSec* aTime)
+{
+  return mData->GetResponseStart(aTime);
+}
+
+NS_IMETHODIMP
+nsPerformanceTiming::GetResponseEnd(DOMTimeMilliSec* aTime)
+{
+  return mData->GetResponseEnd(aTime);
+}
+
+NS_IMETHODIMP
+nsPerformanceTiming::GetDomLoading(DOMTimeMilliSec* aTime)
+{
+  return mData->GetDomLoading(aTime);
+}
+
+NS_IMETHODIMP
+nsPerformanceTiming::GetDomInteractive(DOMTimeMilliSec* aTime)
+{
+  return mData->GetDomInteractive(aTime);
+}
+
+NS_IMETHODIMP
+nsPerformanceTiming::GetDomContentLoadedEventStart(DOMTimeMilliSec* aTime)
+{
+  return mData->GetDomContentLoadedEventStart(aTime);
+}
+
+NS_IMETHODIMP
+nsPerformanceTiming::GetDomContentLoadedEventEnd(DOMTimeMilliSec* aTime)
+{
+  return mData->GetDomContentLoadedEventEnd(aTime);
+}
+
+NS_IMETHODIMP
+nsPerformanceTiming::GetDomComplete(DOMTimeMilliSec* aTime)
+{
+  return mData->GetDomComplete(aTime);
+}
+
+NS_IMETHODIMP
+nsPerformanceTiming::GetLoadEventStart(DOMTimeMilliSec* aTime)
+{
+  return mData->GetLoadEventStart(aTime);
+}
+
+NS_IMETHODIMP
+nsPerformanceTiming::GetLoadEventEnd(DOMTimeMilliSec* aTime)
+{
+  return mData->GetLoadEventEnd(aTime);
+}
+
+
+
+DOMCI_DATA(PerformanceNavigation, nsPerformanceNavigation)
+
+NS_IMPL_ADDREF(nsPerformanceNavigation)
+NS_IMPL_RELEASE(nsPerformanceNavigation)
+
+// QueryInterface implementation for nsPerformanceNavigation
+NS_INTERFACE_MAP_BEGIN(nsPerformanceNavigation)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMPerformanceNavigation)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMPerformanceNavigation)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(PerformanceNavigation)
+NS_INTERFACE_MAP_END
+
+nsPerformanceNavigation::nsPerformanceNavigation(nsDOMNavigationTiming* aData)
+{
+  NS_ASSERTION(aData, "Timing data should be provided");
+  mData = aData;
+}
+
+nsPerformanceNavigation::~nsPerformanceNavigation()
+{
+}
+
+NS_IMETHODIMP
+nsPerformanceNavigation::GetType(
+    nsDOMPerformanceNavigationType* aNavigationType)
+{
+  return mData->GetType(aNavigationType);
+}
+
+NS_IMETHODIMP
+nsPerformanceNavigation::GetRedirectCount(PRUint16* aRedirectCount)
+{
+  return mData->GetRedirectCount(aRedirectCount);
+}
+
+
+DOMCI_DATA(Performance, nsPerformance)
+
+NS_IMPL_ADDREF(nsPerformance)
+NS_IMPL_RELEASE(nsPerformance)
+
+nsPerformance::nsPerformance(nsDOMNavigationTiming* aTiming)
+{
+  NS_ASSERTION(aTiming, "Timing data should be provided");
+  mData = aTiming;
+}
+
+nsPerformance::~nsPerformance()
+{
+}
+
+// QueryInterface implementation for nsPerformance
+NS_INTERFACE_MAP_BEGIN(nsPerformance)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMPerformance)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMPerformance)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Performance)
+NS_INTERFACE_MAP_END
+
+//
+// nsIDOMPerformance methods
+//
+NS_IMETHODIMP
+nsPerformance::GetTiming(nsIDOMPerformanceTiming** aTiming)
+{
+  if (!mTiming) {
+    mTiming = new nsPerformanceTiming(mData);
+  }
+  NS_IF_ADDREF(*aTiming = mTiming);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPerformance::GetNavigation(nsIDOMPerformanceNavigation** aNavigation)
+{
+  if (!mNavigation) {
+    mNavigation = new nsPerformanceNavigation(mData);
+  }
+  NS_IF_ADDREF(*aNavigation = mNavigation);
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/dom/base/nsPerformance.h
@@ -0,0 +1,95 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** 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 implementation of Web Timing draft specification
+ * http://dev.w3.org/2006/webapi/WebTiming/
+ *
+ * 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):
+ *   Sergey Novikov <sergeyn@google.com> (original author)
+ *   Igor Bazarny <igor.bazarny@gmail.com> (update to match bearly-final spec)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of 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 nsPerformance_h___
+#define nsPerformance_h___
+
+#include "nsIDOMPerformance.h"
+#include "nsIDOMPerformanceTiming.h"
+#include "nsIDOMPerformanceNavigation.h"
+#include "nscore.h"
+#include "nsCOMPtr.h"
+#include "nsAutoPtr.h"
+
+class nsIDocument;
+class nsIURI;
+class nsDOMNavigationTiming;
+
+// Script "performance.timing" object
+class nsPerformanceTiming : public nsIDOMPerformanceTiming
+{
+public:
+  nsPerformanceTiming(nsDOMNavigationTiming* data);
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMPERFORMANCETIMING
+private:
+  ~nsPerformanceTiming();
+  nsRefPtr<nsDOMNavigationTiming> mData;
+};
+
+// Script "performance.navigation" object
+class nsPerformanceNavigation : public nsIDOMPerformanceNavigation
+{
+public:
+  nsPerformanceNavigation(nsDOMNavigationTiming* data);
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMPERFORMANCENAVIGATION
+private:
+  ~nsPerformanceNavigation();
+  nsRefPtr<nsDOMNavigationTiming> mData;
+};
+
+// Script "performance" object
+class nsPerformance : public nsIDOMPerformance
+{
+public:
+  nsPerformance(nsDOMNavigationTiming* timing);
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMPERFORMANCE
+
+private:
+  ~nsPerformance();
+
+  nsRefPtr<nsDOMNavigationTiming> mData;
+  nsCOMPtr<nsIDOMPerformanceTiming> mTiming;
+  nsCOMPtr<nsIDOMPerformanceNavigation> mNavigation;
+};
+
+#endif /* nsPerformance_h___ */
+
--- a/dom/interfaces/base/Makefile.in
+++ b/dom/interfaces/base/Makefile.in
@@ -83,11 +83,14 @@ XPIDLSRCS =					\
         nsIDOMClientRect.idl			\
         nsIDOMClientRectList.idl		\
 	nsIFocusManager.idl			\
 	nsIQueryContentEventResult.idl		\
 	nsITabChild.idl				\
 	nsITabParent.idl			\
 	nsIDOMGlobalPropertyInitializer.idl	\
 	nsIStructuredCloneContainer.idl		\
+	nsIDOMPerformance.idl			\
+	nsIDOMPerformanceTiming.idl		\
+	nsIDOMPerformanceNavigation.idl		\
 	$(NULL)
 
 include $(topsrcdir)/config/rules.mk
--- a/dom/interfaces/base/domstubs.idl
+++ b/dom/interfaces/base/domstubs.idl
@@ -35,16 +35,17 @@
  * 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"
 
 typedef unsigned long long DOMTimeStamp;
+typedef unsigned long long DOMTimeMilliSec;
 
 // Core
 interface nsIDOMAttr;
 interface nsIDOMCDATASection;
 interface nsIDOMCharacterData;
 interface nsIDOMComment;
 interface nsIDOMDOMImplementation;
 interface nsIDOMDocument;
new file mode 100644
--- /dev/null
+++ b/dom/interfaces/base/nsIDOMPerformance.idl
@@ -0,0 +1,50 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** 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 implementation of Web Timing draft specification
+ * http://dev.w3.org/2006/webapi/WebTiming/
+ *
+ * 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):
+ *   Sergey Novikov <sergeyn@google.com> (original author)
+ *   Igor Bazarny <igor.bazarny@gmail.com> (update to match nearly-final spec)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of 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"
+interface nsIDOMPerformanceTiming;
+interface nsIDOMPerformanceNavigation;
+
+[scriptable, uuid(446faf26-000b-4e66-a5fd-ae37c5ed6beb)]
+interface nsIDOMPerformance : nsISupports
+{
+  readonly attribute nsIDOMPerformanceTiming timing;
+  readonly attribute nsIDOMPerformanceNavigation navigation;
+};
+
new file mode 100644
--- /dev/null
+++ b/dom/interfaces/base/nsIDOMPerformanceNavigation.idl
@@ -0,0 +1,55 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** 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 implementation of Web Timing draft specification
+ * http://dev.w3.org/2006/webapi/WebTiming/
+ *
+ * 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):
+ *   Sergey Novikov <sergeyn@google.com> (original author)
+ *   Igor Bazarny <igor.bazarny@gmail.com> (update to match nearly-final spec)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of 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 "domstubs.idl"
+
+typedef unsigned short nsDOMPerformanceNavigationType;
+
+[scriptable, uuid(a2132ad8-a841-4285-a140-338e8de6c2e0)]
+interface nsIDOMPerformanceNavigation : nsISupports
+{
+  const nsDOMPerformanceNavigationType TYPE_NAVIGATE = 0;
+  const nsDOMPerformanceNavigationType TYPE_RELOAD = 1;
+  const nsDOMPerformanceNavigationType TYPE_BACK_FORWARD = 2;
+  const nsDOMPerformanceNavigationType TYPE_RESERVED = 255;
+
+  readonly attribute nsDOMPerformanceNavigationType type;
+  readonly attribute unsigned short redirectCount;
+};
+
new file mode 100644
--- /dev/null
+++ b/dom/interfaces/base/nsIDOMPerformanceTiming.idl
@@ -0,0 +1,67 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** 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 implementation of Web Timing draft specification
+ * http://dev.w3.org/2006/webapi/WebTiming/
+ *
+ * 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):
+ *   Sergey Novikov <sergeyn@google.com> (original author)
+ *   Igor Bazarny <igor.bazarny@gmail.com> (update to match nearly-final spec)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of 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 "domstubs.idl"
+
+[scriptable, uuid(2a630b50-61b6-41b3-996d-70be67fbbcb0)]
+interface nsIDOMPerformanceTiming : nsISupports
+{
+  readonly attribute DOMTimeMilliSec navigationStart;
+  readonly attribute DOMTimeMilliSec unloadEventStart;
+  readonly attribute DOMTimeMilliSec unloadEventEnd;
+  readonly attribute DOMTimeMilliSec redirectStart;
+  readonly attribute DOMTimeMilliSec redirectEnd;
+  readonly attribute DOMTimeMilliSec fetchStart;
+  readonly attribute DOMTimeMilliSec domainLookupStart;
+  readonly attribute DOMTimeMilliSec domainLookupEnd;
+  readonly attribute DOMTimeMilliSec connectStart;
+  readonly attribute DOMTimeMilliSec connectEnd;
+  readonly attribute DOMTimeMilliSec handshakeStart;
+  readonly attribute DOMTimeMilliSec requestStart;
+  readonly attribute DOMTimeMilliSec responseStart;
+  readonly attribute DOMTimeMilliSec responseEnd;
+  readonly attribute DOMTimeMilliSec domLoading;
+  readonly attribute DOMTimeMilliSec domInteractive;
+  readonly attribute DOMTimeMilliSec domContentLoadedEventStart;
+  readonly attribute DOMTimeMilliSec domContentLoadedEventEnd;
+  readonly attribute DOMTimeMilliSec domComplete;
+  readonly attribute DOMTimeMilliSec loadEventStart;
+  readonly attribute DOMTimeMilliSec loadEventEnd;
+};
+
--- a/dom/interfaces/base/nsIDOMWindowInternal.idl
+++ b/dom/interfaces/base/nsIDOMWindowInternal.idl
@@ -41,21 +41,22 @@
 
 %{ C++
 #include "jspubtd.h"
 %}
 
 interface nsIPrompt;
 interface nsIControllers;
 interface nsIDOMLocation;
+interface nsIDOMPerformance;
 interface nsIVariant;
 interface nsIAnimationFrameListener;
 interface nsIDOMMediaQueryList;
 
-[scriptable, uuid(3a7b0839-b9d6-42ff-8ba6-910aba60a966)]
+[scriptable, uuid(5930f197-259e-4f6b-aeca-c96a74518cc6)]
 interface nsIDOMWindowInternal : nsIDOMWindow2
 {
   readonly attribute nsIDOMWindowInternal        window;
 
   /* [replaceable] self */
   readonly attribute nsIDOMWindowInternal        self;
 
   readonly attribute nsIDOMNavigator             navigator;
@@ -251,8 +252,18 @@ interface nsIDOMMozURLProperty : nsISupp
   void revokeObjectURL(in DOMString URL);
 };
 
 [scriptable, uuid(05563c0c-b74c-41ad-91d1-bc22d580a581)]
 interface nsIDOMWindow_2_0_BRANCH : nsISupports
 {
   readonly attribute nsIDOMMozURLProperty URL;
 };
+
+[scriptable, uuid(2146c906-57f7-486c-a1b4-8cdb57ef577f)]
+interface nsIDOMWindowPerformance : nsISupports
+{
+  /**
+   * A namespace to hold performance related data and statistics.
+   */
+  readonly attribute nsIDOMPerformance performance;
+};
+
--- a/js/src/xpconnect/src/Makefile.in
+++ b/js/src/xpconnect/src/Makefile.in
@@ -116,16 +116,17 @@ LOCAL_INCLUDES = \
 		-I$(topsrcdir)/caps/include \
 		-I$(topsrcdir)/content/base/src \
 		-I$(topsrcdir)/content/html/content/src \
 		-I$(topsrcdir)/content/html/document/src \
 		-I$(topsrcdir)/content/svg/content/src \
 		-I$(topsrcdir)/layout/style \
 		-I$(topsrcdir)/layout/base \
 		-I$(topsrcdir)/dom/base \
+		-I$(topsrcdir)/xpcom/ds \
 		$(NULL)
 
 EXTRA_DSO_LDOPTS += \
 		$(MOZ_COMPONENT_LIBS) \
 		$(MOZ_JS_LIBS) \
 		$(NULL)
 
 SHARED_LIBRARY_LIBS = \
--- a/layout/base/Makefile.in
+++ b/layout/base/Makefile.in
@@ -160,16 +160,17 @@ LOCAL_INCLUDES += \
 		-I$(srcdir)/../xul/base/src/tree/src \
 		-I$(srcdir)/../../content/base/src \
 		-I$(srcdir)/../../content/events/src \
 		-I$(srcdir)/../../content/xbl/src \
 		-I$(srcdir)/../../view/src \
 		-I$(srcdir)/../../dom/base \
 		-I$(srcdir)/../../content/html/content/src \
 		-I$(srcdir)/../../content/svg/content/src \
+		-I$(topsrcdir)/xpcom/ds \
 		$(NULL)
 
 ifdef MOZ_MATHML
 LOCAL_INCLUDES += \
         -I$(srcdir)/../mathml
 endif
 
 ifdef MOZ_SVG
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -110,16 +110,17 @@
 #include "nsIXULDocument.h"
 #include "nsXULPopupManager.h"
 #endif
 #include "nsPrintfCString.h"
 
 #include "nsIClipboardHelper.h"
 
 #include "nsPIDOMWindow.h"
+#include "nsDOMNavigationTiming.h"
 #include "nsPIWindowRoot.h"
 #include "nsJSEnvironment.h"
 #include "nsFocusManager.h"
 
 #include "nsIScrollableFrame.h"
 #include "nsIHTMLDocument.h"
 #include "nsITimelineService.h"
 #include "nsGfxCIID.h"
@@ -319,16 +320,21 @@ public:
   /**
    * Find the view to use as the container view for MakeWindow. Returns
    * null if this will be the root of a view manager hierarchy. In that
    * case, if mParentWidget is null then this document should not even
    * be displayed.
    */
   virtual nsIView* FindContainerView();
 
+  /**
+   * Set collector for navigation timing data (load, unload events).
+   */
+  virtual void SetNavigationTiming(nsDOMNavigationTiming* timing);
+
   // nsIContentViewerEdit
   NS_DECL_NSICONTENTVIEWEREDIT
 
   // nsIContentViewerFile
   NS_DECL_NSICONTENTVIEWERFILE
 
   // nsIMarkupDocumentViewer
   NS_DECL_NSIMARKUPDOCUMENTVIEWER
@@ -987,16 +993,24 @@ DocumentViewerImpl::InitInternal(nsIWidg
     // MakeWindow())...
 
     rv = InitPresentationStuff(!makeCX);
   }
 
   return rv;
 }
 
+void DocumentViewerImpl::SetNavigationTiming(nsDOMNavigationTiming* timing)
+{
+  NS_ASSERTION(mDocument, "Must have a document to set navigation timing.");
+  if (mDocument) {
+    mDocument->SetNavigationTiming(timing);
+  }
+}
+
 //
 // LoadComplete(aStatus)
 //
 //   aStatus - The status returned from loading the document.
 //
 // This method is called by the container when the document has been
 // completely loaded.
 //
@@ -1048,18 +1062,25 @@ DocumentViewerImpl::LoadComplete(nsresul
     // onload to the document content since that would likely confuse scripts
     // on the page.
 
     nsIDocShell *docShell = window->GetDocShell();
     NS_ENSURE_TRUE(docShell, NS_ERROR_UNEXPECTED);
 
     docShell->GetRestoringDocument(&restoring);
     if (!restoring) {
+      nsRefPtr<nsDOMNavigationTiming> timing(mDocument->GetNavigationTiming());
+      if (timing) {
+        timing->NotifyLoadEventStart();
+      }
       nsEventDispatcher::Dispatch(window, mPresContext, &event, nsnull,
                                   &status);
+      if (timing) {
+        timing->NotifyLoadEventEnd();
+      }
 #ifdef MOZ_TIMELINE
       // if navigator.xul's load is complete, the main nav window is visible
       // mark that point.
 
       nsIURI *uri = mDocument ? mDocument->GetDocumentURI() : nsnull;
 
       if (uri) {
         //printf("DEBUG: getting spec for uri (%p)\n", uri.get());
--- a/layout/base/nsIDocumentViewer.h
+++ b/layout/base/nsIDocumentViewer.h
@@ -43,16 +43,18 @@
 #include "nsIContentViewer.h"
 
 class nsIDocument;
 class nsPresContext;
 class nsIPresShell;
 class nsIStyleSheet;
 class nsIView;
 
+class nsDOMNavigationTiming;
+
 #define NS_IDOCUMENT_VIEWER_IID \
   { 0x5a5c9a1d, 0x49c4, 0x4f3f, \
     { 0x80, 0xcd, 0x12, 0x09, 0x5b, 0x1e, 0x1f, 0x61 } }
 
 /**
  * A document viewer is a kind of content viewer that uses NGLayout
  * to manage the presentation of the content.
  */
@@ -64,13 +66,15 @@ public:
   NS_IMETHOD GetPresShell(nsIPresShell** aResult) = 0;
   
   NS_IMETHOD GetPresContext(nsPresContext** aResult) = 0;
 
   NS_IMETHOD SetDocumentInternal(nsIDocument* aDocument,
                                  PRBool aForceReuseInnerWindow) = 0;
 
   virtual nsIView* FindContainerView() = 0;
+
+  virtual void SetNavigationTiming(nsDOMNavigationTiming* timing) = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIDocumentViewer, NS_IDOCUMENT_VIEWER_IID)
 
 #endif /* nsIDocumentViewer_h___ */