Bug 782751 - User Timing API Implementation; r=baku
☠☠ backed out by 70bb61e6791b ☠ ☠
authorKyle Machulis <kyle@nonpolynomial.com>
Thu, 29 Jan 2015 18:04:27 -0800
changeset 240073 c471685170800b1041a1b5243a4578407eb54361
parent 240072 0cdf611d1e9fd8ad39e4bb3b06bbabf70dfbb827
child 240074 e8659e5122e1bbdc8c689ed389449556cdb8a037
push id525
push usermartin.thomson@gmail.com
push dateFri, 30 Jan 2015 21:02:41 +0000
reviewersbaku
bugs782751
milestone38.0a1
Bug 782751 - User Timing API Implementation; r=baku
dom/base/PerformanceEntry.cpp
dom/base/PerformanceEntry.h
dom/base/PerformanceMark.cpp
dom/base/PerformanceMark.h
dom/base/PerformanceMeasure.cpp
dom/base/PerformanceMeasure.h
dom/base/PerformanceResourceTiming.cpp
dom/base/PerformanceResourceTiming.h
dom/base/moz.build
dom/base/nsDOMAttributeMap.cpp
dom/base/nsPerformance.cpp
dom/base/nsPerformance.h
dom/tests/mochitest/general/test_interfaces.html
dom/webidl/Performance.webidl
dom/webidl/PerformanceMark.webidl
dom/webidl/PerformanceMeasure.webidl
dom/webidl/moz.build
modules/libpref/init/all.js
--- a/dom/base/PerformanceEntry.cpp
+++ b/dom/base/PerformanceEntry.cpp
@@ -14,18 +14,22 @@ NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Pe
 NS_IMPL_CYCLE_COLLECTING_ADDREF(PerformanceEntry)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(PerformanceEntry)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PerformanceEntry)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
-PerformanceEntry::PerformanceEntry(nsPerformance* aPerformance)
-: mPerformance(aPerformance)
+PerformanceEntry::PerformanceEntry(nsPerformance* aPerformance,
+                                   const nsAString& aName,
+                                   const nsAString& aEntryType)
+: mPerformance(aPerformance),
+  mName(aName),
+  mEntryType(aEntryType)
 {
   MOZ_ASSERT(aPerformance, "Parent performance object should be provided");
 }
 
 PerformanceEntry::~PerformanceEntry()
 {
 }
 
