Bug 1050498 - Record compositing operations, r=jsantell,smaug,tromey
authorVictor Porof <vporof@mozilla.com>
Tue, 25 Aug 2015 08:51:58 +0300
changeset 291773 b9ae6b3addaf6d475010fef397be889941c1a15e
parent 291772 54ac9a920d8523827d07217964fd54c706e15a4b
child 291774 993fc891dfdaea4126b16567846d2b6d80f109ba
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)
reviewersjsantell, smaug, tromey
bugs1050498
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 1050498 - Record compositing operations, r=jsantell,smaug,tromey
browser/devtools/performance/modules/markers.js
browser/locales/en-US/chrome/browser/devtools/markers.properties
docshell/base/nsDocShell.h
docshell/base/timeline/AutoGlobalTimelineMarker.cpp
docshell/base/timeline/AutoTimelineMarker.cpp
docshell/base/timeline/ObservedDocShell.cpp
docshell/base/timeline/ObservedDocShell.h
docshell/base/timeline/TimelineConsumers.cpp
docshell/base/timeline/TimelineConsumers.h
docshell/base/timeline/TimelineMarker.cpp
docshell/base/timeline/TimelineMarker.h
docshell/base/timeline/moz.build
dom/ipc/TabChild.cpp
dom/ipc/TabChild.h
gfx/layers/client/ClientLayerManager.cpp
gfx/layers/client/ClientLayerManager.h
gfx/layers/ipc/CompositorChild.cpp
gfx/layers/ipc/CompositorChild.h
gfx/layers/ipc/CompositorParent.cpp
gfx/layers/ipc/CompositorParent.h
gfx/layers/ipc/PCompositor.ipdl
view/nsView.cpp
view/nsView.h
widget/nsIWidgetListener.cpp
widget/nsIWidgetListener.h
--- a/browser/devtools/performance/modules/markers.js
+++ b/browser/devtools/performance/modules/markers.js
@@ -68,16 +68,21 @@ const TIMELINE_BLUEPRINT = {
     colorName: "graphs-purple",
     label: L10N.getStr("marker.label.reflow"),
   },
   "Paint": {
     group: 0,
     colorName: "graphs-green",
     label: L10N.getStr("marker.label.paint"),
   },
+  "Composite": {
+    group: 0,
+    colorName: "graphs-green",
+    label: L10N.getStr("marker.label.composite"),
+  },
 
   /* Group 1 - JS */
   "DOMEvent": {
     group: 1,
     colorName: "graphs-yellow",
     label: L10N.getStr("marker.label.domevent"),
     fields: Formatters.DOMEventFields,
   },
--- a/browser/locales/en-US/chrome/browser/devtools/markers.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/markers.properties
@@ -12,16 +12,17 @@
 # are specifically for marker names in the performance tool.
 
 # LOCALIZATION NOTE (marker.label.*):
 # These strings are displayed in the Performance Tool waterfall, identifying markers.
 # We want to use the same wording as Google Chrome when appropriate.
 marker.label.styles=Recalculate Style
 marker.label.reflow=Layout
 marker.label.paint=Paint
+marker.label.composite=Composite Layers
 marker.label.javascript=Function Call
 marker.label.parseHTML=Parse HTML
 marker.label.parseXML=Parse XML
 marker.label.domevent=DOM Event
 marker.label.consoleTime=Console
 marker.label.garbageCollection=Incremental GC
 marker.label.garbageCollection.nonIncremental=Non-incremental GC
 marker.label.cycleCollection=Cycle Collection
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -273,19 +273,21 @@ private:
   // It is necessary to allow adding a timeline marker wherever a docshell
   // instance is available. This operation happens frequently and needs to
   // be very fast, so instead of using a Map or having to search for some
   // docshell-specific markers storage, a pointer to an `ObservedDocShell` is
   // is stored on docshells directly.
   friend void mozilla::TimelineConsumers::AddConsumer(nsDocShell* aDocShell);
   friend void mozilla::TimelineConsumers::RemoveConsumer(nsDocShell* aDocShell);
   friend void mozilla::TimelineConsumers::AddMarkerForDocShell(
-    nsDocShell* aDocShell, UniquePtr<TimelineMarker>&& aMarker);
+    nsDocShell* aDocShell, const char* aName, TracingMetadata aMetaData);
   friend void mozilla::TimelineConsumers::AddMarkerForDocShell(
-    nsDocShell* aDocShell, const char* aName, TracingMetadata aMetaData);
+    nsDocShell* aDocShell, const char* aName, const TimeStamp& aTime, TracingMetadata aMetaData);
+  friend void mozilla::TimelineConsumers::AddMarkerForDocShell(
+    nsDocShell* aDocShell, UniquePtr<TimelineMarker>&& aMarker);
 
 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/AutoGlobalTimelineMarker.cpp
