Bug 1372118 - Part5. Support OMTA. draft
authorEthan Lin <ethlin@mozilla.com>
Tue, 27 Jun 2017 09:05:01 -0700
changeset 600611 7c18e960be81441388af734c2e112afe34672c6f
parent 600610 a9416c994f0897bd1fff29e36738108e377c9802
child 635033 46075420688d8bb567dfb6e71d2174844c1138ba
push id65803
push userbmo:ethlin@mozilla.com
push dateTue, 27 Jun 2017 16:20:34 +0000
bugs1372118
milestone56.0a1
Bug 1372118 - Part5. Support OMTA. MozReview-Commit-ID: BbYDzZUUluQ
gfx/layers/wr/WebRenderLayerManager.cpp
gfx/layers/wr/WebRenderLayerManager.h
layout/painting/nsDisplayList.cpp
layout/painting/nsDisplayList.h
--- a/gfx/layers/wr/WebRenderLayerManager.cpp
+++ b/gfx/layers/wr/WebRenderLayerManager.cpp
@@ -15,23 +15,30 @@
 #include "WebRenderCanvasLayer.h"
 #include "WebRenderColorLayer.h"
 #include "WebRenderContainerLayer.h"
 #include "WebRenderImageLayer.h"
 #include "WebRenderPaintedLayer.h"
 #include "WebRenderPaintedLayerBlob.h"
 #include "WebRenderTextLayer.h"
 #include "WebRenderDisplayItemLayer.h"
