bug 570341 - Initial implementation of web timing specification
authorIgor Bazarny <igor.bazarny@gmail.com>
Mon, 23 May 2011 14:19:36 -0700
changeset 70026 1a645ea075fc9fba940b2c03baef039834ed7e07
parent 70025 85d824b348302ab2bfa153aef4586378a815bc56
child 70027 f7fc3beccae7c00ddf8500fede6c64259669284c
child 70031 c5995eec40f3314b4cb61ce65a5636ca2355cde5
push id20162
push usercbiesinger@gmail.com
push dateMon, 23 May 2011 23:26:59 +0000
treeherdermozilla-central@f7fc3beccae7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs570341
milestone6.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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___ */