Bug 1200119 - Add a way to create usable markers from different threads, r=tromey
authorVictor Porof <vporof@mozilla.com>
Fri, 11 Sep 2015 17:03:06 +0200
changeset 294738 0d9327e758e527c7af1e7f795abcce13397f8c59
parent 294737 bc2d12101a250b5f82d991e5c70d36709387354b
child 294739 1b51d0c353153f42c62b4e4ef6ca10fcd93c6f69
push id5245
push userraliiev@mozilla.com
push dateThu, 29 Oct 2015 11:30:51 +0000
treeherdermozilla-beta@dac831dc1bd0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstromey
bugs1200119
milestone43.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 1200119 - Add a way to create usable markers from different threads, r=tromey
docshell/base/nsDocShell.h
docshell/base/timeline/AbstractTimelineMarker.cpp
docshell/base/timeline/AbstractTimelineMarker.h
docshell/base/timeline/OTMTMarkerReceiver.h
docshell/base/timeline/ObservedDocShell.cpp
docshell/base/timeline/ObservedDocShell.h
docshell/base/timeline/TimelineConsumers.cpp
docshell/base/timeline/TimelineConsumers.h
docshell/base/timeline/TimelineMarker.h
docshell/base/timeline/moz.build
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -277,17 +277,19 @@ private:
   // is stored on docshells directly.
   friend void mozilla::TimelineConsumers::AddConsumer(nsDocShell*);
   friend void mozilla::TimelineConsumers::RemoveConsumer(nsDocShell*);
   friend void mozilla::TimelineConsumers::AddMarkerForDocShell(
     nsDocShell*, const char*, MarkerTracingType);
   friend void mozilla::TimelineConsumers::AddMarkerForDocShell(
     nsDocShell*, const char*, const TimeStamp&, MarkerTracingType);
   friend void mozilla::TimelineConsumers::AddMarkerForDocShell(
-    nsDocShell*, UniquePtr<TimelineMarker>&&);
+    nsDocShell*, UniquePtr<AbstractTimelineMarker>&&);
+  friend void mozilla::TimelineConsumers::AddOTMTMarkerForDocShell(
+    nsDocShell*, UniquePtr<AbstractTimelineMarker>&);
 
 public:
   // Tell the favicon service that aNewURI has the same favicon as aOldURI.
   static void CopyFavicon(nsIURI* aOldURI,
                           nsIURI* aNewURI,
                           bool aInPrivateBrowsing);
 
 protected:
--- a/docshell/base/timeline/AbstractTimelineMarker.cpp
+++ b/docshell/base/timeline/AbstractTimelineMarker.cpp
@@ -23,16 +23,23 @@ AbstractTimelineMarker::AbstractTimeline
                                                MarkerTracingType aTracingType)
   : mName(aName)
   , mTracingType(aTracingType)
 {
   MOZ_COUNT_CTOR(AbstractTimelineMarker);
   SetCustomTime(aTime);
 }
 