+#include "nsDisplayList.h"
 
 namespace mozilla {
 
 using namespace gfx;
 
 namespace layers {
 
+void WMCanvasData::EmptyTransaction()
+{
+  MOZ_ASSERT(mLayer);
+  static_cast<WebRenderCanvasLayer*>(WebRenderLayer::ToWebRenderLayer(mLayer))->UpdateCompositableClient();
+}
+
 WebRenderLayerManager::WebRenderLayerManager(nsIWidget* aWidget)
   : mWidget(aWidget)
   , mLatestTransactionId(0)
   , mNeedsComposite(false)
   , mIsFirstPaint(false)
   , mTarget(nullptr)
   , mPaintSequenceNumber(0)
 {
@@ -140,16 +147,22 @@ WebRenderLayerManager::BeginTransaction(
 
 bool
 WebRenderLayerManager::EndEmptyTransaction(EndTransactionFlags aFlags)
 {
   if (!mRoot) {
     return false;
   }
 
+  if (gfxPrefs::WebRenderLayerFree() &&
+      XRE_IsContentProcess()) {
+    WebRenderLayerManager::EndTransactionWithoutLayer(nullptr, nullptr);
+    return true;
+  }
+
   // We might used painted layer images so don't delete them yet.
   return EndTransactionInternal(nullptr, nullptr, aFlags);
 }
 
 /*static*/ int32_t
 PopulateScrollData(WebRenderScrollData& aTarget, Layer* aLayer)
 {
   MOZ_ASSERT(aLayer);
@@ -246,18 +259,38 @@ WebRenderLayerManager::EndTransactionWit
   wr::DisplayListBuilder builder(WrBridge()->GetPipeline(), contentSize);
 
   if (aDisplayList && aDisplayListBuilder) {
     StackingContextHelper sc;
     mParentCommands.Clear();
     CreateWebRenderCommandsFromDisplayList(this, aDisplayList, aDisplayListBuilder, sc, builder);
     builder.Finalize(contentSize, mBuiltDisplayList);
 
+    for (auto iter = mLastItemData.Iter(); !iter.Done(); iter.Next()) {
+      auto data = iter.UserData();
+      if (auto animData = data->AsAnimationData()) {
+        // discard animation
+        AddCompositorAnimationsIdForDiscard(animData->GetCompositorAnimationsId());
+      }
+      if (auto imageData = data->AsImageData()) {
+        // discard image
+        //AddImageKeyForDiscard(imageData->mKey.value());
+      }
+    }
+
+    DiscardCompositorAnimations();
+
     mLastItemData.SwapElements(mItemData);
   } else {
+    for (auto iter = mLastItemData.Iter(); !iter.Done(); iter.Next()) {
+      auto data = iter.UserData();
+      if (auto canvasData = data->AsCanvasData()) {
+        canvasData->EmptyTransaction();
+      }
+    }
   }
 
   builder.PushBuiltDisplayList(mBuiltDisplayList);
 
   WrBridge()->AddWebRenderParentCommands(mParentCommands);
 
 
   WrBridge()->ClearReadLocks();
--- a/gfx/layers/wr/WebRenderLayerManager.h
+++ b/gfx/layers/wr/WebRenderLayerManager.h
@@ -58,16 +58,17 @@ public:
     return HashGeneric(aKey->first(), aKey->second());
   }
 
   enum { ALLOW_MEMMOVE = true };
 
   FrameDisplayItemKey mKey;
 };
 
+class WMAnimationData;
 class WMImageData;
 class WMCanvasData;
 
 class WMData
 {
 public:
   NS_INLINE_DECL_REFCOUNTING(WMData)
 
@@ -82,16 +83,116 @@ public:
   };
 
   virtual TYPE GetType() = 0;
 
 protected:
   virtual ~WMData() {}
 };
 
+class WMAnimationData : public WMData
+{
+public:
+  virtual WMAnimationData* AsAnimationData() override { return this; }
+  virtual TYPE GetType() override { return TYPE::ANIMATION; }
+  static TYPE Type() { return TYPE::ANIMATION; }
+
+  typedef InfallibleTArray<Animation> AnimationArray;
+
+  WMAnimationData()
+    : mAnimationGeneration(0)
+    , mCompositorAnimationsId(AnimationHelper::GetNextCompositorAnimationsId())
+  {
+  }
+
+  Animation* AddAnimation()
+  {
+    // Here generates a new id when the first animation is added and
+    // this id is used to represent the animations in this layer.
+    //EnsureAnimationsId();
+    if (!mCompositorAnimationsId) {
+      mCompositorAnimationsId = AnimationHelper::GetNextCompositorAnimationsId();
+    }
+
+    MOZ_ASSERT(!mPendingAnimations, "should have called ClearAnimations first");
+
+    Animation* anim = mAnimations.AppendElement();
+
+    //Mutated();
+    return anim;
+  }
+
+  Animation* AddAnimationForNextTransaction()
+  {
+    MOZ_ASSERT(mPendingAnimations,
+               "should have called ClearAnimationsForNextTransaction first");
+
+    Animation* anim = mPendingAnimations->AppendElement();
+
+    return anim;
+  }
+
+  bool HasTransformAnimation() const
+  {
+    for (uint32_t i = 0; i < mAnimations.Length(); i++) {
+      if (mAnimations[i].property() == eCSSProperty_transform) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  AnimationArray& GetAnimations() { return mAnimations; }
+
+  uint64_t GetCompositorAnimationsId() const { return mCompositorAnimationsId; }
+
+  void UpdateTransformDataForAnimation()
+  {
+    for (Animation& animation : mAnimations) {
+      if (animation.property() == eCSSProperty_transform) {
+        // TODO
+        //TransformData& transformData = animation.data().get_TransformData();
+        //transformData.inheritedXScale() = GetInheritedXScale();
+        //transformData.inheritedYScale() = GetInheritedYScale();
+        //transformData.hasPerspectiveParent() =
+          //GetParent() && GetParent()->GetTransformIsPerspective();
+      }
+    }
+  }
+
+  void ResolveStartTime(TimeStamp aAnimationReadyTime)
+  {
+    for (Animation& anim : mAnimations) {
+      // If the animation is play-pending, resolve the start time.
+      // This mirrors the calculation in Animation::StartTimeFromReadyTime.
+      if (anim.startTime().type() == MaybeTimeDuration::Tnull_t &&
+          !anim.originTime().IsNull() &&
+          !anim.isNotPlaying()) {
+        TimeDuration readyTime = aAnimationReadyTime - anim.originTime();
+        anim.startTime() =
+          anim.playbackRate() == 0
+          ? readyTime
+          : readyTime - anim.holdTime().MultDouble(1.0 /
+                                                   anim.playbackRate());
+      }
+    }
+  }
+
+  void SetAnimationGeneration(uint64_t aAnimationGeneration)
+  {
+    mAnimationGeneration = aAnimationGeneration;
+  }
+
+protected:
+  uint64_t mAnimationGeneration;
+  uint64_t mCompositorAnimationsId;
+  AnimationArray mAnimations;
+  nsAutoPtr<AnimationArray> mPendingAnimations;
+};
+
 class WMImageData : public WMData
 {
 public:
   virtual WMImageData* AsImageData() override { return this; }
   virtual TYPE GetType() override { return TYPE::IMAGE; }
   static TYPE Type() { return TYPE::IMAGE; }
 
   wr::MaybeExternalImageId mExternalImageId;
@@ -251,16 +352,47 @@ public:
                                   const std::string& aKey,
                                   const std::string& aValue) {
     mApzTestData.LogTestDataForPaint(mPaintSequenceNumber, aScrollId, aKey, aValue);
   }
   // See equivalent function in ClientLayerManager
   const APZTestData& GetAPZTestData() const
   { return mApzTestData; }
 
+  void SendAnimationData(WMAnimationData* aAnimationData,
+                         OptionalOpacity aOpacity,
+                         OptionalTransform aTransform);
+  template<class T> already_AddRefed<T>
+  CreateOrRecycleWMData(nsDisplayItem* aItem)
+  {
+    MOZ_ASSERT(aItem);
+    FrameDisplayItemKey key = MakePair(aItem->Frame(), aItem->GetPerFrameKey());
+
+    RefPtr<WMData> data;
+
+    if (mItemData.Contains(key)) {
+      data = mItemData.Get(key);
+    } else if (mLastItemData.Contains(key)) {
+      data = mLastItemData.Get(key);
+      mItemData.Put(key, data);
+      mLastItemData.Remove(key);
+    }
+
+    if (!data || (data->GetType() != T::Type())) {
+      data = new T();
+      mItemData.Put(key, data);
+    }
+
+    MOZ_ASSERT(data);
+    MOZ_ASSERT(data->GetType() == T::Type());
+
+    RefPtr<T> res = static_cast<T*>(data.get());
+    return res.forget();
+  }
+
 private:
   /**
    * Take a snapshot of the parent context, and copy
    * it into mTarget.
    */
   void MakeSnapshotIfRequired(LayoutDeviceIntSize aSize);
 
   void ClearLayer(Layer* aLayer);
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -849,19 +849,314 @@ nsDisplayListBuilder::AddAnimationsAndTr
     }
 
     data = TransformData(origin, offsetToTransformOrigin,
                          bounds, devPixelsToAppUnits,
                          scaleX, scaleY, hasPerspectiveParent);
   } else if (aProperty == eCSSProperty_opacity) {
     data = null_t();
   }
+  AddAnimationsForProperty(aFrame, aProperty, compositorAnimations,
+                           aLayer, data, pending);
+}
+
+static void
+AddAnimationForProperty(nsIFrame* aFrame, const AnimationProperty& aProperty,
+                        dom::Animation* aAnimation, WMAnimationData* aAnimationData,
+                        AnimationData& aData, bool aPending)
+{
+  //MOZ_ASSERT(aLayer->AsContainerLayer(), "Should only animate ContainerLayer");
+  MOZ_ASSERT(aAnimation->GetEffect(),
+             "Should not be adding an animation without an effect");
+  MOZ_ASSERT(!aAnimation->GetCurrentOrPendingStartTime().IsNull() ||
+             !aAnimation->IsPlaying() ||
+             (aAnimation->GetTimeline() &&
+              aAnimation->GetTimeline()->TracksWallclockTime()),
+             "If the animation has an unresolved start time it should either"
+             " be static (so we don't need a start time) or else have a"
+             " timeline capable of converting TimeStamps (so we can calculate"
+             " one later");
+
+  layers::Animation* animation =
+    aPending ?
+    aAnimationData->AddAnimationForNextTransaction() :
+    aAnimationData->AddAnimation();
+  const TimingParams& timing = aAnimation->GetEffect()->SpecifiedTiming();
+
+  // If we are starting a new transition that replaces an existing transition
+  // running on the compositor, it is possible that the animation on the
+  // compositor will have advanced ahead of the main thread. If we use as
+  // the starting point of the new transition, the current value of the
+  // replaced transition as calculated on the main thread using the refresh
+  // driver time, the new transition will jump when it starts. Instead, we
+  // re-calculate the starting point of the new transition by applying the
+  // current TimeStamp to the parameters of the replaced transition.
+  //
+  // We need to do this here, rather than when we generate the new transition,
+  // since after generating the new transition other requestAnimationFrame
+  // callbacks may run that introduce further lag between the main thread and
+  // the compositor.
+  if (aAnimation->AsCSSTransition() &&
+      aAnimation->GetEffect() &&
+      aAnimation->GetEffect()->AsTransition()) {
+    // We update startValue from the replaced transition only if the effect is
+    // an ElementPropertyTransition.
+    aAnimation->GetEffect()->AsTransition()->
+      UpdateStartValueFromReplacedTransition();
+  }
+
+  animation->originTime() = !aAnimation->GetTimeline()
+                            ? TimeStamp()
+                            : aAnimation->GetTimeline()->
+                                ToTimeStamp(TimeDuration());
+
+  Nullable<TimeDuration> startTime = aAnimation->GetCurrentOrPendingStartTime();
+  if (startTime.IsNull()) {
+    animation->startTime() = null_t();
+  } else {
+    animation->startTime() = startTime.Value();
+  }
+
+  animation->holdTime() = aAnimation->GetCurrentTime().Value();
+
+  const ComputedTiming computedTiming =
+    aAnimation->GetEffect()->GetComputedTiming();
+  animation->delay() = timing.Delay();
+  animation->endDelay() = timing.EndDelay();
+  animation->duration() = computedTiming.mDuration;
+  animation->iterations() = computedTiming.mIterations;
+  animation->iterationStart() = computedTiming.mIterationStart;
+  animation->direction() = static_cast<uint8_t>(timing.Direction());
+  animation->fillMode() = static_cast<uint8_t>(computedTiming.mFill);
+  animation->property() = aProperty.mProperty;
+  animation->playbackRate() = aAnimation->PlaybackRate();
+  animation->data() = aData;
+  animation->easingFunction() = ToTimingFunction(timing.TimingFunction());
+  animation->iterationComposite() =
+    static_cast<uint8_t>(aAnimation->GetEffect()->
+                         AsKeyframeEffect()->IterationComposite());
+  animation->isNotPlaying() = !aAnimation->IsPlaying();
+
+  TransformReferenceBox refBox(aFrame);
+
+  // If the animation is additive or accumulates, we need to pass its base value
+  // to the compositor.
+
+  AnimationValue baseStyle =
+    aAnimation->GetEffect()->AsKeyframeEffect()->BaseStyle(aProperty.mProperty);
+  if (!baseStyle.IsNull()) {
+    SetAnimatable(aProperty.mProperty,
+                  baseStyle,
+                  aFrame, refBox,
+                  animation->baseStyle());
+  } else {
+    animation->baseStyle() = null_t();
+  }
+
+  for (uint32_t segIdx = 0; segIdx < aProperty.mSegments.Length(); segIdx++) {
+    const AnimationPropertySegment& segment = aProperty.mSegments[segIdx];
+
+    AnimationSegment* animSegment = animation->segments().AppendElement();
+    SetAnimatable(aProperty.mProperty,
+                  segment.mFromValue,
+                  aFrame, refBox,
+                  animSegment->startState());
+    SetAnimatable(aProperty.mProperty,
+                  segment.mToValue,
+                  aFrame, refBox,
+                  animSegment->endState());
+
+    animSegment->startPortion() = segment.mFromKey;
+    animSegment->endPortion() = segment.mToKey;
+    animSegment->startComposite() =
+      static_cast<uint8_t>(segment.mFromComposite);
+    animSegment->endComposite() =
+      static_cast<uint8_t>(segment.mToComposite);
+    animSegment->sampleFn() = ToTimingFunction(segment.mTimingFunction);
+  }
+}
+
+static void
+AddAnimationsForProperty(nsIFrame* aFrame, nsCSSPropertyID aProperty,
+                         nsTArray<RefPtr<dom::Animation>>& aAnimations,
+                         WMAnimationData* aAnimationData,
+                         AnimationData& aData,
+                         bool aPending)
+{
+  MOZ_ASSERT(nsCSSProps::PropHasFlags(aProperty,
+                                      CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR),
+             "inconsistent property flags");
+
+  EffectSet* effects = EffectSet::GetEffectSet(aFrame);
+  MOZ_ASSERT(effects);
+
+  bool sentAnimations = false;
+  // Add from first to last (since last overrides)
+  for (size_t animIdx = 0; animIdx < aAnimations.Length(); animIdx++) {
+    dom::Animation* anim = aAnimations[animIdx];
+    if (!anim->IsRelevant()) {
+      continue;
+    }
+
+    dom::KeyframeEffectReadOnly* keyframeEffect =
+      anim->GetEffect() ? anim->GetEffect()->AsKeyframeEffect() : nullptr;
+    MOZ_ASSERT(keyframeEffect,
+               "A playing animation should have a keyframe effect");
+    const AnimationProperty* property =
+      keyframeEffect->GetEffectiveAnimationOfProperty(aProperty);
+    if (!property) {
+      continue;
+    }
+
+    // Note that if the property is overridden by !important rules,
+    // GetEffectiveAnimationOfProperty returns null instead.
+    // This is what we want, since if we have animations overridden by
+    // !important rules, we don't want to send them to the compositor.
+    MOZ_ASSERT(anim->CascadeLevel() !=
+                 EffectCompositor::CascadeLevel::Animations ||
+               !effects->PropertiesWithImportantRules()
+                  .HasProperty(aProperty),
+               "GetEffectiveAnimationOfProperty already tested the property "
+               "is not overridden by !important rules");
+
+    // Don't add animations that are pending if their timeline does not
+    // track wallclock time. This is because any pending animations on layers
+    // will have their start time updated with the current wallclock time.
+    // If we can't convert that wallclock time back to an equivalent timeline
+    // time, we won't be able to update the content animation and it will end
+    // up being out of sync with the layer animation.
+    //
+    // Currently this only happens when the timeline is driven by a refresh
+    // driver under test control. In this case, the next time the refresh
+    // driver is advanced it will trigger any pending animations.
+    if (anim->PlayState() == AnimationPlayState::Pending &&
+        (anim->GetTimeline() &&
+         !anim->GetTimeline()->TracksWallclockTime())) {
+      continue;
+    }
+
+    AddAnimationForProperty(aFrame, *property, anim, aAnimationData, aData, aPending);
+    keyframeEffect->SetIsRunningOnCompositor(aProperty, true);
+    sentAnimations = true;
+  }
+
+  if (sentAnimations && aProperty == eCSSProperty_transform) {
+    TimeStamp now = aFrame->PresContext()->RefreshDriver()->MostRecentRefresh();
+    effects->UpdateLastTransformSyncTime(now);
+  }
+}
+
+/* static */ already_AddRefed<WMAnimationData>
+nsDisplayListBuilder::AddAnimationsAndTransitionsToWRLayerManager(WebRenderLayerManager* aManager,
+                                                                  nsDisplayListBuilder* aBuilder,
+                                                                  nsDisplayItem* aItem,
+                                                                  nsIFrame* aFrame,
+                                                                  nsCSSPropertyID aProperty)
+{
+  MOZ_ASSERT(nsCSSProps::PropHasFlags(aProperty,
+                                      CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR),
+             "inconsistent property flags");
+
+  // This function can be called in two ways:  from
+  // nsDisplay*::BuildLayer while constructing a layer (with all
+  // pointers non-null), or from RestyleManager's handling of
+  // UpdateOpacityLayer/UpdateTransformLayer hints.
+  MOZ_ASSERT(!aBuilder == !aItem,
+             "should only be called in two configurations, with both "
+             "aBuilder and aItem, or with neither");
+  MOZ_ASSERT(!aItem || aFrame == aItem->Frame(), "frame mismatch");
+
+  // Only send animations to a layer that is actually using
+  // off-main-thread compositing.
+  //MOZ_ASSERT(backend == layers::LayersBackend::LAYERS_WR);
+
+  bool pending = !aBuilder;
+#if 0
+  if (pending) {
+    aLayer->ClearAnimationsForNextTransaction();
+  } else {
+    aLayer->ClearAnimations();
+  }
+#endif
+  // Update the animation generation on the layer. We need to do this before
+  // any early returns since even if we don't add any animations to the
+  // layer, we still need to mark it as up-to-date with regards to animations.
+  // Otherwise, in RestyleManager we'll notice the discrepancy between the
+  // animation generation numbers and update the layer indefinitely.
+  uint64_t animationGeneration =
+    RestyleManager::GetAnimationGenerationForFrame(aFrame);
+  //aLayer->SetAnimationGeneration(animationGeneration);
+  RefPtr<WMAnimationData> animationData = aManager->CreateOrRecycleWMData<WMAnimationData>(aItem);
+  animationData->SetAnimationGeneration(animationGeneration);
+
+  EffectCompositor::ClearIsRunningOnCompositor(aFrame, aProperty);
+  nsTArray<RefPtr<dom::Animation>> compositorAnimations =
+    EffectCompositor::GetAnimationsForCompositor(aFrame, aProperty);
+  if (compositorAnimations.IsEmpty()) {
+    return nullptr;
+  }
+
+  // If the frame is not prerendered, bail out.
+  // Do this check only during layer construction; during updating the
+  // caller is required to check it appropriately.
+  if (aItem && !aItem->CanUseAsyncAnimations(aBuilder)) {
+    // EffectCompositor needs to know that we refused to run this animation
+    // asynchronously so that it will not throttle the main thread
+    // animation.
+    aFrame->SetProperty(nsIFrame::RefusedAsyncAnimationProperty(), true);
+
+    // We need to schedule another refresh driver run so that EffectCompositor
+    // gets a chance to unthrottle the animation.
+    aFrame->SchedulePaint();
+    return nullptr;
+  }
+
+  AnimationData data;
+  if (aProperty == eCSSProperty_transform) {
+    // XXX Performance here isn't ideal for SVG. We'd prefer to avoid resolving
+    // the dimensions of refBox. That said, we only get here if there are CSS
+    // animations or transitions on this element, and that is likely to be a
+    // lot rarer than transforms on SVG (the frequency of which drives the need
+    // for TransformReferenceBox).
+    TransformReferenceBox refBox(aFrame);
+    nsRect bounds(0, 0, refBox.Width(), refBox.Height());
+    // all data passed directly to the compositor should be in dev pixels
+    int32_t devPixelsToAppUnits = aFrame->PresContext()->AppUnitsPerDevPixel();
+    float scale = devPixelsToAppUnits;
+    Point3D offsetToTransformOrigin =
+      nsDisplayTransform::GetDeltaToTransformOrigin(aFrame, scale, &bounds);
+    nsPoint origin;
+    float scaleX = 1.0f;
+    float scaleY = 1.0f;
+    bool hasPerspectiveParent = false;
+    if (aItem) {
+      // This branch is for display items to leverage the cache of
+      // nsDisplayListBuilder.
+      origin = aItem->ToReferenceFrame();
+    } else {
+      // This branch is running for restyling.
+      // Animations are animated at the coordination of the reference
+      // frame outside, not the given frame itself.  The given frame
+      // is also reference frame too, so the parent's reference frame
+      // are used.
+      nsIFrame* referenceFrame =
+        nsLayoutUtils::GetReferenceFrame(nsLayoutUtils::GetCrossDocParentFrame(aFrame));
+      origin = aFrame->GetOffsetToCrossDoc(referenceFrame);
+    }
+
+    data = TransformData(origin, offsetToTransformOrigin,
+                         bounds, devPixelsToAppUnits,
+                         scaleX, scaleY, hasPerspectiveParent);
+  } else if (aProperty == eCSSProperty_opacity) {
+    data = null_t();
+  }
 
   AddAnimationsForProperty(aFrame, aProperty, compositorAnimations,
-                           aLayer, data, pending);
+                           animationData, data, pending);
+  return animationData.forget();
 }
 
 void
 nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter::InsertScrollFrame(nsIScrollableFrame* aScrollableFrame)
 {
   MOZ_ASSERT(!mUsed);
   size_t descendantsEndIndex = mBuilder->mActiveScrolledRoots.Length();
   const ActiveScrolledRoot* parentASR = mBuilder->mCurrentActiveScrolledRoot;
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -59,16 +59,17 @@ class FrameLayerBuilder;
 namespace layers {
 class Layer;
 class ImageLayer;
 class ImageContainer;
 class StackingContextHelper;
 class WebRenderCommand;
 class WebRenderParentCommand;
 class WebRenderDisplayItemLayer;
+class WMAnimationData;
 } // namespace layers
 namespace wr {
 class DisplayListBuilder;
 } // namespace wr
 } // namespace mozilla
 
 // A set of blend modes, that never includes OP_OVER (since it's
 // considered the default, rather than a specific blend mode).
@@ -790,16 +791,23 @@ public:
    * the layer as pending animations.
    */
   static void AddAnimationsAndTransitionsToLayer(Layer* aLayer,
                                                  nsDisplayListBuilder* aBuilder,
                                                  nsDisplayItem* aItem,
                                                  nsIFrame* aFrame,
                                                  nsCSSPropertyID aProperty);
 
+  static already_AddRefed<mozilla::layers::WMAnimationData>
+    AddAnimationsAndTransitionsToWRLayerManager(mozilla::layers::WebRenderLayerManager* aManager,
+                                                nsDisplayListBuilder* aBuilder,
+                                                nsDisplayItem* aItem,
+                                                nsIFrame* aFrame,
+                                                nsCSSPropertyID aProperty);
+
   /**
    * A helper class to temporarily set the value of
    * mIsAtRootOfPseudoStackingContext, and temporarily
    * set mCurrentFrame and related state. Also temporarily sets mDirtyRect.
    * aDirtyRect is relative to aForChild.
    */
   class AutoBuildingDisplayList;
   friend class AutoBuildingDisplayList;