--- a/dom/base/PerformanceEntry.h
+++ b/dom/base/PerformanceEntry.h
@@ -15,17 +15,19 @@ namespace dom {
 // http://www.w3.org/TR/performance-timeline/#performanceentry
 class PerformanceEntry : public nsISupports,
                          public nsWrapperCache
 {
 protected:
   virtual ~PerformanceEntry();
 
 public:
-  explicit PerformanceEntry(nsPerformance* aPerformance);
+  PerformanceEntry(nsPerformance* aPerformance,
+                   const nsAString& aName,
+                   const nsAString& aEntryType);
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(PerformanceEntry)
 
   virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
   nsPerformance* GetParentObject() const
   {
new file mode 100644
--- /dev/null
+++ b/dom/base/PerformanceMark.cpp
@@ -0,0 +1,27 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "PerformanceMark.h"
+#include "mozilla/dom/PerformanceMarkBinding.h"
+
+using namespace mozilla::dom;
+
+PerformanceMark::PerformanceMark(nsPerformance* aPerformance,
+                                 const nsAString& aName)
+: PerformanceEntry(aPerformance, aName, NS_LITERAL_STRING("mark"))
+{
+  MOZ_ASSERT(aPerformance, "Parent performance object should be provided");
+  mStartTime = aPerformance->GetDOMTiming()->TimeStampToDOMHighRes(mozilla::TimeStamp::Now());
+}
+
+PerformanceMark::~PerformanceMark()
+{
+}
+
+JSObject*
+PerformanceMark::WrapObject(JSContext* aCx)
+{
+  return PerformanceMarkBinding::Wrap(aCx, this);
+}
new file mode 100644
--- /dev/null
+++ b/dom/base/PerformanceMark.h
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_performancemark_h___
+#define mozilla_dom_performancemark_h___
+
+#include "mozilla/dom/PerformanceEntry.h"
+
+namespace mozilla {
+namespace dom {
+
+// http://www.w3.org/TR/user-timing/#performancemark
+class PerformanceMark MOZ_FINAL : public PerformanceEntry
+{
+public:
+  PerformanceMark(nsPerformance* aPerformance,
+                  const nsAString& aName);
+
+  virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
+
+  virtual DOMHighResTimeStamp StartTime() const MOZ_OVERRIDE
+  {
+    return mStartTime;
+  }
+
+protected:
+  virtual ~PerformanceMark();
+  DOMHighResTimeStamp mStartTime;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_performancemark_h___ */
new file mode 100644
--- /dev/null
+++ b/dom/base/PerformanceMeasure.cpp
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "PerformanceMeasure.h"
+#include "mozilla/dom/PerformanceMeasureBinding.h"
+
+using namespace mozilla::dom;
+
+PerformanceMeasure::PerformanceMeasure(nsPerformance* aPerformance,
+                                       const nsAString& aName,
+                                       DOMHighResTimeStamp aStartTime,
+                                       DOMHighResTimeStamp aEndTime)
+: PerformanceEntry(aPerformance, aName, NS_LITERAL_STRING("measure")),
+  mStartTime(aStartTime),
+  mDuration(aEndTime - aStartTime)
+{
+  MOZ_ASSERT(aPerformance, "Parent performance object should be provided");
+}
+
+PerformanceMeasure::~PerformanceMeasure()
+{
+}
+
+JSObject*
+PerformanceMeasure::WrapObject(JSContext* aCx)
+{
+  return PerformanceMeasureBinding::Wrap(aCx, this);
+}
new file mode 100644
--- /dev/null
+++ b/dom/base/PerformanceMeasure.h
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_performancemeasure_h___
+#define mozilla_dom_performancemeasure_h___
+
+#include "mozilla/dom/PerformanceEntry.h"
+
+namespace mozilla {
+namespace dom {
+
+// http://www.w3.org/TR/user-timing/#performancemeasure
+class PerformanceMeasure MOZ_FINAL : public PerformanceEntry
+{
+public:
+  PerformanceMeasure(nsPerformance* aPerformance,
+                     const nsAString& aName,
+                     DOMHighResTimeStamp aStartTime,
+                     DOMHighResTimeStamp aEndTime);
+
+  virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
+
+  virtual DOMHighResTimeStamp StartTime() const MOZ_OVERRIDE
+  {
+    return mStartTime;
+  }
+
+  virtual DOMHighResTimeStamp Duration() const MOZ_OVERRIDE
+  {
+    return mDuration;
+  }
+
+protected:
+  virtual ~PerformanceMeasure();
+  DOMHighResTimeStamp mStartTime;
+  DOMHighResTimeStamp mDuration;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_performancemeasure_h___ */
--- a/dom/base/PerformanceResourceTiming.cpp
+++ b/dom/base/PerformanceResourceTiming.cpp
@@ -18,18 +18,19 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(PerformanceResourceTiming)
 NS_INTERFACE_MAP_END_INHERITING(PerformanceEntry)
 
 NS_IMPL_ADDREF_INHERITED(PerformanceResourceTiming, PerformanceEntry)
 NS_IMPL_RELEASE_INHERITED(PerformanceResourceTiming, PerformanceEntry)
 
 PerformanceResourceTiming::PerformanceResourceTiming(nsPerformanceTiming* aPerformanceTiming,
-                                                     nsPerformance* aPerformance)
-: PerformanceEntry(aPerformance),
+                                                     nsPerformance* aPerformance,
+                                                     const nsAString& aName)
+: PerformanceEntry(aPerformance, aName, NS_LITERAL_STRING("resource")),
   mTiming(aPerformanceTiming)
 {
   MOZ_ASSERT(aPerformance, "Parent performance object should be provided");
 }
 
 PerformanceResourceTiming::~PerformanceResourceTiming()
 {
 }
--- a/dom/base/PerformanceResourceTiming.h
+++ b/dom/base/PerformanceResourceTiming.h
@@ -23,17 +23,18 @@ public:
   typedef mozilla::TimeStamp TimeStamp;
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(
       PerformanceResourceTiming,
       PerformanceEntry)
 
   PerformanceResourceTiming(nsPerformanceTiming* aPerformanceTiming,
-                            nsPerformance* aPerformance);
+                            nsPerformance* aPerformance,
+                            const nsAString& aName);
 
   virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
 
   virtual DOMHighResTimeStamp StartTime() const MOZ_OVERRIDE;
 
   virtual DOMHighResTimeStamp Duration() const MOZ_OVERRIDE
   {
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -176,16 +176,18 @@ EXPORTS.mozilla.dom += [
     'MessagePort.h',
     'MessagePortList.h',
     'NameSpaceConstants.h',
     'Navigator.h',
     'NodeInfo.h',
     'NodeInfoInlines.h',
     'NodeIterator.h',
     'PerformanceEntry.h',
+    'PerformanceMark.h',
+    'PerformanceMeasure.h',
     'PerformanceResourceTiming.h',
     'ResponsiveImageSelector.h',
     'ScreenOrientation.h',
     'ScriptSettings.h',
     'ShadowRoot.h',
     'StructuredCloneTags.h',
     'StyleSheetList.h',
     'SubtleCrypto.h',
@@ -310,16 +312,18 @@ UNIFIED_SOURCES += [
     'nsWindowMemoryReporter.cpp',
     'nsWindowRoot.cpp',
     'nsWrapperCache.cpp',
     'nsXHTMLContentSerializer.cpp',
     'nsXMLContentSerializer.cpp',
     'nsXMLHttpRequest.cpp',
     'nsXMLNameSpaceMap.cpp',
     'PerformanceEntry.cpp',
+    'PerformanceMark.cpp',
+    'PerformanceMeasure.cpp',
     'PerformanceResourceTiming.cpp',
     'ResponsiveImageSelector.cpp',
     'ScriptSettings.cpp',
     'ShadowRoot.cpp',
     'StyleSheetList.cpp',
     'SubtleCrypto.cpp',
     'Text.cpp',
     'TextInputProcessor.cpp',
--- a/dom/base/nsDOMAttributeMap.cpp
+++ b/dom/base/nsDOMAttributeMap.cpp
@@ -9,16 +9,17 @@
  */
 
 #include "nsDOMAttributeMap.h"
 
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/dom/Attr.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/NamedNodeMapBinding.h"
+#include "mozilla/dom/NodeInfoInlines.h"
 #include "nsAttrName.h"
 #include "nsContentUtils.h"
 #include "nsError.h"
 #include "nsIContentInlines.h"
 #include "nsIDocument.h"
 #include "nsNameSpaceManager.h"
 #include "nsNodeInfoManager.h"
 #include "nsUnicharUtils.h"
--- a/dom/base/nsPerformance.cpp
+++ b/dom/base/nsPerformance.cpp
@@ -6,27 +6,31 @@
 #include "nsPerformance.h"
 #include "nsCOMPtr.h"
 #include "nsIHttpChannel.h"
 #include "nsITimedChannel.h"
 #include "nsDOMNavigationTiming.h"
 #include "nsContentUtils.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIDOMWindow.h"
+#include "nsILoadInfo.h"
 #include "nsIURI.h"
+#include "nsThreadUtils.h"
 #include "PerformanceEntry.h"
+#include "PerformanceMark.h"
+#include "PerformanceMeasure.h"
 #include "PerformanceResourceTiming.h"
+#include "mozilla/ErrorResult.h"
 #include "mozilla/dom/PerformanceBinding.h"
 #include "mozilla/dom/PerformanceTimingBinding.h"
 #include "mozilla/dom/PerformanceNavigationBinding.h"
 #include "mozilla/TimeStamp.h"
-#include "nsThreadUtils.h"
-#include "nsILoadInfo.h"
 
 using namespace mozilla;
+using namespace mozilla::dom;
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsPerformanceTiming, mPerformance)
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsPerformanceTiming, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsPerformanceTiming, Release)
 
 nsPerformanceTiming::nsPerformanceTiming(nsPerformance* aPerformance,
                                          nsITimedChannel* aChannel,
@@ -320,17 +324,17 @@ DOMTimeMilliSec
 nsPerformanceTiming::ResponseStart()
 {
   return static_cast<int64_t>(ResponseStartHighRes());
 }
 
 DOMHighResTimeStamp
 nsPerformanceTiming::ResponseEndHighRes()
 {
-  if (!nsContentUtils::IsPerformanceTimingEnabled() || !IsInitialized()) {
+  if (!IsInitialized()) {
     return mZeroTime;
   }
   if (mResponseEnd.IsNull() ||
      (!mCacheReadEnd.IsNull() && mCacheReadEnd < mResponseEnd)) {
     mResponseEnd = mCacheReadEnd;
   }
   return TimeStampToDOMHighResOrFetchStart(mResponseEnd);
 }
@@ -345,17 +349,17 @@ bool
 nsPerformanceTiming::IsInitialized() const
 {
   return mInitialized;
 }
 
 JSObject*
 nsPerformanceTiming::WrapObject(JSContext *cx)
 {
-  return dom::PerformanceTimingBinding::Wrap(cx, this);
+  return PerformanceTimingBinding::Wrap(cx, this);
 }
 
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsPerformanceNavigation, mPerformance)
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsPerformanceNavigation, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsPerformanceNavigation, Release)
 
@@ -367,17 +371,17 @@ nsPerformanceNavigation::nsPerformanceNa
 
 nsPerformanceNavigation::~nsPerformanceNavigation()
 {
 }
 
 JSObject*
 nsPerformanceNavigation::WrapObject(JSContext *cx)
 {
-  return dom::PerformanceNavigationBinding::Wrap(cx, this);
+  return PerformanceNavigationBinding::Wrap(cx, this);
 }
 
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(nsPerformance, DOMEventTargetHelper,
                                    mWindow, mTiming,
                                    mNavigation, mEntries,
                                    mParentPerformance)
 NS_IMPL_ADDREF_INHERITED(nsPerformance, DOMEventTargetHelper)
@@ -444,23 +448,23 @@ nsPerformance::Navigation()
     mNavigation = new nsPerformanceNavigation(this);
   }
   return mNavigation;
 }
 
 DOMHighResTimeStamp
 nsPerformance::Now()
 {
-  return GetDOMTiming()->TimeStampToDOMHighRes(mozilla::TimeStamp::Now());
+  return GetDOMTiming()->TimeStampToDOMHighRes(TimeStamp::Now());
 }
 
 JSObject*
 nsPerformance::WrapObject(JSContext *cx)
 {
-  return dom::PerformanceBinding::Wrap(cx, this);
+  return PerformanceBinding::Wrap(cx, this);
 }
 
 void
 nsPerformance::GetEntries(nsTArray<nsRefPtr<PerformanceEntry> >& retval)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   retval = mEntries;
@@ -478,37 +482,54 @@ nsPerformance::GetEntriesByType(const ns
     if (mEntries[i]->GetEntryType().Equals(entryType)) {
       retval.AppendElement(mEntries[i]);
     }
   }
 }
 
 void
 nsPerformance::GetEntriesByName(const nsAString& name,
-                                const mozilla::dom::Optional<nsAString>& entryType,
+                                const Optional<nsAString>& entryType,
                                 nsTArray<nsRefPtr<PerformanceEntry> >& retval)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   retval.Clear();
   uint32_t count = mEntries.Length();
   for (uint32_t i = 0 ; i < count; i++) {
     if (mEntries[i]->GetName().Equals(name) &&
         (!entryType.WasPassed() ||
          mEntries[i]->GetEntryType().Equals(entryType.Value()))) {
       retval.AppendElement(mEntries[i]);
     }
   }
 }
 
 void
+nsPerformance::ClearEntries(const Optional<nsAString>& aEntryName,
+                            const nsAString& aEntryType)
+{
+  for (uint32_t i = 0; i < mEntries.Length();) {
+    if ((!aEntryName.WasPassed() ||
+         mEntries[i]->GetName().Equals(aEntryName.Value())) &&
+        (aEntryType.IsEmpty() ||
+         mEntries[i]->GetEntryType().Equals(aEntryType))) {
+      mEntries.RemoveElementAt(i);
+    } else {
+      ++i;
+    }
+  }
+}
+
+void
 nsPerformance::ClearResourceTimings()
 {
   MOZ_ASSERT(NS_IsMainThread());
-  mEntries.Clear();
+  ClearEntries(Optional<nsAString>(),
+               NS_LITERAL_STRING("resource"));
 }
 
 void
 nsPerformance::SetResourceTimingBufferSize(uint64_t maxSize)
 {
   MOZ_ASSERT(NS_IsMainThread());
   mPrimaryBufferSize = maxSize;
 }
@@ -524,16 +545,17 @@ nsPerformance::AddEntry(nsIHttpChannel* 
   MOZ_ASSERT(NS_IsMainThread());
   // Check if resource timing is prefed off.
   if (!nsContentUtils::IsResourceTimingEnabled()) {
     return;
   }
 
   // Don't add the entry if the buffer is full
   if (mEntries.Length() >= mPrimaryBufferSize) {
+    NS_WARNING("Performance Entry buffer size maximum reached!");
     return;
   }
 
   if (channel && timedChannel) {
     nsAutoCString name;
     nsAutoString initiatorType;
     nsCOMPtr<nsIURI> originalURI;
 
@@ -553,34 +575,26 @@ nsPerformance::AddEntry(nsIHttpChannel* 
     // any offset for the resource timing, this will be set to "0" - the
     // resource timing returns a relative timing (no offset).
     nsRefPtr<nsPerformanceTiming> performanceTiming =
         new nsPerformanceTiming(this, timedChannel, channel,
             0);
 
     // The PerformanceResourceTiming object will use the nsPerformanceTiming
     // object to get all the required timings.
-    nsRefPtr<dom::PerformanceResourceTiming> performanceEntry =
-        new dom::PerformanceResourceTiming(performanceTiming, this);
+    nsRefPtr<PerformanceResourceTiming> performanceEntry =
+      new PerformanceResourceTiming(performanceTiming, this, entryName);
 
-    performanceEntry->SetName(entryName);
-    performanceEntry->SetEntryType(NS_LITERAL_STRING("resource"));
     // If the initiator type had no valid value, then set it to the default
     // ("other") value.
     if (initiatorType.IsEmpty()) {
       initiatorType = NS_LITERAL_STRING("other");
     }
     performanceEntry->SetInitiatorType(initiatorType);
-
-    mEntries.InsertElementSorted(performanceEntry,
-        PerformanceEntryComparator());
-    if (mEntries.Length() >= mPrimaryBufferSize) {
-      // call onresourcetimingbufferfull
-      DispatchBufferFullEvent();
-    }
+    InsertPerformanceEntry(performanceEntry);
   }
 }
 
 bool
 nsPerformance::PerformanceEntryComparator::Equals(
     const PerformanceEntry* aElem1,
     const PerformanceEntry* aElem2) const
 {
@@ -593,8 +607,231 @@ bool
 nsPerformance::PerformanceEntryComparator::LessThan(
     const PerformanceEntry* aElem1,
     const PerformanceEntry* aElem2) const
 {
   NS_ABORT_IF_FALSE(aElem1 && aElem2,
       "Trying to compare null performance entries");
   return aElem1->StartTime() < aElem2->StartTime();
 }
+
+void
+nsPerformance::InsertPerformanceEntry(PerformanceEntry* aEntry)
+{
+  MOZ_ASSERT(aEntry);
+  MOZ_ASSERT(mEntries.Length() < mPrimaryBufferSize);
+  if (mEntries.Length() == mPrimaryBufferSize) {
+    NS_WARNING("Performance Entry buffer size maximum reached!");
+    return;
+  }
+  mEntries.InsertElementSorted(aEntry,
+                               PerformanceEntryComparator());
+  if (mEntries.Length() == mPrimaryBufferSize) {
+    // call onresourcetimingbufferfull
+    DispatchBufferFullEvent();
+  }
+}
+
+void
+nsPerformance::Mark(const nsAString& aName, ErrorResult& aRv)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  // Don't add the entry if the buffer is full
+  if (mEntries.Length() >= mPrimaryBufferSize) {
+    NS_WARNING("Performance Entry buffer size maximum reached!");
+    return;
+  }
+  if (IsPerformanceTimingAttribute(aName)) {
+    aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
+    return;
+  }
+  nsRefPtr<PerformanceMark> performanceMark =
+    new PerformanceMark(this, aName);
+  InsertPerformanceEntry(performanceMark);
+}
+
+void
+nsPerformance::ClearMarks(const Optional<nsAString>& aName)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  ClearEntries(aName, NS_LITERAL_STRING("mark"));
+}
+
+DOMHighResTimeStamp
+nsPerformance::ResolveTimestampFromName(const nsAString& aName,
+                                        ErrorResult& aRv)
+{
+  nsAutoTArray<nsRefPtr<PerformanceEntry>, 1> arr;
+  DOMHighResTimeStamp ts;
+  Optional<nsAString> typeParam;
+  nsAutoString str;
+  str.AssignLiteral("mark");
+  typeParam = &str;
+  GetEntriesByName(aName, typeParam, arr);
+  if (!arr.IsEmpty()) {
+    return arr.LastElement()->StartTime();
+  }
+  if (!IsPerformanceTimingAttribute(aName)) {
+    aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
+    return 0;
+  }
+  ts = GetPerformanceTimingFromString(aName);
+  if (!ts) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
+    return 0;
+  }
+  return ConvertDOMMilliSecToHighRes(ts);
+}
+
+void
+nsPerformance::Measure(const nsAString& aName,
+                       const Optional<nsAString>& aStartMark,
+                       const Optional<nsAString>& aEndMark,
+                       ErrorResult& aRv)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  // Don't add the entry if the buffer is full
+  if (mEntries.Length() >= mPrimaryBufferSize) {
+    NS_WARNING("Performance Entry buffer size maximum reached!");
+    return;
+  }
+  DOMHighResTimeStamp startTime;
+  DOMHighResTimeStamp endTime;
+
+  if (IsPerformanceTimingAttribute(aName)) {
+    aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
+    return;
+  }
+
+  if (aStartMark.WasPassed()) {
+    startTime = ResolveTimestampFromName(aStartMark.Value(), aRv);
+    if (NS_WARN_IF(aRv.Failed())) {
+      return;
+    }
+  } else {
+    // Navigation start is used in this case, but since DOMHighResTimeStamp is
+    // in relation to navigation start, this will be zero if a name is not
+    // passed.
+    startTime = 0;
+  }
+  if (aEndMark.WasPassed()) {
+    endTime = ResolveTimestampFromName(aEndMark.Value(), aRv);
+    if (NS_WARN_IF(aRv.Failed())) {
+      return;
+    }
+  } else {
+    endTime = Now();
+  }
+  nsRefPtr<PerformanceMeasure> performanceMeasure =
+    new PerformanceMeasure(this, aName, startTime, endTime);
+  InsertPerformanceEntry(performanceMeasure);
+}
+
+void
+nsPerformance::ClearMeasures(const Optional<nsAString>& aName)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  ClearEntries(aName, NS_LITERAL_STRING("measure"));
+}
+
+DOMHighResTimeStamp
+nsPerformance::ConvertDOMMilliSecToHighRes(DOMTimeMilliSec aTime) {
+  // If the time we're trying to convert is equal to zero, it hasn't been set
+  // yet so just return 0.
+  if (aTime == 0) {
+    return 0;
+  }
+  return aTime - GetDOMTiming()->GetNavigationStart();
+}
+
+// To be removed once bug 1124165 lands
+bool
+nsPerformance::IsPerformanceTimingAttribute(const nsAString& aName)
+{
+  // Note that toJSON is added to this list due to bug 1047848
+  static const char* attributes[] =
+    {"navigationStart", "unloadEventStart", "unloadEventEnd", "redirectStart",
+     "redirectEnd", "fetchStart", "domainLookupStart", "domainLookupEnd",
+     "connectStart", "connectEnd", "requestStart", "responseStart",
+     "responseEnd", "domLoading", "domInteractive", "domContentLoadedEventStart",
+     "domContentLoadedEventEnd", "domComplete", "loadEventStart",
+     "loadEventEnd", nullptr};
+
+  for (uint32_t i = 0; attributes[i]; ++i) {
+    if (aName.EqualsASCII(attributes[i])) {
+      return true;
+    }
+  }
+  return false;
+}
+
+DOMTimeMilliSec
+nsPerformance::GetPerformanceTimingFromString(const nsAString& aProperty)
+{
+  if (!IsPerformanceTimingAttribute(aProperty)) {
+    return 0;
+  }
+  if (aProperty.EqualsLiteral("navigationStart")) {
+    // DOMHighResTimeStamp is in relation to navigationStart, so this will be
+    // zero.
+    return GetDOMTiming()->GetNavigationStart();
+  }
+  if (aProperty.EqualsLiteral("unloadEventStart")) {
+    return GetDOMTiming()->GetUnloadEventStart();
+  }
+  if (aProperty.EqualsLiteral("unloadEventEnd")) {
+    return GetDOMTiming()->GetUnloadEventEnd();
+  }
+  if (aProperty.EqualsLiteral("redirectStart")) {
+    return Timing()->RedirectStart();
+  }
+  if (aProperty.EqualsLiteral("redirectEnd")) {
+    return Timing()->RedirectEnd();
+  }
+  if (aProperty.EqualsLiteral("fetchStart")) {
+    return Timing()->FetchStart();
+  }
+  if (aProperty.EqualsLiteral("domainLookupStart")) {
+    return Timing()->DomainLookupStart();
+  }
+  if (aProperty.EqualsLiteral("domainLookupEnd")) {
+    return Timing()->DomainLookupEnd();
+  }
+  if (aProperty.EqualsLiteral("connectStart")) {
+    return Timing()->ConnectStart();
+  }
+  if (aProperty.EqualsLiteral("connectEnd")) {
+    return Timing()->ConnectEnd();
+  }
+  if (aProperty.EqualsLiteral("requestStart")) {
+    return Timing()->RequestStart();
+  }
+  if (aProperty.EqualsLiteral("responseStart")) {
+    return Timing()->ResponseStart();
+  }
+  if (aProperty.EqualsLiteral("responseEnd")) {
+    return Timing()->ResponseEnd();
+  }
+  if (aProperty.EqualsLiteral("domLoading")) {
+    return GetDOMTiming()->GetDomLoading();
+  }
+  if (aProperty.EqualsLiteral("domInteractive")) {
+    return GetDOMTiming()->GetDomInteractive();
+  }
+  if (aProperty.EqualsLiteral("domContentLoadedEventStart")) {
+    return GetDOMTiming()->GetDomContentLoadedEventStart();
+  }
+  if (aProperty.EqualsLiteral("domContentLoadedEventEnd")) {
+    return GetDOMTiming()->GetDomContentLoadedEventEnd();
+  }
+  if (aProperty.EqualsLiteral("domComplete")) {
+    return GetDOMTiming()->GetDomComplete();
+  }
+  if (aProperty.EqualsLiteral("loadEventStart")) {
+    return GetDOMTiming()->GetLoadEventStart();
+  }
+  if (aProperty.EqualsLiteral("loadEventEnd"))  {
+    return GetDOMTiming()->GetLoadEventEnd();
+  }
+  MOZ_CRASH("IsPerformanceTimingAttribute and GetPerformanceTimingFromString are out of sync");
+  return 0;
+}
+
--- a/dom/base/nsPerformance.h
+++ b/dom/base/nsPerformance.h
@@ -16,16 +16,17 @@
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/DOMEventTargetHelper.h"
 
 class nsITimedChannel;
 class nsPerformance;
 class nsIHttpChannel;
 
 namespace mozilla {
+class ErrorResult;
 namespace dom {
   class PerformanceEntry;
 }
 }
 
 // Script "performance.timing" object
 class nsPerformanceTiming MOZ_FINAL : public nsWrapperCache
 {
@@ -330,22 +331,36 @@ public:
                         nsTArray<nsRefPtr<PerformanceEntry> >& retval);
   void GetEntriesByName(const nsAString& name,
                         const mozilla::dom::Optional< nsAString >& entryType,
                         nsTArray<nsRefPtr<PerformanceEntry> >& retval);
   void AddEntry(nsIHttpChannel* channel,
                 nsITimedChannel* timedChannel);
   void ClearResourceTimings();
   void SetResourceTimingBufferSize(uint64_t maxSize);
+  void Mark(const nsAString& aName, mozilla::ErrorResult& aRv);
+  void ClearMarks(const mozilla::dom::Optional<nsAString>& aName);
+  void Measure(const nsAString& aName,
+               const mozilla::dom::Optional<nsAString>& aStartMark,
+               const mozilla::dom::Optional<nsAString>& aEndMark,
+               mozilla::ErrorResult& aRv);
+  void ClearMeasures(const mozilla::dom::Optional<nsAString>& aName);
+
   IMPL_EVENT_HANDLER(resourcetimingbufferfull)
 
 private:
   ~nsPerformance();
+  bool IsPerformanceTimingAttribute(const nsAString& aName);
+  DOMHighResTimeStamp ResolveTimestampFromName(const nsAString& aName, mozilla::ErrorResult& aRv);
+  DOMTimeMilliSec GetPerformanceTimingFromString(const nsAString& aTimingName);
+  DOMHighResTimeStamp ConvertDOMMilliSecToHighRes(const DOMTimeMilliSec aTime);
   void DispatchBufferFullEvent();
-
+  void InsertPerformanceEntry(PerformanceEntry* aEntry);
+  void ClearEntries(const mozilla::dom::Optional<nsAString>& aEntryName,
+                    const nsAString& aEntryType);
   nsCOMPtr<nsPIDOMWindow> mWindow;
   nsRefPtr<nsDOMNavigationTiming> mDOMTiming;
   nsCOMPtr<nsITimedChannel> mChannel;
   nsRefPtr<nsPerformanceTiming> mTiming;
   nsRefPtr<nsPerformanceNavigation> mNavigation;
   nsTArray<nsRefPtr<PerformanceEntry> > mEntries;
   nsRefPtr<nsPerformance> mParentPerformance;
   uint64_t mPrimaryBufferSize;
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -855,16 +855,20 @@ var interfaceNamesInGlobalScope =
     "PannerNode",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     { name: "Path2D", pref: "canvas.path.enabled" },
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Performance",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "PerformanceEntry",
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    "PerformanceMark",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "PerformanceMeasure",
+// IMPORTANT: Do not change this list without review from a DOM peer!
     "PerformanceNavigation",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "PerformanceResourceTiming",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "PerformanceTiming",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "PeriodicWave",
 // IMPORTANT: Do not change this list without review from a DOM peer!
--- a/dom/webidl/Performance.webidl
+++ b/dom/webidl/Performance.webidl
@@ -46,8 +46,21 @@ partial interface Performance {
 partial interface Performance {
   [Pref="dom.enable_resource_timing"]
   void clearResourceTimings();
   [Pref="dom.enable_resource_timing"]
   void setResourceTimingBufferSize(unsigned long maxSize);
   [Pref="dom.enable_resource_timing"]
   attribute EventHandler onresourcetimingbufferfull;
 };
+
+// http://www.w3.org/TR/user-timing/
+[Exposed=Window]
+partial interface Performance {
+  [Pref="dom.enable_user_timing", Throws]
+  void mark(DOMString markName);
+  [Pref="dom.enable_user_timing"]
+  void clearMarks(optional DOMString markName);
+  [Pref="dom.enable_user_timing", Throws]
+  void measure(DOMString measureName, optional DOMString startMark, optional DOMString endMark);
+  [Pref="dom.enable_user_timing"]
+  void clearMeasures(optional DOMString measureName);
+};
new file mode 100644
--- /dev/null
+++ b/dom/webidl/PerformanceMark.webidl
@@ -0,0 +1,12 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * http://www.w3.org/TR/user-timing/#performancemark
+ */
+
+interface PerformanceMark : PerformanceEntry
+{
+};
new file mode 100644
--- /dev/null
+++ b/dom/webidl/PerformanceMeasure.webidl
@@ -0,0 +1,12 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * http://www.w3.org/TR/user-timing/#performancemeasure
+ */
+
+interface PerformanceMeasure : PerformanceEntry
+{
+};
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -315,16 +315,18 @@ WEBIDL_FILES = [
     'OfflineResourceList.webidl',
     'OscillatorNode.webidl',
     'PaintRequest.webidl',
     'PaintRequestList.webidl',
     'PannerNode.webidl',
     'ParentNode.webidl',
     'Performance.webidl',
     'PerformanceEntry.webidl',
+    'PerformanceMark.webidl',
+    'PerformanceMeasure.webidl',
     'PerformanceNavigation.webidl',
     'PerformanceResourceTiming.webidl',
     'PerformanceTiming.webidl',
     'PeriodicWave.webidl',
     'PermissionSettings.webidl',
     'PhoneNumberService.webidl',
     'Plugin.webidl',
     'PluginArray.webidl',
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -139,16 +139,19 @@ pref("dom.workers.websocket.enabled", tr
 pref("dom.serviceWorkers.enabled", false);
 
 // Whether nonzero values can be returned from performance.timing.*
 pref("dom.enable_performance", true);
 
 // Whether resource timing will be gathered and returned by performance.GetEntries*
 pref("dom.enable_resource_timing", true);
 
+// Enable high-resolution timing markers for users
+pref("dom.enable_user_timing", true);
+
 // Whether the Gamepad API is enabled
 pref("dom.gamepad.enabled", true);
 #ifdef RELEASE_BUILD
 pref("dom.gamepad.non_standard_events.enabled", false);
 #else
 pref("dom.gamepad.non_standard_events.enabled", true);
 #endif