+UniquePtr<AbstractTimelineMarker>
+AbstractTimelineMarker::Clone()
+{
+  MOZ_ASSERT(false, "Clone method not yet implemented on this marker type.");
+  return nullptr;
+}
+
 AbstractTimelineMarker::~AbstractTimelineMarker()
 {
   MOZ_COUNT_DTOR(AbstractTimelineMarker);
 }
 
 void
 AbstractTimelineMarker::SetCurrentTime()
 {
--- a/docshell/base/timeline/AbstractTimelineMarker.h
+++ b/docshell/base/timeline/AbstractTimelineMarker.h
@@ -4,16 +4,17 @@
  * 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_AbstractTimelineMarker_h_
 #define mozilla_AbstractTimelineMarker_h_
 
 #include "TimelineMarkerEnums.h" // for MarkerTracingType
 #include "nsDOMNavigationTiming.h" // for DOMHighResTimeStamp
+#include "mozilla/UniquePtr.h"
 
 struct JSContext;
 
 namespace mozilla {
 class TimeStamp;
 
 namespace dom {
 struct ProfileTimelineMarker;
@@ -31,18 +32,21 @@ public:
                          MarkerTracingType aTracingType);
 
   AbstractTimelineMarker(const char* aName,
                          const TimeStamp& aTime,
                          MarkerTracingType aTracingType);
 
   virtual ~AbstractTimelineMarker();
 
+  virtual UniquePtr<AbstractTimelineMarker> Clone();
+
   virtual bool Equals(const AbstractTimelineMarker& aOther) = 0;
   virtual void AddDetails(JSContext* aCx, dom::ProfileTimelineMarker& aMarker) = 0;
+  virtual JSObject* GetStack() = 0;
 
   const char* GetName() const { return mName; }
   DOMHighResTimeStamp GetTime() const { return mTime; }
   MarkerTracingType GetTracingType() const { return mTracingType; }
 
 private:
   const char* mName;
   DOMHighResTimeStamp mTime;
new file mode 100644
--- /dev/null
+++ b/docshell/base/timeline/OTMTMarkerReceiver.h
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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_OTMTMarkerObserver_h_
+#define mozilla_OTMTMarkerObserver_h_
+
+#include "mozilla/Mutex.h"
+
+namespace mozilla {
+class AbstractTimelineMarker;
+
+class OTMTMarkerReceiver
+{
+private:
+  OTMTMarkerReceiver() = delete;
+  OTMTMarkerReceiver(const OTMTMarkerReceiver& aOther) = delete;
+  void operator=(const OTMTMarkerReceiver& aOther) = delete;
+
+public:
+  explicit OTMTMarkerReceiver(const char* aMutexName)
+    : mLock(aMutexName)
+  {
+  }
+
+  virtual ~OTMTMarkerReceiver() {}
+  virtual void AddOTMTMarkerClone(UniquePtr<AbstractTimelineMarker>& aMarker) = 0;
+
+protected:
+  Mutex& GetLock() { return mLock; };
+
+private:
+  Mutex mLock;
+};
+
+} // namespace mozilla
+
+#endif /* mozilla_OTMTMarkerObserver_h_ */
--- a/docshell/base/timeline/ObservedDocShell.cpp
+++ b/docshell/base/timeline/ObservedDocShell.cpp
@@ -1,48 +1,65 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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 "ObservedDocShell.h"
 
-#include "TimelineMarker.h"
+#include "AbstractTimelineMarker.h"
 #include "LayerTimelineMarker.h"
+#include "MainThreadUtils.h"
 #include "mozilla/Move.h"
 
 namespace mozilla {
 
 ObservedDocShell::ObservedDocShell(nsDocShell* aDocShell)
-  : mDocShell(aDocShell)
-{}
+  : OTMTMarkerReceiver("ObservedDocShellMutex")
+  , mDocShell(aDocShell)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+}
 
 void
-ObservedDocShell::AddMarker(UniquePtr<TimelineMarker>&& aMarker)
+ObservedDocShell::AddMarker(UniquePtr<AbstractTimelineMarker>&& aMarker)
 {
+  MOZ_ASSERT(NS_IsMainThread());
   mTimelineMarkers.AppendElement(Move(aMarker));
 }
 
 void
+ObservedDocShell::AddOTMTMarkerClone(UniquePtr<AbstractTimelineMarker>& aMarker)
+{
+  MOZ_ASSERT(!NS_IsMainThread());
+  MutexAutoLock lock(GetLock());
+  UniquePtr<AbstractTimelineMarker> cloned = aMarker->Clone();
+  mTimelineMarkers.AppendElement(Move(cloned));
+}
+
+void
 ObservedDocShell::ClearMarkers()
 {
+  MOZ_ASSERT(NS_IsMainThread());
   mTimelineMarkers.Clear();
 }
 
 void
 ObservedDocShell::PopMarkers(JSContext* aCx,
                              nsTArray<dom::ProfileTimelineMarker>& aStore)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
   // If we see an unpaired START, we keep it around for the next call
   // to ObservedDocShell::PopMarkers. We store the kept START objects here.
-  nsTArray<UniquePtr<TimelineMarker>> keptStartMarkers;
+  nsTArray<UniquePtr<AbstractTimelineMarker>> keptStartMarkers;
 
   for (uint32_t i = 0; i < mTimelineMarkers.Length(); ++i) {
-    UniquePtr<TimelineMarker>& startPayload = mTimelineMarkers[i];
+    UniquePtr<AbstractTimelineMarker>& startPayload = mTimelineMarkers[i];
 
     // If this is a TIMESTAMP marker, there's no corresponding END,
     // as it's a single unit of time, not a duration.
     if (startPayload->GetTracingType() == MarkerTracingType::TIMESTAMP) {
       dom::ProfileTimelineMarker* marker = aStore.AppendElement();
       marker->mName = NS_ConvertUTF8toUTF16(startPayload->GetName());
       marker->mStart = startPayload->GetTime();
       marker->mEnd = startPayload->GetTime();
@@ -71,17 +88,17 @@ ObservedDocShell::PopMarkers(JSContext* 
       // for the matching end. It doesn't hurt to apply this logic to
       // all event types.
       uint32_t markerDepth = 0;
 
       // The assumption is that the devtools timeline flushes markers frequently
       // enough for the amount of markers to always be small enough that the
       // nested for loop isn't going to be a performance problem.
       for (uint32_t j = i + 1; j < mTimelineMarkers.Length(); ++j) {
-        UniquePtr<TimelineMarker>& endPayload = mTimelineMarkers[j];
+        UniquePtr<AbstractTimelineMarker>& endPayload = mTimelineMarkers[j];
         bool endIsLayerType = strcmp(endPayload->GetName(), "Layer") == 0;
 
         // Look for "Layer" markers to stream out "Paint" markers.
         if (startIsPaintType && endIsLayerType) {
           LayerTimelineMarker* layerPayload = static_cast<LayerTimelineMarker*>(endPayload.get());
           layerPayload->AddLayerRectangles(layerRectangles);
           hasSeenLayerType = true;
         }
--- a/docshell/base/timeline/ObservedDocShell.h
+++ b/docshell/base/timeline/ObservedDocShell.h
@@ -2,42 +2,46 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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_ObservedDocShell_h_
 #define mozilla_ObservedDocShell_h_
 
+#include "OTMTMarkerReceiver.h"
 #include "nsTArray.h"
 #include "mozilla/nsRefPtr.h"
 
 class nsDocShell;
 
 namespace mozilla {
-class TimelineMarker;
+class AbstractTimelineMarker;
 
 namespace dom {
 struct ProfileTimelineMarker;
 }
 
 // # ObservedDocShell
 //
 // A wrapper around a docshell for which docshell-specific markers are
 // allowed to exist. See TimelineConsumers for register/unregister logic.
-class ObservedDocShell : public LinkedListElement<ObservedDocShell>
+class ObservedDocShell : public LinkedListElement<ObservedDocShell>,
+                         public OTMTMarkerReceiver
 {
 private:
   nsRefPtr<nsDocShell> mDocShell;
-  nsTArray<UniquePtr<TimelineMarker>> mTimelineMarkers;
+  nsTArray<UniquePtr<AbstractTimelineMarker>> mTimelineMarkers;
 
 public:
   explicit ObservedDocShell(nsDocShell* aDocShell);
   nsDocShell* operator*() const { return mDocShell.get(); }
 
-  void AddMarker(UniquePtr<TimelineMarker>&& aMarker);
+  void AddMarker(UniquePtr<AbstractTimelineMarker>&& aMarker);
+  void AddOTMTMarkerClone(UniquePtr<AbstractTimelineMarker>& aMarker) override;
+
   void ClearMarkers();
   void PopMarkers(JSContext* aCx, nsTArray<dom::ProfileTimelineMarker>& aStore);
 };
 
 } // namespace mozilla
 
 #endif /* mozilla_ObservedDocShell_h_ */
--- a/docshell/base/timeline/TimelineConsumers.cpp
+++ b/docshell/base/timeline/TimelineConsumers.cpp
@@ -5,58 +5,72 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/TimelineConsumers.h"
 
 namespace mozilla {
 
 unsigned long TimelineConsumers::sActiveConsumers = 0;
 LinkedList<ObservedDocShell>* TimelineConsumers::sObservedDocShells = nullptr;
+Mutex* TimelineConsumers::sLock = nullptr;
 
 LinkedList<ObservedDocShell>&
 TimelineConsumers::GetOrCreateObservedDocShellsList()
 {
   if (!sObservedDocShells) {
     sObservedDocShells = new LinkedList<ObservedDocShell>();
   }
   return *sObservedDocShells;
 }
 
+Mutex&
+TimelineConsumers::GetLock()
+{
+  if (!sLock) {
+    sLock = new Mutex("TimelineConsumersMutex");
+  }
+  return *sLock;
+}
+
 void
 TimelineConsumers::AddConsumer(nsDocShell* aDocShell)
 {
+  MOZ_ASSERT(NS_IsMainThread());
   UniquePtr<ObservedDocShell>& observed = aDocShell->mObserved;
 
   MOZ_ASSERT(!observed);
   sActiveConsumers++;
   observed.reset(new ObservedDocShell(aDocShell));
   GetOrCreateObservedDocShellsList().insertFront(observed.get());
 }
 
 void
 TimelineConsumers::RemoveConsumer(nsDocShell* aDocShell)
 {
+  MOZ_ASSERT(NS_IsMainThread());
   UniquePtr<ObservedDocShell>& observed = aDocShell->mObserved;
 
   MOZ_ASSERT(observed);
   sActiveConsumers--;
   observed.get()->ClearMarkers();
   observed.get()->remove();
   observed.reset(nullptr);
 }
 
 bool
 TimelineConsumers::IsEmpty()
 {
+  MOZ_ASSERT(NS_IsMainThread());
   return sActiveConsumers == 0;
 }
 
 bool
 TimelineConsumers::GetKnownDocShells(Vector<nsRefPtr<nsDocShell>>& aStore)
 {
+  MOZ_ASSERT(NS_IsMainThread());
   const LinkedList<ObservedDocShell>& docShells = GetOrCreateObservedDocShellsList();
 
   for (const ObservedDocShell* rds = docShells.getFirst();
        rds != nullptr;
        rds = rds->getNext()) {
     if (!aStore.append(**rds)) {
       return false;
     }
@@ -65,84 +79,181 @@ TimelineConsumers::GetKnownDocShells(Vec
   return true;
 }
 
 void
 TimelineConsumers::AddMarkerForDocShell(nsDocShell* aDocShell,
                                         const char* aName,
                                         MarkerTracingType aTracingType)
 {
+  MOZ_ASSERT(NS_IsMainThread());
   if (aDocShell->IsObserved()) {
     aDocShell->mObserved->AddMarker(Move(MakeUnique<TimelineMarker>(aName, aTracingType)));
   }
 }
 
 void
 TimelineConsumers::AddMarkerForDocShell(nsDocShell* aDocShell,
                                         const char* aName,
                                         const TimeStamp& aTime,
                                         MarkerTracingType aTracingType)
 {
+  MOZ_ASSERT(NS_IsMainThread());
   if (aDocShell->IsObserved()) {
     aDocShell->mObserved->AddMarker(Move(MakeUnique<TimelineMarker>(aName, aTime, aTracingType)));
   }
 }
 
 void
 TimelineConsumers::AddMarkerForDocShell(nsDocShell* aDocShell,
-                                        UniquePtr<TimelineMarker>&& aMarker)
+                                        UniquePtr<AbstractTimelineMarker>&& aMarker)
 {
+  MOZ_ASSERT(NS_IsMainThread());
   if (aDocShell->IsObserved()) {
     aDocShell->mObserved->AddMarker(Move(aMarker));
   }
 }
 
 void
+TimelineConsumers::AddOTMTMarkerForDocShell(nsDocShell* aDocShell,
+                                            UniquePtr<AbstractTimelineMarker>& aMarker)
+{
+  MOZ_ASSERT(!NS_IsMainThread());
+  GetLock().AssertCurrentThreadOwns();
+  if (aDocShell->IsObserved()) {
+    aDocShell->mObserved->AddOTMTMarkerClone(aMarker);
+  }
+}
+
+void
 TimelineConsumers::AddMarkerForDocShell(nsIDocShell* aDocShell,
                                         const char* aName,
                                         MarkerTracingType aTracingType)
 {
+  MOZ_ASSERT(NS_IsMainThread());
   AddMarkerForDocShell(static_cast<nsDocShell*>(aDocShell), aName, aTracingType);
 }
 
 void
 TimelineConsumers::AddMarkerForDocShell(nsIDocShell* aDocShell,
                                         const char* aName,
                                         const TimeStamp& aTime,
                                         MarkerTracingType aTracingType)
 {
+  MOZ_ASSERT(NS_IsMainThread());
   AddMarkerForDocShell(static_cast<nsDocShell*>(aDocShell), aName, aTime, aTracingType);
 }
 
 void
 TimelineConsumers::AddMarkerForDocShell(nsIDocShell* aDocShell,
-                                        UniquePtr<TimelineMarker>&& aMarker)
+                                        UniquePtr<AbstractTimelineMarker>&& aMarker)
 {
+  MOZ_ASSERT(NS_IsMainThread());
   AddMarkerForDocShell(static_cast<nsDocShell*>(aDocShell), Move(aMarker));
 }
 
 void
+TimelineConsumers::AddOTMTMarkerForDocShell(nsIDocShell* aDocShell,
+                                            UniquePtr<AbstractTimelineMarker>& aMarker)
+{
+  MOZ_ASSERT(!NS_IsMainThread());
+  GetLock().AssertCurrentThreadOwns();
+  AddOTMTMarkerForDocShell(static_cast<nsDocShell*>(aDocShell), aMarker);
+}
+
+void
 TimelineConsumers::AddMarkerForDocShellsList(Vector<nsRefPtr<nsDocShell>>& aDocShells,
                                              const char* aName,
                                              MarkerTracingType aTracingType)
 {
+  MOZ_ASSERT(NS_IsMainThread());
   for (Vector<nsRefPtr<nsDocShell>>::Range range = aDocShells.all();
        !range.empty();
        range.popFront()) {
     AddMarkerForDocShell(range.front(), aName, aTracingType);
   }
 }
 
 void
+TimelineConsumers::AddMarkerForDocShellsList(Vector<nsRefPtr<nsDocShell>>& aDocShells,
+                                             const char* aName,
+                                             const TimeStamp& aTime,
+                                             MarkerTracingType aTracingType)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  for (Vector<nsRefPtr<nsDocShell>>::Range range = aDocShells.all();
+       !range.empty();
+       range.popFront()) {
+    AddMarkerForDocShell(range.front(), aName, aTime, aTracingType);
+  }
+}
+
+void
+TimelineConsumers::AddMarkerForDocShellsList(Vector<nsRefPtr<nsDocShell>>& aDocShells,
+                                             UniquePtr<AbstractTimelineMarker>& aMarker)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  for (Vector<nsRefPtr<nsDocShell>>::Range range = aDocShells.all();
+       !range.empty();
+       range.popFront()) {
+    UniquePtr<AbstractTimelineMarker> cloned = aMarker->Clone();
+    AddMarkerForDocShell(range.front(), Move(cloned));
+  }
+}
+
+void
+TimelineConsumers::AddOTMTMarkerForDocShellsList(Vector<nsRefPtr<nsDocShell>>& aDocShells,
+                                                 UniquePtr<AbstractTimelineMarker>& aMarker)
+{
+  MOZ_ASSERT(!NS_IsMainThread());
+  GetLock().AssertCurrentThreadOwns();
+  for (Vector<nsRefPtr<nsDocShell>>::Range range = aDocShells.all();
+       !range.empty();
+       range.popFront()) {
+    AddOTMTMarkerForDocShell(range.front(), aMarker);
+  }
+}
+
+void
 TimelineConsumers::AddMarkerForAllObservedDocShells(const char* aName,
                                                     MarkerTracingType aTracingType)
 {
+  MOZ_ASSERT(NS_IsMainThread());
   Vector<nsRefPtr<nsDocShell>> docShells;
-  if (!GetKnownDocShells(docShells)) {
-    // If we don't successfully populate our vector with *all* docshells being
-    // observed, don't add the marker to *any* of them.
-    return;
+  if (GetKnownDocShells(docShells)) {
+    AddMarkerForDocShellsList(docShells, aName, aTracingType);
+  }
+}
+
+void
+TimelineConsumers::AddMarkerForAllObservedDocShells(const char* aName,
+                                                    const TimeStamp& aTime,
+                                                    MarkerTracingType aTracingType)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  Vector<nsRefPtr<nsDocShell>> docShells;
+  if (GetKnownDocShells(docShells)) {
+    AddMarkerForDocShellsList(docShells, aName, aTime, aTracingType);
   }
+}
 
-  AddMarkerForDocShellsList(docShells, aName, aTracingType);
+void
+TimelineConsumers::AddMarkerForAllObservedDocShells(UniquePtr<AbstractTimelineMarker>& aMarker)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  Vector<nsRefPtr<nsDocShell>> docShells;
+  if (GetKnownDocShells(docShells)) {
+    AddMarkerForDocShellsList(docShells, aMarker);
+  }
+}
+
+void
+TimelineConsumers::AddOTMTMarkerForAllObservedDocShells(UniquePtr<AbstractTimelineMarker>& aMarker)
+{
+  MOZ_ASSERT(!NS_IsMainThread());
+  GetLock().AssertCurrentThreadOwns();
+  Vector<nsRefPtr<nsDocShell>> docShells;
+  if (GetKnownDocShells(docShells)) {
+    AddOTMTMarkerForDocShellsList(docShells, aMarker);
+  }
 }
 
 } // namespace mozilla
--- a/docshell/base/timeline/TimelineConsumers.h
+++ b/docshell/base/timeline/TimelineConsumers.h
@@ -6,35 +6,41 @@
 
 #ifndef mozilla_TimelineConsumers_h_
 #define mozilla_TimelineConsumers_h_
 
 #include "mozilla/UniquePtr.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/Vector.h"
 #include "mozilla/TimeStamp.h"
+#include "mozilla/Mutex.h"
 
 #include "TimelineMarkerEnums.h"
 
 class nsDocShell;
 class nsIDocShell;
 
 namespace mozilla {
 class ObservedDocShell;
-class TimelineMarker;
+class AbstractTimelineMarker;
 
 class TimelineConsumers
 {
 private:
   // Counter for how many timelines are currently interested in markers.
   static unsigned long sActiveConsumers;
   static LinkedList<ObservedDocShell>* sObservedDocShells;
   static LinkedList<ObservedDocShell>& GetOrCreateObservedDocShellsList();
 
+  // Lock used when adding off-the-main-thread markers.
+  static Mutex* sLock;
+
 public:
+  static Mutex& GetLock();
+
   static void AddConsumer(nsDocShell* aDocShell);
   static void RemoveConsumer(nsDocShell* aDocShell);
   static bool IsEmpty();
   static bool GetKnownDocShells(Vector<nsRefPtr<nsDocShell>>& aStore);
 
   // Methods for adding markers relevant for particular docshells, or generic
   // (meaning that they either can't be tied to a particular docshell, or one
   // wasn't accessible in the part of the codebase where they're instantiated).
@@ -60,26 +66,47 @@ public:
   static void AddMarkerForDocShell(nsIDocShell* aDocShell,
                                    const char* aName,
                                    const TimeStamp& aTime,
                                    MarkerTracingType aTracingType);
 
   // These methods register and receive ownership of an already created marker,
   // relevant for a specific docshell.
   static void AddMarkerForDocShell(nsDocShell* aDocShell,
-                                   UniquePtr<TimelineMarker>&& aMarker);
+                                   UniquePtr<AbstractTimelineMarker>&& aMarker);
   static void AddMarkerForDocShell(nsIDocShell* aDocShell,
-                                   UniquePtr<TimelineMarker>&& aMarker);
+                                   UniquePtr<AbstractTimelineMarker>&& aMarker);
 
-  // This method creates custom markers, relevant for a list of docshells.
+  // These methods create or clone markers relevant for a list of docshells.
   static void AddMarkerForDocShellsList(Vector<nsRefPtr<nsDocShell>>& aDocShells,
                                         const char* aName,
                                         MarkerTracingType aTracingType);
+  static void AddMarkerForDocShellsList(Vector<nsRefPtr<nsDocShell>>& aDocShells,
+                                        const char* aName,
+                                        const TimeStamp& aTime,
+                                        MarkerTracingType aTracingType);
+  static void AddMarkerForDocShellsList(Vector<nsRefPtr<nsDocShell>>& aDocShells,
+                                        UniquePtr<AbstractTimelineMarker>& aMarker);
 
-  // This method creates custom markers, none of which have to be tied to a
-  // particular docshell.
+  // These methods create or clone markers, none of which have to be tied to
+  // a particular docshell.
   static void AddMarkerForAllObservedDocShells(const char* aName,
                                                MarkerTracingType aTracingType);
+  static void AddMarkerForAllObservedDocShells(const char* aName,
+                                               const TimeStamp& aTime,
+                                               MarkerTracingType aTracingType);
+  static void AddMarkerForAllObservedDocShells(UniquePtr<AbstractTimelineMarker>& aMarker);
+
+  // Thread-safe versions of the above methods. Need to lock first using
+  // the mutex returned by `TimelineConsumers::GetLock()`.
+  static void AddOTMTMarkerForDocShell(nsDocShell* aDocShell,
+                                       UniquePtr<AbstractTimelineMarker>& aMarker);
+  static void AddOTMTMarkerForDocShell(nsIDocShell* aDocShell,
+                                       UniquePtr<AbstractTimelineMarker>& aMarker);
+  static void AddOTMTMarkerForDocShellsList(Vector<nsRefPtr<nsDocShell>>& aDocShells,
+                                            UniquePtr<AbstractTimelineMarker>& aMarker);
+  static void AddOTMTMarkerForAllObservedDocShells(UniquePtr<AbstractTimelineMarker>& aMarker);
+
 };
 
 } // namespace mozilla
 
 #endif /* mozilla_TimelineConsumers_h_ */
--- a/docshell/base/timeline/TimelineMarker.h
+++ b/docshell/base/timeline/TimelineMarker.h
@@ -2,17 +2,16 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* 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_TimelineMarker_h_
 #define mozilla_TimelineMarker_h_
 
-#include "nsContentUtils.h"
 #include "AbstractTimelineMarker.h"
 
 namespace mozilla {
 
 // Objects of this type can be added to the timeline if there is an interested
 // consumer. The class can also be subclassed to let a given marker creator
 // provide custom details.
 class TimelineMarker : public AbstractTimelineMarker
@@ -24,18 +23,17 @@ public:
 
   TimelineMarker(const char* aName,
                  const TimeStamp& aTime,
                  MarkerTracingType aTracingType,
                  MarkerStackRequest aStackRequest = MarkerStackRequest::STACK);
 
   virtual bool Equals(const AbstractTimelineMarker& aOther) override;
   virtual void AddDetails(JSContext* aCx, dom::ProfileTimelineMarker& aMarker) override;
-
-  JSObject* GetStack();
+  virtual JSObject* GetStack() override;
 
 protected:
   void CaptureStack();
 
 private:
   // While normally it is not a good idea to make a persistent root,
   // in this case changing nsDocShell to participate in cycle
   // collection was deemed too invasive, and the markers are only held
--- a/docshell/base/timeline/moz.build
+++ b/docshell/base/timeline/moz.build
@@ -8,16 +8,17 @@ EXPORTS.mozilla += [
     'AbstractTimelineMarker.h',
     'AutoGlobalTimelineMarker.h',
     'AutoTimelineMarker.h',
     'ConsoleTimelineMarker.h',
     'EventTimelineMarker.h',
     'JavascriptTimelineMarker.h',
     'LayerTimelineMarker.h',
     'ObservedDocShell.h',
+    'OTMTMarkerReceiver.h',
     'RestyleTimelineMarker.h',
     'TimelineConsumers.h',
     'TimelineMarker.h',
     'TimelineMarkerEnums.h',
     'TimestampTimelineMarker.h',
 ]
 
 UNIFIED_SOURCES += [