+++ b/docshell/base/timeline/AutoGlobalTimelineMarker.cpp
@@ -1,17 +1,17 @@
 /* -*- 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 "mozilla/AutoGlobalTimelineMarker.h"
 
-#include "mozilla/TimelineConsumers.h"
+#include "TimelineConsumers.h"
 #include "MainThreadUtils.h"
 
 namespace mozilla {
 
 AutoGlobalTimelineMarker::AutoGlobalTimelineMarker(const char* aName
                                                    MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
   : mName(aName)
 {
--- a/docshell/base/timeline/AutoTimelineMarker.cpp
+++ b/docshell/base/timeline/AutoTimelineMarker.cpp
@@ -1,17 +1,17 @@
 /* -*- 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 "mozilla/AutoTimelineMarker.h"
 
-#include "mozilla/TimelineConsumers.h"
+#include "TimelineConsumers.h"
 #include "MainThreadUtils.h"
 #include "nsDocShell.h"
 
 namespace mozilla {
 
 AutoTimelineMarker::AutoTimelineMarker(nsIDocShell* aDocShell, const char* aName
                                        MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
   : mName(aName)
--- a/docshell/base/timeline/ObservedDocShell.cpp
+++ b/docshell/base/timeline/ObservedDocShell.cpp
@@ -11,23 +11,16 @@
 
 namespace mozilla {
 
 ObservedDocShell::ObservedDocShell(nsDocShell* aDocShell)
   : mDocShell(aDocShell)
 {}
 
 void
-ObservedDocShell::AddMarker(const char* aName, TracingMetadata aMetaData)
-{
-  TimelineMarker* marker = new TimelineMarker(mDocShell, aName, aMetaData);
-  mTimelineMarkers.AppendElement(marker);
-}
-
-void
 ObservedDocShell::AddMarker(UniquePtr<TimelineMarker>&& aMarker)
 {
   mTimelineMarkers.AppendElement(Move(aMarker));
 }
 
 void
 ObservedDocShell::ClearMarkers()
 {
--- a/docshell/base/timeline/ObservedDocShell.h
+++ b/docshell/base/timeline/ObservedDocShell.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 ObservedDocShell_h_
 #define ObservedDocShell_h_
 
-#include "GeckoProfiler.h"
 #include "nsTArray.h"
 #include "mozilla/nsRefPtr.h"
 
 class nsDocShell;
 class TimelineMarker;
 
 namespace mozilla {
 namespace dom {
@@ -28,17 +27,16 @@ class ObservedDocShell : public LinkedLi
 private:
   nsRefPtr<nsDocShell> mDocShell;
   nsTArray<UniquePtr<TimelineMarker>> mTimelineMarkers;
 
 public:
   explicit ObservedDocShell(nsDocShell* aDocShell);
   nsDocShell* operator*() const { return mDocShell.get(); }
 
-  void AddMarker(const char* aName, TracingMetadata aMetaData);
   void AddMarker(UniquePtr<TimelineMarker>&& aMarker);
   void ClearMarkers();
   void PopMarkers(JSContext* aCx, nsTArray<dom::ProfileTimelineMarker>& aStore);
 };
 
 } // namespace mozilla
 
 #endif /* ObservedDocShell_h_ */
--- a/docshell/base/timeline/TimelineConsumers.cpp
+++ b/docshell/base/timeline/TimelineConsumers.cpp
@@ -62,45 +62,83 @@ TimelineConsumers::GetKnownDocShells(Vec
     }
   }
 
   return true;
 }
 
 void
 TimelineConsumers::AddMarkerForDocShell(nsDocShell* aDocShell,
+                                        const char* aName,
+                                        TracingMetadata aMetaData)
+{
+  if (aDocShell->IsObserved()) {
+    aDocShell->mObserved->AddMarker(Move(MakeUnique<TimelineMarker>(aDocShell, aName, aMetaData)));
+  }
+}
+
+void
+TimelineConsumers::AddMarkerForDocShell(nsDocShell* aDocShell,
+                                        const char* aName,
+                                        const TimeStamp& aTime,
+                                        TracingMetadata aMetaData)
+{
+  if (aDocShell->IsObserved()) {
+    aDocShell->mObserved->AddMarker(Move(MakeUnique<TimelineMarker>(aDocShell, aName, aTime, aMetaData)));
+  }
+}
+
+void
+TimelineConsumers::AddMarkerForDocShell(nsDocShell* aDocShell,
                                         UniquePtr<TimelineMarker>&& aMarker)
 {
   if (aDocShell->IsObserved()) {
     aDocShell->mObserved->AddMarker(Move(aMarker));
   }
 }
 
 void
