Bug 1504065 - Support background-color animations on the compositor for nsIDOMWindowUtils::GetOMTAValue. r=birtles
☠☠ backed out by 20898bcaaa00 ☠ ☠
authorHiroyuki Ikezoe <hikezoe@mozilla.com>
Tue, 27 Nov 2018 09:27:22 +0000
changeset 507424 1291883702312b5ca9c44983121ac39fd2b8bdf6
parent 507423 359e81b35cfb72928457964fcf1c826db1404818
child 507425 009f60526bb42f964bbefd9feb3213f5651c0ae1
push id1905
push userffxbld-merge
push dateMon, 21 Jan 2019 12:33:13 +0000
treeherdermozilla-release@c2fca1944d8c [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 - Support background-color animations on the compositor for nsIDOMWindowUtils::GetOMTAValue. r=birtles Depends on D13001 Differential Revision: https://phabricator.services.mozilla.com/D13002
dom/base/nsDOMWindowUtils.cpp
gfx/layers/AnimationHelper.cpp
gfx/layers/AnimationHelper.h
gfx/layers/ipc/LayersMessages.ipdlh
layout/style/nsComputedDOMStyle.cpp
layout/style/nsComputedDOMStyle.h
layout/style/test/animation_utils.js
layout/style/test/mochitest.ini
layout/style/test/test_animations_omta.html
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -3715,16 +3715,24 @@ nsDOMWindowUtils::GetOMTAStyle(Element* 
       }
     } else if (aProperty.EqualsLiteral("transform")) {
       OMTAValue value = GetOMTAValue(frame,
                                      DisplayItemType::TYPE_TRANSFORM,
                                      GetWebRenderBridge());
       if (value.type() == OMTAValue::TMatrix4x4) {
         cssValue = nsComputedDOMStyle::MatrixToCSSValue(value.get_Matrix4x4());
       }
+    } else if (aProperty.EqualsLiteral("background-color")) {
+      OMTAValue value = GetOMTAValue(frame,
+                                     DisplayItemType::TYPE_BACKGROUND_COLOR,
+                                     GetWebRenderBridge());
+      if (value.type() == OMTAValue::Tnscolor) {
+        cssValue = new nsROCSSPrimitiveValue;
+        nsComputedDOMStyle::SetToRGBAColor(cssValue, value.get_nscolor());
+      }
     }
   }
 
   if (cssValue) {
     nsString text;
     ErrorResult rv;
     cssValue->GetCssText(text, rv);
     aResult.Assign(text);
--- a/gfx/layers/AnimationHelper.cpp
+++ b/gfx/layers/AnimationHelper.cpp
@@ -50,16 +50,19 @@ CompositorAnimationStorage::GetOMTAValue
 {
   OMTAValue omtaValue = mozilla::null_t();
   auto animatedValue = GetAnimatedValue(aId);
   if (!animatedValue) {
     return omtaValue;
   }
 
   switch (animatedValue->mType) {
+    case AnimatedValue::COLOR:
+      omtaValue = animatedValue->mColor;
+      break;
     case AnimatedValue::OPACITY:
       omtaValue = animatedValue->mOpacity;
       break;
     case AnimatedValue::TRANSFORM: {
       gfx::Matrix4x4 transform = animatedValue->mTransform.mFrameTransform;
       const TransformData& data = animatedValue->mTransform.mData;
       float scale = data.appUnitsPerDevPixel();
       gfx::Point3D transformOrigin = data.transformOrigin();
@@ -114,16 +117,28 @@ CompositorAnimationStorage::SetAnimatedV
   const TransformData dontCare = {};
   SetAnimatedValue(aId,
                    std::move(aTransformInDevSpace),
                    gfx::Matrix4x4(),
                    dontCare);
 }
 
 void
+CompositorAnimationStorage::SetAnimatedValue(uint64_t aId, nscolor aColor)
+{
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+  auto count = mAnimatedValues.Count();
+  AnimatedValue* value = mAnimatedValues.LookupOrAdd(aId, aColor);
+  if (count == mAnimatedValues.Count()) {
+    MOZ_ASSERT(value->mType == AnimatedValue::COLOR);
+    value->mColor = aColor;
+  }
+}
+
+void
 CompositorAnimationStorage::SetAnimatedValue(uint64_t aId,
                                              const float& aOpacity)
 {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   auto count = mAnimatedValues.Count();
   AnimatedValue* value = mAnimatedValues.LookupOrAdd(aId, aOpacity);
   if (count == mAnimatedValues.Count()) {
     MOZ_ASSERT(value->mType == AnimatedValue::OPACITY);
--- a/gfx/layers/AnimationHelper.h
+++ b/gfx/layers/AnimationHelper.h
@@ -50,22 +50,24 @@ struct AnimationTransform {
   gfx::Matrix4x4 mFrameTransform;
   TransformData mData;
 };
 
 struct AnimatedValue {
   enum {
     TRANSFORM,
     OPACITY,
+    COLOR,
     NONE
   } mType {NONE};
 
   union {
     AnimationTransform mTransform;
     float mOpacity;
+    nscolor mColor;
   };
 
   AnimatedValue(gfx::Matrix4x4&& aTransformInDevSpace,
                 gfx::Matrix4x4&& aFrameTransform,
                 const TransformData& aData)
     : mType(AnimatedValue::TRANSFORM)
     , mOpacity(0.0)
   {
@@ -75,16 +77,22 @@ struct AnimatedValue {
   }
 
   explicit AnimatedValue(const float& aValue)
     : mType(AnimatedValue::OPACITY)
     , mOpacity(aValue)
   {
   }
 
+  explicit AnimatedValue(nscolor aValue)
+    : mType(AnimatedValue::COLOR)
+    , mColor(aValue)
+  {
+  }
+
   ~AnimatedValue() {}
 
 private:
   AnimatedValue() = delete;
 };
 
 // CompositorAnimationStorage stores the animations and animated values
 // keyed by a CompositorAnimationsId. The "animations" are a representation of
@@ -124,16 +132,21 @@ public:
                         gfx::Matrix4x4&& aTransformInDevSpace);
 
   /**
    * Set the animation opacity based on the unique id
    */
   void SetAnimatedValue(uint64_t aId, const float& aOpacity);
 
   /**
+   * Set the animation color based on the unique id
+   */
+  void SetAnimatedValue(uint64_t aId, nscolor aColor);
+
+  /**
    * Return the animated value if a given id can map to its animated value
    */
   AnimatedValue* GetAnimatedValue(const uint64_t& aId) const;
 
   OMTAValue GetOMTAValue(const uint64_t& aId) const;
 
   /**
    * Return the iterator of animated value table
--- a/gfx/layers/ipc/LayersMessages.ipdlh
+++ b/gfx/layers/ipc/LayersMessages.ipdlh
@@ -562,14 +562,15 @@ struct TransactionInfo
 
 union MaybeTransform {
   Matrix4x4;
   void_t;
 };
 
 union OMTAValue {
   null_t;
+  nscolor;
   float;
   Matrix4x4;
 };
 
 } // namespace
 } // namespace
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -1112,17 +1112,17 @@ nsComputedDOMStyle::DoGetBinding()
 }
 
 already_AddRefed<CSSValue>
 nsComputedDOMStyle::DoGetBottom()
 {
   return GetOffsetWidthFor(eSideBottom);
 }
 
-void
+/* static */ void
 nsComputedDOMStyle::SetToRGBAColor(nsROCSSPrimitiveValue* aValue,
                                    nscolor aColor)
 {
   nsAutoString string;
   const bool hasAlpha = NS_GET_A(aColor) != 255;
   if (hasAlpha) {
     string.AppendLiteral("rgba(");
   } else {
--- a/layout/style/nsComputedDOMStyle.h
+++ b/layout/style/nsComputedDOMStyle.h
@@ -126,16 +126,17 @@ public:
                                      mozilla::MutationClosureData*) override;
   virtual nsIDocument* DocToUpdate() override;
 
   nsDOMCSSDeclaration::ParsingEnvironment
   GetParsingEnvironment(nsIPrincipal* aSubjectPrincipal) const final;
 
   static already_AddRefed<nsROCSSPrimitiveValue>
     MatrixToCSSValue(const mozilla::gfx::Matrix4x4& aMatrix);
+  static void SetToRGBAColor(nsROCSSPrimitiveValue* aValue, nscolor aColor);
 
   static void RegisterPrefChangeCallbacks();
   static void UnregisterPrefChangeCallbacks();
 
   // nsIMutationObserver
   NS_DECL_NSIMUTATIONOBSERVER_PARENTCHAINCHANGED
 
 private:
@@ -423,17 +424,16 @@ private:
   already_AddRefed<CSSValue> DoGetPaintOrder();
 
 
   // For working around a MSVC bug. See related comment in
   // GenerateComputedDOMStyleGenerated.py.
   already_AddRefed<CSSValue> DummyGetter();
 
   /* Helper functions */
-  void SetToRGBAColor(nsROCSSPrimitiveValue* aValue, nscolor aColor);
   void SetValueFromComplexColor(nsROCSSPrimitiveValue* aValue,
                                 const mozilla::StyleComplexColor& aColor);
   void SetValueToPositionCoord(const mozilla::Position::Coord& aCoord,
                                nsROCSSPrimitiveValue* aValue);
   void SetValueToPosition(const mozilla::Position& aPosition,
                           nsDOMCSSValueList* aValueList);
   void SetValueToURLValue(const mozilla::css::URLValue* aURL,
                           nsROCSSPrimitiveValue* aValue);
--- a/layout/style/test/animation_utils.js
+++ b/layout/style/test/animation_utils.js
@@ -417,29 +417,40 @@ const ExpectComparisonTo = {
   };
 
   // Many callers of this method will pass 'undefined' for
   // expectedComparisonResult.
   window.omta_is_approx = function(elem, property, expected, tolerance,
                                    runningOn, desc, expectedComparisonResult,
                                    pseudo) {
     // Check input
-    const omtaProperties = [ "transform", "opacity" ];
+    // FIXME: Auto generate this array.
+    const omtaProperties = [ "transform", "opacity", "background-color" ];
     if (!omtaProperties.includes(property)) {
       ok(false, property + " is not an OMTA property");
       return;
     }
-    var isTransform = property == "transform";
-    var normalize = isTransform ? convertTo3dMatrix : parseFloat;
-    var compare = isTransform ?
-                  matricesRoughlyEqual :
-                  function(a, b, error) { return Math.abs(a - b) <= error; };
-    var normalizedToString = isTransform ?
-                             convert3dMatrixToString :
-                             JSON.stringify;
+    var normalize;
+    var compare;
+    var normalizedToString = JSON.stringify;
+    switch (property) {
+      case "transform":
+        normalize = convertTo3dMatrix;
+        compare = matricesRoughlyEqual;
+        normalizedToString = convert3dMatrixToString;
+        break;
+      case "opacity":
+        normalize = parseFloat;
+        compare = function(a, b, error) { return Math.abs(a - b) <= error; };
+        break;
+      default:
+        normalize = function(value) { return value; };
+        compare = function(a, b, error) { return a == b; };
+        break;
+    }
 
     // Get actual values
     var compositorStr =
       SpecialPowers.DOMWindowUtils.getOMTAStyle(elem, property, pseudo);
     var computedStr = window.getComputedStyle(elem, pseudo)[property];
 
     // Prepare expected value
     var expectedValue = normalize(expected);
--- a/layout/style/test/mochitest.ini
+++ b/layout/style/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.step-position-jump.enabled=true
 support-files =
   animation_utils.js
   ccd-quirks.html
   ccd.sjs
   ccd-standards.html
   chrome/bug418986-2.js
   chrome/match.png
--- a/layout/style/test/test_animations_omta.html
+++ b/layout/style/test/test_animations_omta.html
@@ -2386,10 +2386,127 @@ addAsyncAnimTest(async function() {
   // compositor thread.
   await waitForPaints();
   omta_is("transform", { tx: 200 }, RunningOn.MainThread,
           "transition runs on main thread at end of active interval");
 
   done_div();
 });
 
+if (SpecialPowers.DOMWindowUtils.layerManagerType != 'WebRender') {
+  // Normal background-color animation.
+  addAsyncAnimTest(async function() {
+    new_div("background-color: rgb(255, 0, 0); " +
+            "transition: background-color 10s linear");
+    await waitForPaintsFlushed();
+
+    gDiv.style.backgroundColor = "rgb(0, 255, 0)";
+    await waitForPaintsFlushed();
+
+    omta_is("background-color", "rgb(255, 0, 0)", RunningOn.Compositor,
+            "background-color transition runs on compositor thread");
+
+    advance_clock(5000);
+    omta_is("background-color", "rgb(128, 128, 0)", RunningOn.Compositor,
+            "background-color on compositor at 5s");
+
+    done_div();
+  });
+
+  // background-color animation with currentColor.
+  addAsyncAnimTest(async function() {
+    new_div("color: rgb(255, 0, 0); " +
+            "background-color: currentColor; " +
+            "transition: background-color 10s linear");
+    await waitForPaintsFlushed();
+
+    gDiv.style.backgroundColor = "rgb(0, 255, 0)";
+    await waitForPaintsFlushed();
+
+    omta_is("background-color", "rgb(255, 0, 0)", RunningOn.Compositor,
+            "background-color transition starting with current-color runs on " +
+            "compositor thread");
+
+    advance_clock(5000);
+    omta_is("background-color", "rgb(128, 128, 0)", RunningOn.Compositor,
+            "background-color on compositor at 5s");
+
+    done_div();
+  });
+
+  // Tests that a background-color animation from inherited currentColor to
+  // a normal color on the compositor is updated when the parent color is
+  // changed.
+  addAsyncAnimTest(async function() {
+    new_div("");
+    const parent = document.createElement("div");
+    gDiv.parentNode.insertBefore(parent, gDiv);
+    parent.style.color = "rgb(255, 0, 0)";
+    parent.appendChild(gDiv);
+
+    gDiv.animate({ backgroundColor: [ "currentColor", "rgb(0, 255, 0)" ] }, 1000);
+
+    await waitForPaintsFlushed();
+
+    omta_is("background-color", "rgb(255, 0, 0)", RunningOn.Compositor,
+            "background-color animation starting with current-color runs on " +
+            "compositor thread");
+
+    advance_clock(500);
+
+    omta_is("background-color", "rgb(128, 128, 0)", RunningOn.Compositor,
+            "background-color on compositor at 5s");
+
+    // Change the parent's color in the middle of the animation.
+    parent.style.color = "rgb(0, 0, 255)";
+    await waitForPaintsFlushed();
+
+    omta_is("background-color", "rgb(0, 128, 128)", RunningOn.Compositor,
+            "background-color on compositor is reflected by the parent's " +
+            "color change");
+
+    done_div();
+    parent.remove();
+  });
+
+  // Tests that a background-color animation from currentColor to a normal color
+  // on <a> element is updated when the link is visited.
+  addAsyncAnimTest(async function() {
+    [ gDiv ] = new_element("a", "display: block");
+    gDiv.setAttribute("href", "not-exist.html");
+    gDiv.classList.add("visited");
+
+    const extraStyle = document.createElement('style');
+    document.head.appendChild(extraStyle);
+    extraStyle.sheet.insertRule(".visited:visited { color: rgb(0, 0, 255); }", 0);
+    extraStyle.sheet.insertRule(".visited:link { color: rgb(255, 0, 0); }", 1);
+
+    gDiv.animate({ backgroundColor: [ "currentColor", "rgb(0, 255, 0)" ] }, 1000);
+    await waitForPaintsFlushed();
+
+    omta_is("background-color", "rgb(255, 0, 0)", RunningOn.Compositor,
+            "background-color animation starting with current-color runs on " +
+            "compositor thread");
+
+    advance_clock(500);
+
+    omta_is("background-color", "rgb(128, 128, 0)", RunningOn.Compositor,
+            "background-color on compositor at 5s");
+
+    gDiv.setAttribute("href", window.top.location.href);
+    await waitForVisitedLinkColoring(gDiv, "color", "rgb(0, 0, 255)");
+    await waitForPaintsFlushed();
+
+    // `omta_is` checks that the result on the compositor equals to the value by
+    // getComputedValue() but getComputedValue lies for visited link values so
+    // we use getOMTAStyle directly instead.
+    is(SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, "background-color"),
+       "rgb(0, 128, 128)",
+       "background-color on <a> element after the link is visited");
+
+    extraStyle.remove();
+    done_element();
+    gDiv = null;
+  });
+}
+
 </script>
 </html>