Bug 1050498 - Record compositing operations, r=jsantell,smaug,tromey
authorVictor Porof <vporof@mozilla.com>
Tue, 25 Aug 2015 08:51:58 +0300
changeset 287667 b9ae6b3addaf6d475010fef397be889941c1a15e
parent 287666 54ac9a920d8523827d07217964fd54c706e15a4b
child 287668 993fc891dfdaea4126b16567846d2b6d80f109ba
push id4738
push usersteffen.wilberg@web.de
push dateTue, 25 Aug 2015 19:49:56 +0000
reviewersjsantell, smaug, tromey
bugs1050498
milestone43.0a1
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.