Bug 1183461 part 7 - Add EventInfoComparator and sort events; r=heycam
authorBrian Birtles <birtles@gmail.com>
Tue, 15 Sep 2015 14:05:49 +0900
changeset 295391 95e0fae6c3e2e243dd22ba01fa8d4a2b76a26856
parent 295390 e3d6aa401ec04d51a251228a11b79e472dc97e40
child 295392 5d135736c0a11630113a1e1a74a70b12f9dbf1ec
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)
reviewersheycam
bugs1183461
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 1183461 part 7 - Add EventInfoComparator and sort events; r=heycam This patch also reworks the dispatch of events in nsRefreshDriver. Previously the refresh driver would dispatch the transition events for all subdocuments then the animation events. This arrangement is complicated and not obviously necessary. This patch simplifies this arrangement by dispatching transition events and animation events for each document before proceeding to subdocuments.
layout/base/nsRefreshDriver.cpp
layout/style/AnimationCommon.cpp
layout/style/AnimationCommon.h
layout/style/nsAnimationManager.h
layout/style/nsTransitionManager.h
--- a/layout/base/nsRefreshDriver.cpp
+++ b/layout/base/nsRefreshDriver.cpp
@@ -1314,78 +1314,54 @@ nsRefreshDriver::DispatchPendingEvents()
   // Swap out the current pending events
   nsTArray<PendingEvent> pendingEvents(Move(mPendingEvents));
   for (PendingEvent& event : pendingEvents) {
     bool dummy;
     event.mTarget->DispatchEvent(event.mEvent, &dummy);
   }
 }
 
