Bug 1504065 - Run background-color animations on the compositor. r=birtles
☠☠ backed out by 20898bcaaa00 ☠ ☠
authorHiroyuki Ikezoe <hikezoe@mozilla.com>
Tue, 27 Nov 2018 09:26:51 +0000
changeset 504643 359e81b35cfb72928457964fcf1c826db1404818
parent 504642 f2ce8f83a2242f896a25043077539882993335ec
child 504644 1291883702312b5ca9c44983121ac39fd2b8bdf6
push id10290
push userffxbld-merge
push dateMon, 03 Dec 2018 16:23:23 +0000
treeherdermozilla-beta@700bed2445e6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbirtles
bugs1504065
milestone65.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 1504065 - Run background-color animations on the compositor. r=birtles Changes for nsIDOMWindowUtils.getOMTAValue is in the next commit with come test cases. Differential Revision: https://phabricator.services.mozilla.com/D13001
dom/animation/KeyframeEffect.cpp
dom/animation/test/chrome/test_running_on_compositor.html
dom/animation/test/mochitest.ini
gfx/layers/AnimationHelper.cpp
gfx/layers/composite/AsyncCompositionManager.cpp
gfx/layers/ipc/LayersMessages.ipdlh
layout/painting/nsDisplayList.cpp
layout/painting/nsDisplayList.h
layout/style/LayerAnimationInfo.cpp
layout/style/LayerAnimationInfo.h
layout/style/ServoBindings.h
layout/style/StyleAnimationValue.cpp
layout/style/StyleAnimationValue.h
modules/libpref/init/StaticPrefList.h
servo/components/style/properties/longhands/background.mako.rs
servo/ports/geckolib/glue.rs
--- a/dom/animation/KeyframeEffect.cpp
+++ b/dom/animation/KeyframeEffect.cpp
@@ -1889,15 +1889,32 @@ KeyframeEffect::IsMatchForCompositor(
 
   // If we know that the animation is not visible, we don't need to send the
   // animation to the compositor.
   if (!aFrame->IsVisibleOrMayHaveVisibleDescendants() ||
       aFrame->IsScrolledOutOfView()) {
     return KeyframeEffect::MatchForCompositor::NoAndBlockThisProperty;
   }
 
+  if (aProperty == eCSSProperty_background_color) {
+    if (!StaticPrefs::gfx_omta_background_color()) {
+      return KeyframeEffect::MatchForCompositor::No;
+    }
+
+    if (nsIContent* content = aFrame->GetContent()) {
+      RefPtr<layers::LayerManager> layerManager =
+        nsContentUtils::LayerManagerForContent(content);
+      if (layerManager &&
+          layerManager->GetBackendType() == layers::LayersBackend::LAYERS_WR) {
+        // Bug 1510030: We don't yet support background-color animations on the
+        // compositor for WebRender.
+        return KeyframeEffect::MatchForCompositor::No;
+      }
+    }
+  }
+
   return mAnimation->IsPlaying()
          ? KeyframeEffect::MatchForCompositor::Yes
          : KeyframeEffect::MatchForCompositor::IfNeeded;
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/animation/test/chrome/test_running_on_compositor.html
+++ b/dom/animation/test/chrome/test_running_on_compositor.html
@@ -45,17 +45,19 @@ div {
   target="_blank">Mozilla Bug 1045994</a>
 <div id="log"></div>
 <script>
 'use strict';
 
 /** Test for bug 1045994 - Add a chrome-only property to inspect if an
     animation is running on the compositor or not **/
 
-var omtaEnabled = isOMTAEnabled();
+const omtaEnabled = isOMTAEnabled();
+const isWebRender =
+  SpecialPowers.DOMWindowUtils.layerManagerType == 'WebRender';
 
 function assert_animation_is_running_on_compositor(animation, desc) {
   assert_equals(animation.isRunningOnCompositor, omtaEnabled,
                 desc + ' at ' + animation.currentTime + 'ms');
 }
 
 function assert_animation_is_not_running_on_compositor(animation, desc) {
   assert_equals(animation.isRunningOnCompositor, false,
@@ -983,10 +985,46 @@ promise_test(async t => {
   }
 
   await waitForPaints();
 
   assert_animation_is_running_on_compositor(animation,
     'Transform animation on table element should be running on the compositor');
 }, 'Transform animation on table element runs on the compositor');
 
+promise_test(async t => {
+  const div = addDiv(t);
+  const animation = div.animate({ backgroundColor: ['blue', 'green'] },
+                                100 * MS_PER_SEC);
+
+  await waitForAnimationReadyToRestyle(animation);
+  await waitForPaints();
+
+  if (!isWebRender) {
+    assert_animation_is_running_on_compositor(animation,
+      'background-color animation should be running on the compositor');
+  } else {
+    assert_animation_is_not_running_on_compositor(animation,
+      'background-color animation is not yet able to run on the compositor ' +
+      'on WebRender');
+  }
+}, 'backgound-color animation runs on the compositor');
+
+promise_test(async t => {
+  await SpecialPowers.pushPrefEnv({
+    set: [["gfx.omta.background-color", false]]
+  });
+
+  const div = addDiv(t);
+  const animation = div.animate({ backgroundColor: ['blue', 'green'] },
+                                100 * MS_PER_SEC);
+
+  await waitForAnimationReadyToRestyle(animation);
+  await waitForPaints();
+
+  assert_animation_is_not_running_on_compositor(animation,
+    'background-color animation should NOT be running on the compositor ' +
+    'if the pref is disabled');
+}, 'backgound-color animation does not run on the compositor if the pref ' +
+   'is disabled');
+
 </script>
 </body>
--- a/dom/animation/test/mochitest.ini
+++ b/dom/animation/test/mochitest.ini
@@ -1,15 +1,16 @@
 [DEFAULT]
 prefs =
   dom.animations-api.compositing.enabled=true
   dom.animations-api.core.enabled=true
   dom.animations-api.getAnimations.enabled=true
   dom.animations-api.implicit-keyframes.enabled=true
   dom.animations-api.timelines.enabled=true
+  gfx.omta.background-color=true
   layout.css.motion-path.enabled=true
   layout.css.individual-transform.enabled=true
 # Support files for chrome tests that we want to load over HTTP need
 # to go in here, not chrome.ini.
 support-files =
   chrome/file_animate_xrays.html
   mozilla/xhr_doc.html
   mozilla/file_deferred_start.html
--- a/gfx/layers/AnimationHelper.cpp
+++ b/gfx/layers/AnimationHelper.cpp
@@ -514,17 +514,17 @@ CreateCSSValueList(const InfallibleTArra
   if (aFunctions.Length() == 0) {
     result = new nsCSSValueList();
     result->mValue.SetNoneValue();
   }
   return new nsCSSValueSharedList(result.forget());
 }
 
 static already_AddRefed<RawServoAnimationValue>
-ToAnimationValue(const Animatable& aAnimatable)
+ToAnimationValue(nsCSSPropertyID aProperty, const Animatable& aAnimatable)
 {
   RefPtr<RawServoAnimationValue> result;
 
   switch (aAnimatable.type()) {
     case Animatable::Tnull_t:
       break;
     case Animatable::TArrayOfTransformFunction: {
       const InfallibleTArray<TransformFunction>& transforms =
@@ -535,16 +535,20 @@ ToAnimationValue(const Animatable& aAnim
         MOZ_ASSERT(list, "Transform list should be non null");
         result = Servo_AnimationValue_Transform(*list).Consume();
       }
       break;
     }
     case Animatable::Tfloat:
       result = Servo_AnimationValue_Opacity(aAnimatable.get_float()).Consume();
       break;
+    case Animatable::Tnscolor:
+      result = Servo_AnimationValue_Color(aProperty,
+                                          aAnimatable.get_nscolor()).Consume();
+      break;
     default:
       MOZ_ASSERT_UNREACHABLE("Unsupported type");
   }
   return result.forget();
 }
 
 void
 AnimationHelper::SetAnimations(
@@ -575,17 +579,18 @@ AnimationHelper::SetAnimations(
           animation.fillMode() = static_cast<uint8_t>(dom::FillMode::Both);
         }
         break;
       default:
         break;
     }
 
     if (animation.baseStyle().type() != Animatable::Tnull_t) {
-      aBaseAnimationStyle = ToAnimationValue(animation.baseStyle());
+      aBaseAnimationStyle = ToAnimationValue(animation.property(),
+                                             animation.baseStyle());
     }
 
     AnimData* data = aAnimData.AppendElement();
 
     data->mTiming = TimingParams {
       animation.duration(),
       animation.delay(),
       animation.endDelay(),
@@ -600,18 +605,20 @@ AnimationHelper::SetAnimations(
       data->mFunctions;
     InfallibleTArray<RefPtr<RawServoAnimationValue>>& startValues =
       data->mStartValues;
     InfallibleTArray<RefPtr<RawServoAnimationValue>>& endValues =
       data->mEndValues;
 
     const InfallibleTArray<AnimationSegment>& segments = animation.segments();
     for (const AnimationSegment& segment : segments) {
-      startValues.AppendElement(ToAnimationValue(segment.startState()));
-      endValues.AppendElement(ToAnimationValue(segment.endState()));
+      startValues.AppendElement(ToAnimationValue(animation.property(),
+                                                 segment.startState()));
+      endValues.AppendElement(ToAnimationValue(animation.property(),
+                                               segment.endState()));
 
       TimingFunction tf = segment.sampleFn();
       Maybe<ComputedTimingFunction> ctf =
         AnimationUtils::TimingFunctionToComputedTimingFunction(tf);
       functions.AppendElement(ctf);
     }
   }
 }
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -628,16 +628,29 @@ ApplyAnimatedValue(Layer* aLayer,
 {
   if (!aValue) {
     // Return gracefully if we have no valid AnimationValue.
     return;
   }
 
   HostLayer* layerCompositor = aLayer->AsHostLayer();
   switch (aProperty) {
+    case eCSSProperty_background_color: {
+      // We don't support 'color' animations on the compositor yet so we never
+      // meet currentColor on the compositor.
+      nscolor color = Servo_AnimationValue_GetColor(aValue, NS_RGBA(0, 0, 0, 0));
+      aLayer->AsColorLayer()->SetColor(gfx::Color::FromABGR(color));
+      aStorage->SetAnimatedValue(aLayer->GetCompositorAnimationsId(), color);
+
+      layerCompositor->SetShadowOpacity(aLayer->GetOpacity());
+      layerCompositor->SetShadowOpacitySetByAnimation(false);
+      layerCompositor->SetShadowBaseTransform(aLayer->GetBaseTransform());
+      layerCompositor->SetShadowTransformSetByAnimation(false);
+      break;
+    }
     case eCSSProperty_opacity: {
       float opacity = Servo_AnimationValue_GetOpacity(aValue);
       layerCompositor->SetShadowOpacity(opacity);
       layerCompositor->SetShadowOpacitySetByAnimation(true);
       aStorage->SetAnimatedValue(aLayer->GetCompositorAnimationsId(), opacity);
 
       layerCompositor->SetShadowBaseTransform(aLayer->GetBaseTransform());
       layerCompositor->SetShadowTransformSetByAnimation(false);
@@ -704,29 +717,32 @@ SampleAnimations(Layer* aLayer,
                                aStorage,
                                animation.property(),
                                animation.data(),
                                animationValue);
             break;
           }
           case AnimationHelper::SampleResult::Skipped:
             switch (animations[0].property()) {
+              case eCSSProperty_background_color:
               case eCSSProperty_opacity: {
-                MOZ_ASSERT(
-                  layer->AsHostLayer()->GetShadowOpacitySetByAnimation());
+                if (animations[0].property() == eCSSProperty_opacity) {
+                  MOZ_ASSERT(
+                    layer->AsHostLayer()->GetShadowOpacitySetByAnimation());
 #ifdef DEBUG
-                // Disable this assertion until the root cause is fixed in bug
-                // 1459775.
-                // MOZ_ASSERT(FuzzyEqualsMultiplicative(
-                //   Servo_AnimationValue_GetOpacity(animationValue),
-                //   *(aStorage->GetAnimationOpacity(layer->GetCompositorAnimationsId()))));
+                  // Disable this assertion until the root cause is fixed in bug
+                  // 1459775.
+                  // MOZ_ASSERT(FuzzyEqualsMultiplicative(
+                  //   Servo_AnimationValue_GetOpacity(animationValue),
+                  //   *(aStorage->GetAnimationOpacity(layer->GetCompositorAnimationsId()))));
 #endif
-                // Even if opacity animation value has unchanged, we have to set
-                // the shadow base transform value here since the value might
-                // have been changed by APZC.
+                }
+                // Even if opacity or background-color  animation value has
+                // unchanged, we have to set the shadow base transform value
+                // here since the value might have been changed by APZC.
                 HostLayer* layerCompositor = layer->AsHostLayer();
                 layerCompositor->SetShadowBaseTransform(
                   layer->GetBaseTransform());
                 layerCompositor->SetShadowTransformSetByAnimation(false);
                 break;
               }
               case eCSSProperty_transform: {
                 MOZ_ASSERT(
--- a/gfx/layers/ipc/LayersMessages.ipdlh
+++ b/gfx/layers/ipc/LayersMessages.ipdlh
@@ -14,16 +14,17 @@ include "mozilla/GfxMessageUtils.h";
 include "ImageLayers.h";
 
 using mozilla::gfx::Glyph from "mozilla/gfx/2D.h";
 using mozilla::gfx::SamplingFilter from "mozilla/gfx/2D.h";
 using struct mozilla::gfx::Color from "mozilla/gfx/2D.h";
 using struct mozilla::gfx::Point3D from "mozilla/gfx/Point.h";
 using mozilla::gfx::IntPoint from "mozilla/gfx/Point.h";
 using class mozilla::gfx::Matrix4x4 from "mozilla/gfx/Matrix.h";
+using nscolor from "nsColor.h";
 using nscoord from "nsCoord.h";
 using struct nsRect from "nsRect.h";
 using struct nsPoint from "nsPoint.h";
 using class mozilla::TimeDuration from "mozilla/TimeStamp.h";
 using class mozilla::TimeStamp from "mozilla/TimeStamp.h";
 using mozilla::ScreenRotation from "mozilla/WidgetUtils.h";
 using nsCSSPropertyID from "nsCSSPropertyID.h";
 using hal::ScreenOrientation from "mozilla/HalScreenConfiguration.h";
@@ -155,16 +156,17 @@ union TransformFunction {
 union MaybeTimeDuration {
   null_t;
   TimeDuration;
 };
 
 union Animatable {
   null_t;
   float;
+  nscolor;
   TransformFunction[];
 };
 
 struct AnimationSegment {
   Animatable startState;
   Animatable endState;
   float startPortion;
   float endPortion;
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -433,16 +433,33 @@ SetAnimatable(nsCSSPropertyID aProperty,
   MOZ_ASSERT(aFrame);
 
   if (aAnimationValue.IsNull()) {
     aAnimatable = null_t();
     return;
   }
 
   switch (aProperty) {
+    case eCSSProperty_background_color: {
+      // We don't support color animation on the compositor yet so that we can
+      // resolve currentColor at this moment.
+      nscolor foreground;
+      if (aFrame->Style()->RelevantLinkVisited()) {
+        if (ComputedStyle* styleIfVisited =
+              aFrame->Style()->GetStyleIfVisited()) {
+          foreground = styleIfVisited->StyleColor()->mColor;
+        } else {
+          foreground = aFrame->Style()->StyleColor()->mColor;
+        }
+      } else {
+        foreground = aFrame->Style()->StyleColor()->mColor;
+      }
+      aAnimatable = aAnimationValue.GetColor(foreground);
+      break;
+    }
     case eCSSProperty_opacity:
       aAnimatable = aAnimationValue.GetOpacity();
       break;
     case eCSSProperty_transform: {
       aAnimatable = InfallibleTArray<TransformFunction>();
       MOZ_ASSERT(aAnimationValue.mServo);
       RefPtr<nsCSSValueSharedList> list;
       Servo_AnimationValue_GetTransform(aAnimationValue.mServo, &list);
@@ -668,17 +685,17 @@ AddAnimationsForProperty(nsIFrame* aFram
 
     data = TransformData(origin,
                          offsetToTransformOrigin,
                          bounds,
                          devPixelsToAppUnits,
                          scaleX,
                          scaleY,
                          hasPerspectiveParent);
-  } else if (aProperty == eCSSProperty_opacity) {
+  } else {
     data = null_t();
   }
 
   MOZ_ASSERT(
     nsCSSProps::PropHasFlags(aProperty, CSSPropFlags::CanAnimateOnCompositor),
     "inconsistent property flags");
 
   // Add from first to last (since last overrides)
@@ -3524,16 +3541,17 @@ nsDisplaySolidColor::GetBounds(nsDisplay
 LayerState
 nsDisplaySolidColor::GetLayerState(nsDisplayListBuilder* aBuilder,
                                    LayerManager* aManager,
                                    const ContainerLayerParameters& aParameters)
 {
   if (ForceActiveLayers()) {
     return LAYER_ACTIVE;
   }
+
   return LAYER_NONE;
 }
 
 already_AddRefed<Layer>
 nsDisplaySolidColor::BuildLayer(
   nsDisplayListBuilder* aBuilder,
   LayerManager* aManager,
   const ContainerLayerParameters& aContainerParameters)
@@ -5017,16 +5035,22 @@ nsDisplayBackgroundColor::GetLayerState(
   LayerManager* aManager,
   const ContainerLayerParameters& aParameters)
 {
   StyleGeometryBox clip =
     mBackgroundStyle->StyleBackground()->mImage.mLayers[0].mClip;
   if (ForceActiveLayers() && clip != StyleGeometryBox::Text) {
     return LAYER_ACTIVE;
   }
+
+  if (EffectCompositor::HasAnimationsForCompositor(
+        mFrame, eCSSProperty_background_color)) {
+    return LAYER_ACTIVE_FORCE;
+  }
+
   return LAYER_NONE;
 }
 
 already_AddRefed<Layer>
 nsDisplayBackgroundColor::BuildLayer(
   nsDisplayListBuilder* aBuilder,
   LayerManager* aManager,
   const ContainerLayerParameters& aContainerParameters)
@@ -5044,16 +5068,21 @@ nsDisplayBackgroundColor::BuildLayer(
   }
   layer->SetColor(mColor);
 
   int32_t appUnitsPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
   layer->SetBounds(mBackgroundRect.ToNearestPixels(appUnitsPerDevPixel));
   layer->SetBaseTransform(gfx::Matrix4x4::Translation(
     aContainerParameters.mOffset.x, aContainerParameters.mOffset.y, 0));
 
+  nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(
+    layer, aBuilder,
+    this, mFrame,
+    eCSSProperty_background_color);
+
   return layer.forget();
 }
 
 bool
 nsDisplayBackgroundColor::CreateWebRenderCommands(
   mozilla::wr::DisplayListBuilder& aBuilder,
   mozilla::wr::IpcResourceUpdateQueue& aResources,
   const StackingContextHelper& aSc,
@@ -8566,16 +8595,24 @@ nsDisplayOpacity::CanUseAsyncAnimations(
 }
 
 bool
 nsDisplayTransform::CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder)
 {
   return mAllowAsyncAnimation;
 }
 
+bool
+nsDisplayBackgroundColor::CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder)
+{
+  LayerManager* layerManager = aBuilder->GetWidgetLayerManager();
+  return layerManager &&
+         layerManager->GetBackendType() != layers::LayersBackend::LAYERS_WR;
+}
+
 /* static */ auto
 nsDisplayTransform::ShouldPrerenderTransformedContent(
   nsDisplayListBuilder* aBuilder,
   nsIFrame* aFrame,
   nsRect* aDirtyRect) -> PrerenderDecision
 {
   // Elements whose transform has been modified recently, or which
   // have a compositor-animated transform, can be prerendered. An element
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -5069,16 +5069,18 @@ public:
     if (aFrame == mDependentFrame) {
       mDependentFrame = nullptr;
     }
     nsDisplayItem::RemoveFrame(aFrame);
   }
 
   void WriteDebugInfo(std::stringstream& aStream) override;
 
+  bool CanUseAsyncAnimations(nsDisplayListBuilder* aBuilder) override;
+
 protected:
   const nsRect mBackgroundRect;
   RefPtr<mozilla::ComputedStyle> mBackgroundStyle;
   nsIFrame* mDependentFrame;
   mozilla::gfx::Color mColor;
 
   struct
   {
--- a/layout/style/LayerAnimationInfo.cpp
+++ b/layout/style/LayerAnimationInfo.cpp
@@ -9,24 +9,27 @@
 #include "nsCSSProps.h" // For nsCSSProps::PropHasFlags
 #include "nsCSSPropertyIDSet.h" // For nsCSSPropertyIDSet::CompositorAnimatable
 
 namespace mozilla {
 
 /* static */ const Array<DisplayItemType,
                          nsCSSPropertyIDSet::CompositorAnimatableCount()>
   LayerAnimationInfo::sDisplayItemTypes = {
+    DisplayItemType::TYPE_BACKGROUND_COLOR,
     DisplayItemType::TYPE_OPACITY,
     DisplayItemType::TYPE_TRANSFORM,
   };
 
 /* static */ DisplayItemType
 LayerAnimationInfo::GetDisplayItemTypeForProperty(nsCSSPropertyID aProperty)
 {
   switch (aProperty) {
+    case eCSSProperty_background_color:
+      return DisplayItemType::TYPE_BACKGROUND_COLOR;
     case eCSSProperty_opacity:
       return DisplayItemType::TYPE_OPACITY;
     case eCSSProperty_transform:
       return DisplayItemType::TYPE_TRANSFORM;
     default:
       break;
   }
   return DisplayItemType::TYPE_ZERO;
--- a/layout/style/LayerAnimationInfo.h
+++ b/layout/style/LayerAnimationInfo.h
@@ -29,19 +29,23 @@ struct LayerAnimationInfo {
   // be animated on the compositor.
   static inline const nsCSSPropertyIDSet&
   GetCSSPropertiesFor(DisplayItemType aDisplayItemType)
   {
     static const nsCSSPropertyIDSet transformProperties =
       nsCSSPropertyIDSet{ eCSSProperty_transform };
     static const nsCSSPropertyIDSet opacityProperties =
       nsCSSPropertyIDSet{ eCSSProperty_opacity };
+    static const nsCSSPropertyIDSet backgroundColorProperties =
+      nsCSSPropertyIDSet{ eCSSProperty_background_color };
     static const nsCSSPropertyIDSet empty = nsCSSPropertyIDSet();
 
     switch (aDisplayItemType) {
+      case DisplayItemType::TYPE_BACKGROUND_COLOR:
+        return backgroundColorProperties;
       case DisplayItemType::TYPE_OPACITY:
         return opacityProperties;
       case DisplayItemType::TYPE_TRANSFORM:
         return transformProperties;
       default:
         MOZ_ASSERT_UNREACHABLE("Should not be called for display item types "
                                "that are not able to have animations on the "
                                "compositor");
@@ -53,16 +57,18 @@ struct LayerAnimationInfo {
   // |aDisplayItemType|.
   //
   // This function works only for display items tied to CSS properties that can
   // be animated on the compositor.
   static inline nsChangeHint
   GetChangeHintFor(DisplayItemType aDisplayItemType)
   {
     switch (aDisplayItemType) {
+      case DisplayItemType::TYPE_BACKGROUND_COLOR:
+        return nsChangeHint_RepaintFrame;
       case DisplayItemType::TYPE_OPACITY:
         return nsChangeHint_UpdateOpacityLayer;
       case DisplayItemType::TYPE_TRANSFORM:
         return nsChangeHint_UpdateTransformLayer;
       default:
         MOZ_ASSERT_UNREACHABLE("Should not be called for display item types "
                                "that are not able to have animations on the "
                                "compositor");
--- a/layout/style/ServoBindings.h
+++ b/layout/style/ServoBindings.h
@@ -671,16 +671,21 @@ double Servo_AnimationValues_ComputeDist
   RawServoAnimationValueBorrowed from,
   RawServoAnimationValueBorrowed to);
 
 void Servo_AnimationValue_Serialize(
   RawServoAnimationValueBorrowed value,
   nsCSSPropertyID property,
   nsAString* buffer);
 
+nscolor Servo_AnimationValue_GetColor(RawServoAnimationValueBorrowed value,
+                                      nscolor foregroundColor);
+RawServoAnimationValueStrong Servo_AnimationValue_Color(nsCSSPropertyID,
+                                                        nscolor);
+
 float Servo_AnimationValue_GetOpacity(RawServoAnimationValueBorrowed value);
 RawServoAnimationValueStrong Servo_AnimationValue_Opacity(float);
 
 void Servo_AnimationValue_GetTransform(
   RawServoAnimationValueBorrowed value,
   RefPtr<nsCSSValueSharedList>* list);
 
 RawServoAnimationValueStrong Servo_AnimationValue_Transform(
--- a/layout/style/StyleAnimationValue.cpp
+++ b/layout/style/StyleAnimationValue.cpp
@@ -115,16 +115,23 @@ AnimationValue::operator!=(const Animati
 
 float
 AnimationValue::GetOpacity() const
 {
   MOZ_ASSERT(mServo);
   return Servo_AnimationValue_GetOpacity(mServo);
 }
 
+nscolor
+AnimationValue::GetColor(nscolor aForegroundColor) const
+{
+  MOZ_ASSERT(mServo);
+  return Servo_AnimationValue_GetColor(mServo, aForegroundColor);
+}
+
 already_AddRefed<const nsCSSValueSharedList>
 AnimationValue::GetTransformList() const
 {
   MOZ_ASSERT(mServo);
 
   RefPtr<nsCSSValueSharedList> transform;
   Servo_AnimationValue_GetTransform(mServo, &transform);
   return transform.forget();
--- a/layout/style/StyleAnimationValue.h
+++ b/layout/style/StyleAnimationValue.h
@@ -73,16 +73,20 @@ struct AnimationValue
 
   bool IsNull() const
   {
     return !mServo;
   }
 
   float GetOpacity() const;
 
+  // Returns nscolor value in this AnimationValue.
+  // Currently only background-color is supported.
+  nscolor GetColor(nscolor aForegroundColor) const;
+
   // Return the transform list as a RefPtr.
   already_AddRefed<const nsCSSValueSharedList> GetTransformList() const;
 
   // Return the scale for mServo, which is calculated with reference to aFrame.
   mozilla::gfx::Size GetScaleValue(const nsIFrame* aFrame) const;
 
   // Uncompute this AnimationValue and then serialize it.
   void SerializeSpecifiedValue(nsCSSPropertyID aProperty,
--- a/modules/libpref/init/StaticPrefList.h
+++ b/modules/libpref/init/StaticPrefList.h
@@ -489,16 +489,28 @@ VARCACHE_PREF(
 )
 
 VARCACHE_PREF(
   "gfx.font_ahem_antialias_none",
    gfx_font_ahem_antialias_none,
   RelaxedAtomicBool, false
 )
 
+#ifdef RELEASE_OR_BETA
+# define PREF_VALUE false
+#else
+# define PREF_VALUE true
+#endif
+VARCACHE_PREF(
+  "gfx.omta.background-color",
+   gfx_omta_background_color,
+  bool, PREF_VALUE
+)
+#undef PREF_VALUE
+
 //---------------------------------------------------------------------------
 // HTML5 parser prefs
 //---------------------------------------------------------------------------
 
 // Toggle which thread the HTML5 parser uses for stream parsing.
 VARCACHE_PREF(
   "html5.offmainthread",
    html5_offmainthread,
--- a/servo/components/style/properties/longhands/background.mako.rs
+++ b/servo/components/style/properties/longhands/background.mako.rs
@@ -10,17 +10,18 @@
     "background-color",
     "Color",
     "computed_value::T::transparent()",
     initial_specified_value="SpecifiedValue::transparent()",
     spec="https://drafts.csswg.org/css-backgrounds/#background-color",
     animation_value_type="AnimatedColor",
     ignored_when_colors_disabled=True,
     allow_quirks=True,
-    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER",
+    flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER \
+           CAN_ANIMATE_ON_COMPOSITOR",
 )}
 
 ${helpers.predefined_type(
     "background-image",
     "ImageLayer",
     initial_value="Either::First(None_)",
     initial_specified_value="Either::First(None_)",
     spec="https://drafts.csswg.org/css-backgrounds/#the-background-image",
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -793,31 +793,76 @@ pub extern "C" fn Servo_AnimationValue_S
             buffer,
             None,
             None, /* No extra custom properties */
         );
     debug_assert!(rv.is_ok());
 }
 
 #[no_mangle]
+pub extern "C" fn Servo_AnimationValue_GetColor(
+    value: RawServoAnimationValueBorrowed,
+    foreground_color: structs::nscolor,
+) -> structs::nscolor {
+    use style::gecko::values::convert_nscolor_to_rgba;
+    use style::gecko::values::convert_rgba_to_nscolor;
+    use style::values::animated::ToAnimatedValue;
+    use style::values::computed::color::Color as ComputedColor;
+
+    let value = AnimationValue::as_arc(&value);
+    match **value {
+        AnimationValue::BackgroundColor(color) => {
+            let computed: ComputedColor = ToAnimatedValue::from_animated_value(color);
+            let foreground_color = convert_nscolor_to_rgba(foreground_color);
+            convert_rgba_to_nscolor(&computed.to_rgba(foreground_color))
+        }
+        _ => panic!("Other color properties are not supported yet"),
+    }
+}
+
+#[no_mangle]
 pub extern "C" fn Servo_AnimationValue_GetOpacity(value: RawServoAnimationValueBorrowed) -> f32 {
     let value = AnimationValue::as_arc(&value);
     if let AnimationValue::Opacity(opacity) = **value {
         opacity
     } else {
         panic!("The AnimationValue should be Opacity");
     }
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_AnimationValue_Opacity(opacity: f32) -> RawServoAnimationValueStrong {
     Arc::new(AnimationValue::Opacity(opacity)).into_strong()
 }
 
 #[no_mangle]
+pub extern "C" fn Servo_AnimationValue_Color(
+    color_property: nsCSSPropertyID,
+    color: structs::nscolor
+) -> RawServoAnimationValueStrong {
+    use style::gecko::values::convert_nscolor_to_rgba;
+    use style::values::animated::color::RGBA as AnimatedRGBA;
+
+    let property = LonghandId::from_nscsspropertyid(color_property)
+        .expect("We don't have shorthand property animation value");
+
+    let rgba = convert_nscolor_to_rgba(color);
+
+    let animatedRGBA = AnimatedRGBA::new(rgba.red_f32(),
+                                         rgba.green_f32(),
+                                         rgba.blue_f32(),
+                                         rgba.alpha_f32());
+    match property {
+        LonghandId::BackgroundColor =>
+            Arc::new(AnimationValue::BackgroundColor(animatedRGBA.into())).into_strong(),
+        _ => panic!("Should be background-color property"),
+    }
+}
+
+#[no_mangle]
 pub extern "C" fn Servo_AnimationValue_GetTransform(
     value: RawServoAnimationValueBorrowed,
     list: *mut structs::RefPtr<nsCSSValueSharedList>,
 ) {
     let value = AnimationValue::as_arc(&value);
     if let AnimationValue::Transform(ref servo_list) = **value {
         let list = unsafe { &mut *list };
         if servo_list.0.is_empty() {