-TimelineConsumers::AddMarkerForDocShell(nsDocShell* aDocShell,
-                                        const char* aName, TracingMetadata aMetaData)
+TimelineConsumers::AddMarkerForDocShell(nsIDocShell* aDocShell,
+                                        const char* aName,
+                                        TracingMetadata aMetaData)
 {
-  if (aDocShell->IsObserved()) {
-    aDocShell->mObserved->AddMarker(aName, aMetaData);
-  }
+  AddMarkerForDocShell(static_cast<nsDocShell*>(aDocShell), aName, aMetaData);
+}
+
+void
+TimelineConsumers::AddMarkerForDocShell(nsIDocShell* aDocShell,
+                                        const char* aName,
+                                        const TimeStamp& aTime,
+                                        TracingMetadata aMetaData)
+{
+  AddMarkerForDocShell(static_cast<nsDocShell*>(aDocShell), aName, aTime, aMetaData);
+}
+
+void
+TimelineConsumers::AddMarkerForDocShell(nsIDocShell* aDocShell,
+                                        UniquePtr<TimelineMarker>&& aMarker)
+{
+  AddMarkerForDocShell(static_cast<nsDocShell*>(aDocShell), Move(aMarker));
 }
 
 void
 TimelineConsumers::AddMarkerForDocShellsList(Vector<nsRefPtr<nsDocShell>>& aDocShells,
-                                             const char* aName, TracingMetadata aMetaData)
+                                             const char* aName,
+                                             TracingMetadata aMetaData)
 {
   for (Vector<nsRefPtr<nsDocShell>>::Range range = aDocShells.all();
        !range.empty();
        range.popFront()) {
     AddMarkerForDocShell(range.front(), aName, aMetaData);
   }
 }
 
 void
-TimelineConsumers::AddMarkerForAllObservedDocShells(const char* aName, TracingMetadata aMetaData)
+TimelineConsumers::AddMarkerForAllObservedDocShells(const char* aName,
+                                                    TracingMetadata aMetaData)
 {
   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;
   }
 
--- a/docshell/base/timeline/TimelineConsumers.h
+++ b/docshell/base/timeline/TimelineConsumers.h
@@ -5,43 +5,79 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_TimelineConsumers_h_
 #define mozilla_TimelineConsumers_h_
 
 #include "mozilla/UniquePtr.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/Vector.h"
-#include "timeline/ObservedDocShell.h"
+#include "GeckoProfiler.h"
 
 class nsDocShell;
+class nsIDocShell;
+class TimelineMarker;
 
 namespace mozilla {
+class ObservedDocShell;
 
 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();
 
 public:
   static void AddConsumer(nsDocShell* aDocShell);
   static void RemoveConsumer(nsDocShell* aDocShell);
   static bool IsEmpty();
   static bool GetKnownDocShells(Vector<nsRefPtr<nsDocShell>>& aStore);
 
-  // Methods for adding markers to appropriate docshells. These will only add
-  // markers if the docshell is currently being observed by a timeline.
+  // 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).
+
+  // These will only add markers if at least one docshell is currently being
+  // observed by a timeline. Markers tied to a particular docshell won't be
+  // created unless that docshell is specifically being currently observed.
   // See nsIDocShell::recordProfileTimelineMarkers
+
+  // These methods create a custom marker from a name and some metadata,
+  // relevant for a specific docshell.
+  static void AddMarkerForDocShell(nsDocShell* aDocShell,
+                                   const char* aName,
+                                   TracingMetadata aMetaData);
+  static void AddMarkerForDocShell(nsIDocShell* aDocShell,
+                                   const char* aName,
+                                   TracingMetadata aMetaData);
+
+  static void AddMarkerForDocShell(nsDocShell* aDocShell,
+                                   const char* aName,
+                                   const TimeStamp& aTime,
+                                   TracingMetadata aMetaData);
+  static void AddMarkerForDocShell(nsIDocShell* aDocShell,
+                                   const char* aName,
+                                   const TimeStamp& aTime,
+                                   TracingMetadata aMetaData);
+
+  // These methods register and receive ownership of an already created marker,
+  // relevant for a specific docshell.
   static void AddMarkerForDocShell(nsDocShell* aDocShell,
                                    UniquePtr<TimelineMarker>&& aMarker);
-  static void AddMarkerForDocShell(nsDocShell* aDocShell,
-                                   const char* aName, TracingMetadata aMetaData);
+  static void AddMarkerForDocShell(nsIDocShell* aDocShell,
+                                   UniquePtr<TimelineMarker>&& aMarker);
+
+  // This method creates custom markers, relevant for a list of docshells.
   static void AddMarkerForDocShellsList(Vector<nsRefPtr<nsDocShell>>& aDocShells,
-                                        const char* aName, TracingMetadata aMetaData);
-  static void AddMarkerForAllObservedDocShells(const char* aName, TracingMetadata aMetaData);
+                                        const char* aName,
+                                        TracingMetadata aMetaData);
+
+  // This method creates custom markers, none of which have to be tied to a
+  // particular docshell.
+  static void AddMarkerForAllObservedDocShells(const char* aName,
+                                               TracingMetadata aMetaData);
 };
 
 } // namespace mozilla
 
 #endif /* mozilla_TimelineConsumers_h_ */