-namespace {
-  enum class AnimationEventType {
-    CSSAnimations,
-    CSSTransitions
-  };
-
-  struct DispatchAnimationEventParams {
-    AnimationEventType mEventType;
-    nsRefreshDriver* mRefreshDriver;
-  };
-}
-
 static bool
 DispatchAnimationEventsOnSubDocuments(nsIDocument* aDocument,
-                                      void* aParams)
+                                      void* aRefreshDriver)
 {
-  MOZ_ASSERT(aParams, "Animation event parameters should be set");
-  auto params = static_cast<DispatchAnimationEventParams*>(aParams);
-
   nsIPresShell* shell = aDocument->GetShell();
   if (!shell) {
     return true;
   }
 
   nsPresContext* context = shell->GetPresContext();
-  if (!context || context->RefreshDriver() != params->mRefreshDriver) {
+  if (!context || context->RefreshDriver() != aRefreshDriver) {
     return true;
   }
 
   nsCOMPtr<nsIDocument> kungFuDeathGrip(aDocument);
 
-  if (params->mEventType == AnimationEventType::CSSAnimations) {
-    context->AnimationManager()->DispatchEvents();
-  } else {
-    context->TransitionManager()->DispatchEvents();
-  }
+  context->TransitionManager()->SortEvents();
+  context->AnimationManager()->SortEvents();
+
+  // Dispatch transition events first since transitions conceptually sit
+  // below animations in terms of compositing order.
+  context->TransitionManager()->DispatchEvents();
+  context->AnimationManager()->DispatchEvents();
+
   aDocument->EnumerateSubDocuments(DispatchAnimationEventsOnSubDocuments,
-                                   aParams);
+                                   aRefreshDriver);
 
   return true;
 }
 
 void
 nsRefreshDriver::DispatchAnimationEvents()
 {
   if (!mPresContext) {
     return;
   }
 
-  nsIDocument* doc = mPresContext->Document();
-
-  // Dispatch transition events first since transitions conceptually sit
-  // below animations in terms of compositing order.
-  DispatchAnimationEventParams params { AnimationEventType::CSSTransitions,
-                                        this };
-  DispatchAnimationEventsOnSubDocuments(doc, &params);
-  if (!mPresContext) {
-    return;
-  }
-
-  params.mEventType = AnimationEventType::CSSAnimations;
-  DispatchAnimationEventsOnSubDocuments(doc, &params);
+  DispatchAnimationEventsOnSubDocuments(mPresContext->Document(), this);
 }
 
 void
 nsRefreshDriver::RunFrameRequestCallbacks(TimeStamp aNowTime)
 {
   // Grab all of our frame request callbacks up front.
   nsTArray<DocumentFrameCallbacks>
     frameRequestCallbacks(mFrameRequestCallbackDocs.Length() +
--- a/layout/style/AnimationCommon.cpp
+++ b/layout/style/AnimationCommon.cpp
@@ -368,17 +368,17 @@ CommonAnimationManager::FlushAnimations(
   for (AnimationCollection* collection = mElementCollections.getFirst();
        collection; collection = collection->getNext()) {
     if (collection->mStyleRuleRefreshTime == now) {
       continue;
     }
 
     MOZ_ASSERT(collection->mElement->GetComposedDoc() ==
                  mPresContext->Document(),
-               "Should not have a transition/animations collection for an "
+               "Should not have a transition/animation collection for an "
                "element that is not part of the document tree");
 
     collection->RequestRestyle(AnimationCollection::RestyleType::Standard);
   }
 }
 
 nsIStyleRule*
 CommonAnimationManager::GetAnimationRule(mozilla::dom::Element* aElement,
--- a/layout/style/AnimationCommon.h
+++ b/layout/style/AnimationCommon.h
@@ -1,22 +1,24 @@
 /* vim: set shiftwidth=2 tabstop=8 autoindent cindent expandtab: */
 /* 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_css_AnimationCommon_h
 #define mozilla_css_AnimationCommon_h
 
+#include <algorithm> // For <std::stable_sort>
 #include "nsIStyleRuleProcessor.h"
 #include "nsIStyleRule.h"
 #include "nsRefreshDriver.h"
 #include "nsChangeHint.h"
 #include "nsCSSProperty.h"
 #include "nsDisplayList.h" // For nsDisplayItem::Type
+#include "mozilla/AnimationComparator.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/StyleAnimationValue.h"
 #include "mozilla/dom/Animation.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/Nullable.h"
 #include "nsStyleStruct.h"
@@ -497,60 +499,109 @@ private:
   dom::Element* MOZ_NON_OWNING_REF mElement;
   nsCSSPseudoElements::Type        mPseudoType;
 };
 
 template <class EventInfo>
 class DelayedEventDispatcher
 {
 public:
+  DelayedEventDispatcher() : mIsSorted(true) { }
+
   void QueueEvent(EventInfo&& aEventInfo)
   {
-    mPendingEvents.AppendElement(mozilla::Forward<EventInfo>(aEventInfo));
+    mPendingEvents.AppendElement(Forward<EventInfo>(aEventInfo));
+    mIsSorted = false;
+  }
+
+  // This is exposed as a separate method so that when we are dispatching
+  // *both* transition events and animation events we can sort both lists
+  // once using the current state of the document before beginning any
+  // dispatch.
+  void SortEvents()
+  {
+    if (mIsSorted) {
+      return;
+    }
+
+    // FIXME: Replace with mPendingEvents.StableSort when bug 1147091 is
+    // fixed.
+    std::stable_sort(mPendingEvents.begin(), mPendingEvents.end(),
+                     EventInfoLessThan());
+    mIsSorted = true;
   }
 
   // Takes a reference to the owning manager's pres context so it can
   // detect if the pres context is destroyed while dispatching one of
   // the events.
+  //
+  // This will call SortEvents automatically if it has not already been
+  // called.
   void DispatchEvents(nsPresContext* const & aPresContext)
   {
     if (!aPresContext || mPendingEvents.IsEmpty()) {
       return;
     }
 
+    SortEvents();
+
     EventArray events;
     mPendingEvents.SwapElements(events);
-    // FIXME: Sort events here in timeline order, then document order
+    // mIsSorted will be set to true by SortEvents above, and we leave it
+    // that way since mPendingEvents is now empty
     for (EventInfo& info : events) {
       EventDispatcher::Dispatch(info.mElement, aPresContext, &info.mEvent);
 
       if (!aPresContext) {
         break;
       }
     }
   }
 
-  void ClearEventQueue() { mPendingEvents.Clear(); }
+  void ClearEventQueue()
+  {
+    mPendingEvents.Clear();
+    mIsSorted = true;
+  }
   bool HasQueuedEvents() const { return !mPendingEvents.IsEmpty(); }
 
   // Methods for supporting cycle-collection
   void Traverse(nsCycleCollectionTraversalCallback* aCallback,
                 const char* aName)
   {
     for (EventInfo& info : mPendingEvents) {
       ImplCycleCollectionTraverse(*aCallback, info.mElement, aName);
       ImplCycleCollectionTraverse(*aCallback, info.mAnimation, aName);
     }
   }
-  void Unlink() { mPendingEvents.Clear(); }
+  void Unlink() { ClearEventQueue(); }
 
 protected:
-  typedef nsTArray<EventInfo> EventArray;
+  class EventInfoLessThan
+  {
+  public:
+    bool operator()(const EventInfo& a, const EventInfo& b) const
+    {
+      if (a.mTimeStamp != b.mTimeStamp) {
+        // Null timestamps sort first
+        if (a.mTimeStamp.IsNull() || b.mTimeStamp.IsNull()) {
+          return a.mTimeStamp.IsNull();
+        } else {
+          return a.mTimeStamp < b.mTimeStamp;
+        }
+      }
 
+      AnimationPtrComparator<nsRefPtr<dom::Animation>> comparator;
+      return comparator.LessThan(a.mAnimation, b.mAnimation);
+    }
+  };
+
+  typedef nsTArray<EventInfo> EventArray;
   EventArray mPendingEvents;
+  bool mIsSorted;
 };
 
 template <class EventInfo>
 inline void
 ImplCycleCollectionUnlink(DelayedEventDispatcher<EventInfo>& aField)
 {
   aField.Unlink();
 }
--- a/layout/style/nsAnimationManager.h
+++ b/layout/style/nsAnimationManager.h
@@ -325,16 +325,17 @@ public:
   /**
    * Dispatch any pending events.  We accumulate animationend and
    * animationiteration events only during refresh driver notifications
    * (and dispatch them at the end of such notifications), but we
    * accumulate animationstart events at other points when style
    * contexts are created.
    */
   void DispatchEvents()  { mEventDispatcher.DispatchEvents(mPresContext); }
+  void SortEvents()      { mEventDispatcher.SortEvents(); }
   void ClearEventQueue() { mEventDispatcher.ClearEventQueue(); }
 
   // Stop animations on the element. This method takes the real element
   // rather than the element for the generated content for animations on
   // ::before and ::after.
   void StopAnimationsForElement(mozilla::dom::Element* aElement,
                                 nsCSSPseudoElements::Type aPseudoType);
 
--- a/layout/style/nsTransitionManager.h
+++ b/layout/style/nsTransitionManager.h
@@ -299,16 +299,17 @@ public:
 
   void QueueEvent(mozilla::TransitionEventInfo&& aEventInfo)
   {
     mEventDispatcher.QueueEvent(
       mozilla::Forward<mozilla::TransitionEventInfo>(aEventInfo));
   }
 
   void DispatchEvents()  { mEventDispatcher.DispatchEvents(mPresContext); }
+  void SortEvents()      { mEventDispatcher.SortEvents(); }
   void ClearEventQueue() { mEventDispatcher.ClearEventQueue(); }
 
 protected:
   virtual ~nsTransitionManager() {}
 
   virtual nsIAtom* GetAnimationsAtom() override {
     return nsGkAtoms::transitionsProperty;
   }