--- a/docshell/base/timeline/TimelineMarker.cpp
+++ b/docshell/base/timeline/TimelineMarker.cpp
@@ -16,16 +16,25 @@ TimelineMarker::TimelineMarker(nsDocShel
   MOZ_ASSERT(aName);
   aDocShell->Now(&mTime);
   if (aMetaData == TRACING_INTERVAL_START || aMetaData == TRACING_TIMESTAMP) {
     CaptureStack();
   }
 }
 
 TimelineMarker::TimelineMarker(nsDocShell* aDocShell, const char* aName,
+                               const mozilla::TimeStamp& aTime,
+                               TracingMetadata aMetaData)
+  : TimelineMarker(aDocShell, aName, aMetaData)
+{
+  bool isInconsistent = false;
+  mTime = (aTime - mozilla::TimeStamp::ProcessCreation(isInconsistent)).ToMilliseconds();
+}
+
+TimelineMarker::TimelineMarker(nsDocShell* aDocShell, const char* aName,
                                TracingMetadata aMetaData,
                                const nsAString& aCause,
                                TimelineStackRequest aStackRequest)
   : mName(aName)
   , mMetaData(aMetaData)
   , mCause(aCause)
 {
   MOZ_COUNT_CTOR(TimelineMarker);
--- a/docshell/base/timeline/TimelineMarker.h
+++ b/docshell/base/timeline/TimelineMarker.h
@@ -22,16 +22,20 @@ class TimelineMarker
 {
 public:
   enum TimelineStackRequest { STACK, NO_STACK };
 
   TimelineMarker(nsDocShell* aDocShell, const char* aName,
                  TracingMetadata aMetaData);
 
   TimelineMarker(nsDocShell* aDocShell, const char* aName,
+                 const mozilla::TimeStamp& aTime,
+                 TracingMetadata aMetaData);
+
+  TimelineMarker(nsDocShell* aDocShell, const char* aName,
                  TracingMetadata aMetaData,
                  const nsAString& aCause,
                  TimelineStackRequest aStackRequest = STACK);
 
   virtual ~TimelineMarker();
 
   // Check whether two markers should be considered the same,
   // for the purpose of pairing start and end markers.  Normally
--- a/docshell/base/timeline/moz.build
+++ b/docshell/base/timeline/moz.build
@@ -2,17 +2,19 @@
 # vim: set filetype=python:
 # 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/.
 
 EXPORTS.mozilla += [
     'AutoGlobalTimelineMarker.h',
     'AutoTimelineMarker.h',
+    'ObservedDocShell.h',
     'TimelineConsumers.h',
+    'TimelineMarker.h',
 ]
 
 UNIFIED_SOURCES += [
     'AutoGlobalTimelineMarker.cpp',
     'AutoTimelineMarker.cpp',
     'ObservedDocShell.cpp',
     'TimelineConsumers.cpp',
     'TimelineMarker.cpp',
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -2857,26 +2857,29 @@ TabChild::GetFrom(uint64_t aLayersId)
 {
   if (!sTabChildren) {
     return nullptr;
   }
   return sTabChildren->Get(aLayersId);
 }
 
 void
-TabChild::DidComposite(uint64_t aTransactionId)
+TabChild::DidComposite(uint64_t aTransactionId,
+                       const TimeStamp& aCompositeStart,
+                       const TimeStamp& aCompositeEnd)
 {
   MOZ_ASSERT(mPuppetWidget);
   MOZ_ASSERT(mPuppetWidget->GetLayerManager());
   MOZ_ASSERT(mPuppetWidget->GetLayerManager()->GetBackendType() ==
                LayersBackend::LAYERS_CLIENT);
 
   ClientLayerManager *manager =
     static_cast<ClientLayerManager*>(mPuppetWidget->GetLayerManager());
-  manager->DidComposite(aTransactionId);
+
+  manager->DidComposite(aTransactionId, aCompositeStart, aCompositeEnd);
 }
 
 void
 TabChild::ClearCachedResources()
 {
   MOZ_ASSERT(mPuppetWidget);
   MOZ_ASSERT(mPuppetWidget->GetLayerManager());
   MOZ_ASSERT(mPuppetWidget->GetLayerManager()->GetBackendType() ==
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -456,17 +456,20 @@ public:
     {
       nsCOMPtr<nsITabChild> tc = do_GetInterface(aDocShell);
       return static_cast<TabChild*>(tc.get());
     }
 
     static TabChild* GetFrom(nsIPresShell* aPresShell);
     static TabChild* GetFrom(uint64_t aLayersId);
 
-    void DidComposite(uint64_t aTransactionId);
+    void DidComposite(uint64_t aTransactionId,
+                      const TimeStamp& aCompositeStart,
+                      const TimeStamp& aCompositeEnd);
+
     void ClearCachedResources();
 
     static inline TabChild*
     GetFrom(nsIDOMWindow* aWindow)
     {
       nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(aWindow);
       nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(webNav);
       return GetFrom(docShell);
--- a/gfx/layers/client/ClientLayerManager.cpp
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -105,17 +105,18 @@ ClientLayerManager::ClientLayerManager(n
 {
   MOZ_COUNT_CTOR(ClientLayerManager);
   mMemoryPressureObserver = new MemoryPressureObserver(this);
 }
 
 ClientLayerManager::~ClientLayerManager()
 {
   if (mTransactionIdAllocator) {
-    DidComposite(mLatestTransactionId);
+    TimeStamp now = TimeStamp::Now();
+    DidComposite(mLatestTransactionId, now, now);
   }
   mMemoryPressureObserver->Destroy();
   ClearCachedResources();
   // Stop receiveing AsyncParentMessage at Forwarder.
   // After the call, the message is directly handled by LayerTransactionChild. 
   // Basically this function should be called in ShadowLayerForwarder's
   // destructor. But when the destructor is triggered by 
   // CompositorChild::Destroy(), the destructor can not handle it correctly.
@@ -379,26 +380,28 @@ ClientLayerManager::GetCompositorChild()
 
 void
 ClientLayerManager::Composite()
 {
   mForwarder->Composite();
 }
 
 void
-ClientLayerManager::DidComposite(uint64_t aTransactionId)
+ClientLayerManager::DidComposite(uint64_t aTransactionId,
+                                 const TimeStamp& aCompositeStart,
+                                 const TimeStamp& aCompositeEnd)
 {
   MOZ_ASSERT(mWidget);
   nsIWidgetListener *listener = mWidget->GetWidgetListener();
   if (listener) {
-    listener->DidCompositeWindow();
+    listener->DidCompositeWindow(aCompositeStart, aCompositeEnd);
   }
   listener = mWidget->GetAttachedWidgetListener();
   if (listener) {
-    listener->DidCompositeWindow();
+    listener->DidCompositeWindow(aCompositeStart, aCompositeEnd);
   }
   mTransactionIdAllocator->NotifyTransactionCompleted(aTransactionId);
 }
 
 void
 ClientLayerManager::GetCompositorSideAPZTestData(APZTestData* aData) const
 {
   if (mForwarder->HasShadowManager()) {
--- a/gfx/layers/client/ClientLayerManager.h
+++ b/gfx/layers/client/ClientLayerManager.h
@@ -198,17 +198,19 @@ public:
   }
   bool NeedsComposite() const { return mNeedsComposite; }
 
   virtual void Composite() override;
   virtual void GetFrameUniformity(FrameUniformityData* aFrameUniformityData) override;
   virtual bool RequestOverfill(mozilla::dom::OverfillCallback* aCallback) override;
   virtual void RunOverfillCallback(const uint32_t aOverfill) override;
 
-  void DidComposite(uint64_t aTransactionId);
+  void DidComposite(uint64_t aTransactionId,
+                    const mozilla::TimeStamp& aCompositeStart,
+                    const mozilla::TimeStamp& aCompositeEnd);
 
   virtual bool SupportsMixBlendModes(EnumSet<gfx::CompositionOp>& aMixBlendModes) override
   {
    return (GetTextureFactoryIdentifier().mSupportedBlendModes & aMixBlendModes) == aMixBlendModes;
   }
 
   virtual bool AreComponentAlphaLayersEnabled() override;
 
--- a/gfx/layers/ipc/CompositorChild.cpp
+++ b/gfx/layers/ipc/CompositorChild.cpp
@@ -340,25 +340,27 @@ CompositorChild::RecvUpdatePluginVisibil
 #else
   MOZ_ASSERT(NS_IsMainThread());
   nsIWidget::UpdateRegisteredPluginWindowVisibility(aOwnerWidget, aVisibleIdList);
   return true;
 #endif // !defined(XP_WIN) && !defined(MOZ_WIDGET_GTK)
 }
 
 bool
-CompositorChild::RecvDidComposite(const uint64_t& aId, const uint64_t& aTransactionId)
+CompositorChild::RecvDidComposite(const uint64_t& aId, const uint64_t& aTransactionId,
+                                  const TimeStamp& aCompositeStart,
+                                  const TimeStamp& aCompositeEnd)
 {
   if (mLayerManager) {
     MOZ_ASSERT(aId == 0);
-    mLayerManager->DidComposite(aTransactionId);
+    mLayerManager->DidComposite(aTransactionId, aCompositeStart, aCompositeEnd);
   } else if (aId != 0) {
     dom::TabChild *child = dom::TabChild::GetFrom(aId);
     if (child) {
-      child->DidComposite(aTransactionId);
+      child->DidComposite(aTransactionId, aCompositeStart, aCompositeEnd);
     }
   }
   return true;
 }
 
 bool
 CompositorChild::RecvOverfill(const uint32_t &aOverfill)
 {
--- a/gfx/layers/ipc/CompositorChild.h
+++ b/gfx/layers/ipc/CompositorChild.h
@@ -69,17 +69,19 @@ public:
   static bool ChildProcessHasCompositor() { return sCompositor != nullptr; }
 
   void AddOverfillObserver(ClientLayerManager* aLayerManager);
 
   virtual bool
   RecvClearCachedResources(const uint64_t& id) override;
 
   virtual bool
-  RecvDidComposite(const uint64_t& aId, const uint64_t& aTransactionId) override;
+  RecvDidComposite(const uint64_t& aId, const uint64_t& aTransactionId,
+                   const TimeStamp& aCompositeStart,
+                   const TimeStamp& aCompositeEnd) override;
 
   virtual bool
   RecvInvalidateAll() override;
 
   virtual bool
   RecvOverfill(const uint32_t &aOverfill) override;
 
   virtual bool
--- a/gfx/layers/ipc/CompositorParent.cpp
+++ b/gfx/layers/ipc/CompositorParent.cpp
@@ -826,17 +826,19 @@ CompositorParent::PauseComposition()
              "PauseComposition() can only be called on the compositor thread");
 
   MonitorAutoLock lock(mPauseCompositionMonitor);
 
   if (!mPaused) {
     mPaused = true;
 
     mCompositor->Pause();
-    DidComposite();
+
+    TimeStamp now = TimeStamp::Now();
+    DidComposite(now, now);
   }
 
   // if anyone's waiting to make sure that composition really got paused, tell them
   lock.NotifyAll();
 }
 
 void
 CompositorParent::ResumeComposition()
@@ -1029,17 +1031,18 @@ CompositorParent::CompositeToTarget(Draw
   if (scheduleDelta > TimeDuration::FromMilliseconds(2) ||
       scheduleDelta < TimeDuration::FromMilliseconds(-2)) {
     printf_stderr("Compositor: Compose starting off schedule by %4.1f ms\n",
                   scheduleDelta.ToMilliseconds());
   }
 #endif
 
   if (!CanComposite()) {
-    DidComposite();
+    TimeStamp end = TimeStamp::Now();
+    DidComposite(start, end);
     return;
   }
 
   AutoResolveRefLayers resolve(mCompositionManager);
 
   if (aTarget) {
     mLayerManager->BeginTransactionWithDrawTarget(aTarget, *aRect);
   } else {
@@ -1072,17 +1075,18 @@ CompositorParent::CompositeToTarget(Draw
     printf_stderr("Painting --- compositing layer tree:\n");
     mLayerManager->Dump();
   }
 #endif
   mLayerManager->SetDebugOverlayWantsNextFrame(false);
   mLayerManager->EndTransaction(time);
 
   if (!aTarget) {
-    DidComposite();
+    TimeStamp end = TimeStamp::Now();
+    DidComposite(start, end);
   }
 
   // We're not really taking advantage of the stored composite-again-time here.
   // We might be able to skip the next few composites altogether. However,
   // that's a bit complex to implement and we'll get most of the advantage
   // by skipping compositing when we detect there's nothing invalid. This is why
   // we do "composite until" rather than "composite again at".
   if (!mCompositor->GetCompositeUntilTime().IsNull() ||
@@ -1188,17 +1192,18 @@ CompositorParent::ShadowLayersUpdated(La
   mPendingTransaction = aTransactionId;
 
   if (root) {
     SetShadowProperties(root);
   }
   if (aScheduleComposite) {
     ScheduleComposition();
     if (mPaused) {
-      DidComposite();
+      TimeStamp now = TimeStamp::Now();
+      DidComposite(now, now);
     }
   }
   mLayerManager->NotifyShadowTreeTransaction();
 }
 
 void
 CompositorParent::ForceComposite(LayerTransactionParent* aLayerTree)
 {
@@ -1221,17 +1226,18 @@ CompositorParent::SetTestSampleTime(Laye
 
   // Update but only if we were already scheduled to animate
   if (testComposite) {
     AutoResolveRefLayers resolve(mCompositionManager);
     bool requestNextFrame = mCompositionManager->TransformShadowTree(aTime);
     if (!requestNextFrame) {
       CancelCurrentCompositeTask();
       // Pretend we composited in case someone is wating for this event.
-      DidComposite();
+      TimeStamp now = TimeStamp::Now();
+      DidComposite(now, now);
     }
   }
 
   return true;
 }
 
 void
 CompositorParent::LeaveTestMode(LayerTransactionParent* aLayerTree)
@@ -1253,17 +1259,18 @@ CompositorParent::ApplyAsyncProperties(L
 
     TimeStamp time = mIsTesting ? mTestTime : mCompositorScheduler->GetLastComposeTime();
     bool requestNextFrame =
       mCompositionManager->TransformShadowTree(time,
         AsyncCompositionManager::TransformsToSkip::APZ);
     if (!requestNextFrame) {
       CancelCurrentCompositeTask();
       // Pretend we composited in case someone is waiting for this event.
-      DidComposite();
+      TimeStamp now = TimeStamp::Now();
+      DidComposite(now, now);
     }
   }
 }
 
 bool
 CompositorParent::RecvGetFrameUniformity(FrameUniformityData* aOutData)
 {
   mCompositionManager->GetFrameUniformity(aOutData);
@@ -1712,17 +1719,19 @@ public:
   virtual void GetAPZTestData(const LayerTransactionParent* aLayerTree,
                               APZTestData* aOutData) override;
   virtual void SetConfirmedTargetAPZC(const LayerTransactionParent* aLayerTree,
                                       const uint64_t& aInputBlockId,
                                       const nsTArray<ScrollableLayerGuid>& aTargets) override;
 
   virtual AsyncCompositionManager* GetCompositionManager(LayerTransactionParent* aParent) override;
 
-  void DidComposite(uint64_t aId);
+  void DidComposite(uint64_t aId,
+                    TimeStamp& aCompositeStart,
+                    TimeStamp& aCompositeEnd);
 
 private:
   // Private destructor, to discourage deletion outside of Release():
   virtual ~CrossProcessCompositorParent();
 
   void DeferredDestroy();
 
   // There can be many CPCPs, and IPDL-generated code doesn't hold a
@@ -1733,36 +1742,38 @@ private:
 
   nsRefPtr<CompositorThreadHolder> mCompositorThreadHolder;
   // If true, we should send a RemotePaintIsReady message when the layer transaction
   // is received
   bool mNotifyAfterRemotePaint;
 };
 
 void
-CompositorParent::DidComposite()
+CompositorParent::DidComposite(TimeStamp& aCompositeStart,
+                               TimeStamp& aCompositeEnd)
 {
   if (mPendingTransaction) {
-    unused << SendDidComposite(0, mPendingTransaction);
+    unused << SendDidComposite(0, mPendingTransaction, aCompositeStart, aCompositeEnd);
     mPendingTransaction = 0;
   }
   if (mLayerManager) {
     nsTArray<ImageCompositeNotification> notifications;
     mLayerManager->ExtractImageCompositeNotifications(&notifications);
     if (!notifications.IsEmpty()) {
       unused << ImageBridgeParent::NotifyImageComposites(notifications);
     }
   }
 
   MonitorAutoLock lock(*sIndirectLayerTreesLock);
   for (LayerTreeMap::iterator it = sIndirectLayerTrees.begin();
        it != sIndirectLayerTrees.end(); it++) {
     LayerTreeState* lts = &it->second;
     if (lts->mParent == this && lts->mCrossProcessParent) {
-      static_cast<CrossProcessCompositorParent*>(lts->mCrossProcessParent)->DidComposite(it->first);
+      static_cast<CrossProcessCompositorParent*>(lts->mCrossProcessParent)->DidComposite(
+        it->first, aCompositeStart, aCompositeEnd);
     }
   }
 }
 
 static void
 OpenCompositor(CrossProcessCompositorParent* aCompositor,
                Transport* aTransport, ProcessId aOtherPid,
                MessageLoop* aIOLoop)
@@ -2036,22 +2047,24 @@ UpdatePluginWindowState(uint64_t aId)
     // Clear because there's no recovering from this until we receive
     // new shadow layer plugin data in ShadowLayersUpdated.
     lts.mPluginData.Clear();
   }
 }
 #endif // #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
 
 void
-CrossProcessCompositorParent::DidComposite(uint64_t aId)
+CrossProcessCompositorParent::DidComposite(uint64_t aId,
+                                           TimeStamp& aCompositeStart,
+                                           TimeStamp& aCompositeEnd)
 {
   sIndirectLayerTreesLock->AssertCurrentThreadOwns();
   LayerTransactionParent *layerTree = sIndirectLayerTrees[aId].mLayerTree;
   if (layerTree && layerTree->GetPendingTransactionId()) {
-    unused << SendDidComposite(aId, layerTree->GetPendingTransactionId());
+    unused << SendDidComposite(aId, layerTree->GetPendingTransactionId(), aCompositeStart, aCompositeEnd);
     layerTree->SetPendingTransactionId(0);
   }
 #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
   UpdatePluginWindowState(aId);
 #endif
 }
 
 void
--- a/gfx/layers/ipc/CompositorParent.h
+++ b/gfx/layers/ipc/CompositorParent.h
@@ -434,17 +434,17 @@ protected:
   static CompositorParent* RemoveCompositor(uint64_t id);
 
    /**
    * Return true if current state allows compositing, that is
    * finishing a layers transaction.
    */
   bool CanComposite();
 
-  void DidComposite();
+  void DidComposite(TimeStamp& aCompositeStart, TimeStamp& aCompositeEnd);
 
   nsRefPtr<LayerManagerComposite> mLayerManager;
   nsRefPtr<Compositor> mCompositor;
   RefPtr<AsyncCompositionManager> mCompositionManager;
   nsIWidget* mWidget;
   TimeStamp mTestTime;
   bool mIsTesting;
 
--- a/gfx/layers/ipc/PCompositor.ipdl
+++ b/gfx/layers/ipc/PCompositor.ipdl
@@ -40,17 +40,18 @@ child:
   // The child should invalidate everything so that the whole window is redrawn.
   async InvalidateAll();
 
   // The compositor completed a layers transaction. id is the layers id
   // of the child layer tree that was composited (or 0 when notifying
   // the root layer tree).
   // transactionId is the id of the transaction before this composite, or 0
   // if there was no transaction since the last composite.
-  async DidComposite(uint64_t id, uint64_t transactionId);
+  async DidComposite(uint64_t id, uint64_t transactionId,
+                     TimeStamp compositeStart, TimeStamp compositeEnd);
 
   // The parent sends the child the requested fill ratio numbers.
   async Overfill(uint32_t aOverfill);
 
   /**
    * Parent informs the child that the graphics objects are ready for
    * compositing.  This usually means that the graphics objects (textures
    * and the like) are available on the GPU.  This is used for chrome UI.
--- a/view/nsView.cpp
+++ b/view/nsView.cpp
@@ -13,16 +13,17 @@
 #include "mozilla/Poison.h"
 #include "nsIWidget.h"
 #include "nsViewManager.h"
 #include "nsIFrame.h"
 #include "nsPresArena.h"
 #include "nsXULPopupManager.h"
 #include "nsIWidgetListener.h"
 #include "nsContentUtils.h" // for nsAutoScriptBlocker
+#include "mozilla/TimelineConsumers.h"
 
 using namespace mozilla;
 
 nsView::nsView(nsViewManager* aViewManager, nsViewVisibility aVisibility)
 {
   MOZ_COUNT_CTOR(nsView);
 
   mVis = aVisibility;
@@ -1066,22 +1067,38 @@ nsView::PaintWindow(nsIWidget* aWidget, 
 void
 nsView::DidPaintWindow()
 {
   nsRefPtr<nsViewManager> vm = mViewManager;
   vm->DidPaintWindow();
 }
 
 void
-nsView::DidCompositeWindow()
+nsView::DidCompositeWindow(const TimeStamp& aCompositeStart,
+                           const TimeStamp& aCompositeEnd)
 {
   nsIPresShell* presShell = mViewManager->GetPresShell();
   if (presShell) {
     nsAutoScriptBlocker scriptBlocker;
-    presShell->GetPresContext()->GetDisplayRootPresContext()->GetRootPresContext()->NotifyDidPaintForSubtree(nsIPresShell::PAINT_COMPOSITE);
+
+    nsPresContext* context = presShell->GetPresContext();
+    context->GetDisplayRootPresContext()->GetRootPresContext()->NotifyDidPaintForSubtree(nsIPresShell::PAINT_COMPOSITE);
+
+    // If the two timestamps are identical, this was likely a fake composite
+    // event which wouldn't be terribly useful to display.
+    if (aCompositeStart == aCompositeEnd) {
+      return;
+    }
+
+    nsIDocShell* docShell = context->GetDocShell();
+
+    TimelineConsumers::AddMarkerForDocShell(docShell,
+      "Composite", aCompositeStart, TRACING_INTERVAL_START);
+    TimelineConsumers::AddMarkerForDocShell(docShell,
+      "Composite", aCompositeEnd, TRACING_INTERVAL_END);
   }
 }
 
 void
 nsView::RequestRepaint()
 {
   nsIPresShell* presShell = mViewManager->GetPresShell();
   if (presShell) {
--- a/view/nsView.h
+++ b/view/nsView.h
@@ -376,17 +376,18 @@ public:
   virtual nsIPresShell* GetPresShell() override;
   virtual nsView* GetView() override { return this; }
   virtual bool WindowMoved(nsIWidget* aWidget, int32_t x, int32_t y) override;
   virtual bool WindowResized(nsIWidget* aWidget, int32_t aWidth, int32_t aHeight) override;
   virtual bool RequestWindowClose(nsIWidget* aWidget) override;
   virtual void WillPaintWindow(nsIWidget* aWidget) override;
   virtual bool PaintWindow(nsIWidget* aWidget, nsIntRegion aRegion) override;
   virtual void DidPaintWindow() override;
-  virtual void DidCompositeWindow() override;
+  virtual void DidCompositeWindow(const mozilla::TimeStamp& aCompositeStart,
+                                  const mozilla::TimeStamp& aCompositeEnd) override;
   virtual void RequestRepaint() override;
   virtual nsEventStatus HandleEvent(mozilla::WidgetGUIEvent* aEvent,
                                     bool aUseAttachedEvents) override;
 
   virtual ~nsView();
 
   nsPoint GetOffsetTo(const nsView* aOther, const int32_t aAPD) const;
   nsIWidget* GetNearestWidget(nsPoint* aOffset, const int32_t aAPD) const;
--- a/widget/nsIWidgetListener.cpp
+++ b/widget/nsIWidgetListener.cpp
@@ -102,17 +102,18 @@ nsIWidgetListener::PaintWindow(nsIWidget
 }
 
 void
 nsIWidgetListener::DidPaintWindow()
 {
 }
 
 void
-nsIWidgetListener::DidCompositeWindow()
+nsIWidgetListener::DidCompositeWindow(const TimeStamp& aCompositeStart,
+                                      const TimeStamp& aCompositeEnd)
 {
 }
 
 void
 nsIWidgetListener::RequestRepaint()
 {
 }
 
--- a/widget/nsIWidgetListener.h
+++ b/widget/nsIWidgetListener.h
@@ -3,16 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsIWidgetListener_h__
 #define nsIWidgetListener_h__
 
 #include <stdint.h>
 
 #include "mozilla/EventForwards.h"
+#include "mozilla/TimeStamp.h"
 
 class nsView;
 class nsIntRegion;
 class nsIPresShell;
 class nsIWidget;
 class nsIXULWindow;
 
 /**
@@ -130,17 +131,18 @@ public:
   /**
    * Indicates that a paint occurred.
    * This is called at a time when it is OK to change the geometry of
    * this widget or of other widgets.
    * Must be called after every call to PaintWindow.
    */
   virtual void DidPaintWindow();
 
-  virtual void DidCompositeWindow();
+  virtual void DidCompositeWindow(const mozilla::TimeStamp& aCompositeStart,
+                                  const mozilla::TimeStamp& aCompositeEnd);
 
   /**
    * Request that layout schedules a repaint on the next refresh driver tick.
    */
   virtual void RequestRepaint();
 
   /**
    * Handle an event.