Reland "[BlinkGenPropertyTrees] Fix ElementIsAnimatingChanged logic"

This relands the commit b0eab4a3e36612417196e6766d7a770f675169c4 from
https://chromium-review.googlesource.com/c/chromium/src/+/1372173

The only change is to mark virtual/threaded/animations/skew-notsequential-compositor.html
as flaky; it has been decided that it is worth landing the CL anyway
and investigating the flakiness afterwards.

TBR=majidvp@chromium.org,pdr@chromium.org

Bug: 912574, 921105
Change-Id: I93dfc5db4056e3567f99254f28b1072a66c4f532
Reviewed-on: https://chromium-review.googlesource.com/c/1407385
Reviewed-by: Stephen McGruer <smcgruer@chromium.org>
Reviewed-by: Philip Rogers <pdr@chromium.org>
Commit-Queue: Stephen McGruer <smcgruer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#622123}
diff --git a/cc/animation/animation_host.cc b/cc/animation/animation_host.cc
index b78c578..f5d6109 100644
--- a/cc/animation/animation_host.cc
+++ b/cc/animation/animation_host.cc
@@ -158,10 +158,17 @@
   scoped_refptr<ElementAnimations> element_animations =
       GetElementAnimationsForElementId(element_id);
   DCHECK(element_animations);
+
+  // |ClearAffectedElementTypes| requires an ElementId map in order to update
+  // the property trees. Generating that map requires walking the keyframe
+  // effects, so we have to do it before removing this one.
+  PropertyToElementIdMap element_id_map =
+      element_animations->GetPropertyToElementIdMap();
+
   element_animations->RemoveKeyframeEffect(keyframe_effect);
 
   if (element_animations->IsEmpty()) {
-    element_animations->ClearAffectedElementTypes();
+    element_animations->ClearAffectedElementTypes(element_id_map);
     element_to_animations_map_.erase(element_animations->element_id());
     element_animations->SetAnimationHost(nullptr);
   }
diff --git a/cc/animation/element_animations.cc b/cc/animation/element_animations.cc
index 01722e1..dda8db9 100644
--- a/cc/animation/element_animations.cc
+++ b/cc/animation/element_animations.cc
@@ -84,7 +84,8 @@
   return properties;
 }
 
-void ElementAnimations::ClearAffectedElementTypes() {
+void ElementAnimations::ClearAffectedElementTypes(
+    const PropertyToElementIdMap& element_id_map) {
   DCHECK(animation_host_);
 
   TargetProperties disable_properties = GetPropertiesMaskForAnimationState();
@@ -96,7 +97,7 @@
   // mutator_host_client() to be null.
   if (has_element_in_active_list() && animation_host()->mutator_host_client()) {
     animation_host()->mutator_host_client()->ElementIsAnimatingChanged(
-        element_id(), ElementListType::ACTIVE, disabled_state_mask,
+        element_id_map, ElementListType::ACTIVE, disabled_state_mask,
         disabled_state);
   }
   set_has_element_in_active_list(false);
@@ -104,7 +105,7 @@
   if (has_element_in_pending_list() &&
       animation_host()->mutator_host_client()) {
     animation_host()->mutator_host_client()->ElementIsAnimatingChanged(
-        element_id(), ElementListType::PENDING, disabled_state_mask,
+        element_id_map, ElementListType::PENDING, disabled_state_mask,
         disabled_state);
   }
   set_has_element_in_pending_list(false);
@@ -349,15 +350,17 @@
   DCHECK(pending_state_.IsValid());
   DCHECK(active_state_.IsValid());
 
+  PropertyToElementIdMap element_id_map = GetPropertyToElementIdMap();
+
   if (has_element_in_active_list() && prev_active != active_state_) {
     PropertyAnimationState diff_active = prev_active ^ active_state_;
     animation_host()->mutator_host_client()->ElementIsAnimatingChanged(
-        element_id(), ElementListType::ACTIVE, diff_active, active_state_);
+        element_id_map, ElementListType::ACTIVE, diff_active, active_state_);
   }
   if (has_element_in_pending_list() && prev_pending != pending_state_) {
     PropertyAnimationState diff_pending = prev_pending ^ pending_state_;
     animation_host()->mutator_host_client()->ElementIsAnimatingChanged(
-        element_id(), ElementListType::PENDING, diff_pending, pending_state_);
+        element_id_map, ElementListType::PENDING, diff_pending, pending_state_);
   }
 }
 
@@ -467,6 +470,60 @@
   return gfx::ScrollOffset();
 }
 
+PropertyToElementIdMap ElementAnimations::GetPropertyToElementIdMap() const {
+  // As noted in the header documentation, this method assumes that each
+  // property type maps to at most one ElementId. This is not conceptually true
+  // for cc/animations, but it is true for the current clients:
+  //
+  //   * ui/ does not set per-keyframe-model ElementIds, so this map will be
+  //   each property type mapping to the same ElementId (i.e. element_id()).
+  //
+  //   * blink guarantees that any two keyframe models that it creates which
+  //   target the same property on the same target will have the same ElementId.
+  //
+  // In order to make this as little of a footgun as possible for future-us,
+  // this method DCHECKs that the assumption holds.
+
+  std::vector<PropertyToElementIdMap::value_type> entries;
+  for (int property_index = TargetProperty::FIRST_TARGET_PROPERTY;
+       property_index <= TargetProperty::LAST_TARGET_PROPERTY;
+       ++property_index) {
+    TargetProperty::Type property =
+        static_cast<TargetProperty::Type>(property_index);
+    ElementId element_id_for_property;
+    for (auto& keyframe_effect : keyframe_effects_list_) {
+      KeyframeModel* model = keyframe_effect.GetKeyframeModel(property);
+      if (model) {
+        // We deliberately use two branches here so that the DCHECK can
+        // differentiate between models with different element ids, and the case
+        // where some models don't have an element id.
+        // TODO(crbug.com/900241): All KeyframeModels should have an ElementId.
+        if (model->element_id()) {
+          DCHECK(!element_id_for_property ||
+                 element_id_for_property == model->element_id())
+              << "Different KeyframeModels for the same target must have the "
+              << "same ElementId";
+          element_id_for_property = model->element_id();
+        } else {
+          // This DCHECK isn't perfect; you could have a case where one model
+          // has an ElementId and the other doesn't, but model->element_id() ==
+          // this->element_id() and so the DCHECK passes. That is unlikely
+          // enough that we don't bother guarding against it specifically.
+          DCHECK(!element_id_for_property ||
+                 element_id_for_property == element_id())
+              << "Either all models should have an ElementId or none should";
+          element_id_for_property = element_id();
+        }
+      }
+    }
+
+    if (element_id_for_property)
+      entries.emplace_back(property, element_id_for_property);
+  }
+
+  return PropertyToElementIdMap(std::move(entries));
+}
+
 bool ElementAnimations::KeyframeModelAffectsActiveElements(
     KeyframeModel* keyframe_model) const {
   // When we force a keyframe_model update due to a notification, we do not have
diff --git a/cc/animation/element_animations.h b/cc/animation/element_animations.h
index 7c51dc7..629379a 100644
--- a/cc/animation/element_animations.h
+++ b/cc/animation/element_animations.h
@@ -55,7 +55,7 @@
   void SetAnimationHost(AnimationHost* host);
 
   void InitAffectedElementTypes();
-  void ClearAffectedElementTypes();
+  void ClearAffectedElementTypes(const PropertyToElementIdMap& element_id_map);
 
   void ElementRegistered(ElementId element_id, ElementListType list_type);
   void ElementUnregistered(ElementId element_id, ElementListType list_type);
@@ -164,6 +164,15 @@
 
   gfx::ScrollOffset ScrollOffsetForAnimation() const;
 
+  // Returns a map of target property to the ElementId for that property, for
+  // KeyframeEffects associated with this ElementAnimations.
+  //
+  // This method makes the assumption that a given target property doesn't map
+  // to more than one ElementId. While conceptually this isn't true for
+  // cc/animations, it is true for the two current clients (ui/ and blink) and
+  // this is required to let BGPT ship (see http://crbug.com/912574).
+  PropertyToElementIdMap GetPropertyToElementIdMap() const;
+
  private:
   friend class base::RefCounted<ElementAnimations>;
 
diff --git a/cc/test/animation_timelines_test_common.cc b/cc/test/animation_timelines_test_common.cc
index 4199443..a015f50 100644
--- a/cc/test/animation_timelines_test_common.cc
+++ b/cc/test/animation_timelines_test_common.cc
@@ -126,18 +126,17 @@
 }
 
 void TestHostClient::ElementIsAnimatingChanged(
-    ElementId element_id,
+    const PropertyToElementIdMap& element_id_map,
     ElementListType list_type,
     const PropertyAnimationState& mask,
     const PropertyAnimationState& state) {
-  TestLayer* layer = FindTestLayer(element_id, list_type);
-  if (!layer)
-    return;
+  for (const auto& it : element_id_map) {
+    TestLayer* layer = FindTestLayer(it.second, list_type);
+    if (!layer)
+      continue;
 
-  for (int property = TargetProperty::FIRST_TARGET_PROPERTY;
-       property <= TargetProperty::LAST_TARGET_PROPERTY; ++property) {
-    TargetProperty::Type target_property =
-        static_cast<TargetProperty::Type>(property);
+    TargetProperty::Type target_property = it.first;
+    int property = static_cast<int>(target_property);
     if (mask.potentially_animating[property])
       layer->set_has_potential_animation(target_property,
                                          state.potentially_animating[property]);
diff --git a/cc/test/animation_timelines_test_common.h b/cc/test/animation_timelines_test_common.h
index ff76f1d..b18a516 100644
--- a/cc/test/animation_timelines_test_common.h
+++ b/cc/test/animation_timelines_test_common.h
@@ -119,7 +119,7 @@
       ElementListType list_type,
       const gfx::ScrollOffset& scroll_offset) override;
 
-  void ElementIsAnimatingChanged(ElementId element_id,
+  void ElementIsAnimatingChanged(const PropertyToElementIdMap& element_id_map,
                                  ElementListType list_type,
                                  const PropertyAnimationState& mask,
                                  const PropertyAnimationState& state) override;
diff --git a/cc/trees/layer_tree_host.cc b/cc/trees/layer_tree_host.cc
index 0197be96..a70c34a 100644
--- a/cc/trees/layer_tree_host.cc
+++ b/cc/trees/layer_tree_host.cc
@@ -1733,12 +1733,12 @@
 }
 
 void LayerTreeHost::ElementIsAnimatingChanged(
-    ElementId element_id,
+    const PropertyToElementIdMap& element_id_map,
     ElementListType list_type,
     const PropertyAnimationState& mask,
     const PropertyAnimationState& state) {
   DCHECK_EQ(ElementListType::ACTIVE, list_type);
-  property_trees()->ElementIsAnimatingChanged(mutator_host(), element_id,
+  property_trees()->ElementIsAnimatingChanged(mutator_host(), element_id_map,
                                               list_type, mask, state, true);
 }
 
diff --git a/cc/trees/layer_tree_host.h b/cc/trees/layer_tree_host.h
index 8b0ae028..96907643 100644
--- a/cc/trees/layer_tree_host.h
+++ b/cc/trees/layer_tree_host.h
@@ -638,7 +638,7 @@
       ElementListType list_type,
       const gfx::ScrollOffset& scroll_offset) override;
 
-  void ElementIsAnimatingChanged(ElementId element_id,
+  void ElementIsAnimatingChanged(const PropertyToElementIdMap& element_id_map,
                                  ElementListType list_type,
                                  const PropertyAnimationState& mask,
                                  const PropertyAnimationState& state) override;
diff --git a/cc/trees/layer_tree_host_impl.cc b/cc/trees/layer_tree_host_impl.cc
index 91a7d3d..352f458d 100644
--- a/cc/trees/layer_tree_host_impl.cc
+++ b/cc/trees/layer_tree_host_impl.cc
@@ -5569,7 +5569,7 @@
 }
 
 void LayerTreeHostImpl::ElementIsAnimatingChanged(
-    ElementId element_id,
+    const PropertyToElementIdMap& element_id_map,
     ElementListType list_type,
     const PropertyAnimationState& mask,
     const PropertyAnimationState& state) {
@@ -5577,8 +5577,9 @@
       list_type == ElementListType::ACTIVE ? active_tree() : pending_tree();
   // TODO(wkorman): Explore enabling DCHECK in ElementIsAnimatingChanged()
   // below. Currently enabling causes batch of unit test failures.
-  if (tree && tree->property_trees()->ElementIsAnimatingChanged(
-                  mutator_host(), element_id, list_type, mask, state, false))
+  if (tree &&
+      tree->property_trees()->ElementIsAnimatingChanged(
+          mutator_host(), element_id_map, list_type, mask, state, false))
     tree->set_needs_update_draw_properties();
 }
 
diff --git a/cc/trees/layer_tree_host_impl.h b/cc/trees/layer_tree_host_impl.h
index b8b2af7..77f8763 100644
--- a/cc/trees/layer_tree_host_impl.h
+++ b/cc/trees/layer_tree_host_impl.h
@@ -354,7 +354,7 @@
       ElementId element_id,
       ElementListType list_type,
       const gfx::ScrollOffset& scroll_offset) override;
-  void ElementIsAnimatingChanged(ElementId element_id,
+  void ElementIsAnimatingChanged(const PropertyToElementIdMap& element_id_map,
                                  ElementListType list_type,
                                  const PropertyAnimationState& mask,
                                  const PropertyAnimationState& state) override;
diff --git a/cc/trees/mutator_host_client.h b/cc/trees/mutator_host_client.h
index d3a3e0e..f070e67 100644
--- a/cc/trees/mutator_host_client.h
+++ b/cc/trees/mutator_host_client.h
@@ -44,7 +44,7 @@
 
   // Allows to change IsAnimating value for a set of properties.
   virtual void ElementIsAnimatingChanged(
-      ElementId element_id,
+      const PropertyToElementIdMap& element_id_map,
       ElementListType list_type,
       const PropertyAnimationState& mask,
       const PropertyAnimationState& state) = 0;
diff --git a/cc/trees/property_tree.cc b/cc/trees/property_tree.cc
index 1e74c89..2b78d1a2 100644
--- a/cc/trees/property_tree.cc
+++ b/cc/trees/property_tree.cc
@@ -1837,7 +1837,7 @@
 
 bool PropertyTrees::ElementIsAnimatingChanged(
     const MutatorHost* mutator_host,
-    ElementId element_id,
+    const PropertyToElementIdMap& element_id_map,
     ElementListType list_type,
     const PropertyAnimationState& mask,
     const PropertyAnimationState& state,
@@ -1849,6 +1849,19 @@
         !mask.potentially_animating[property])
       continue;
 
+    // The mask represents which properties have had their state changed. This
+    // can include properties for which there are no longer any animations, in
+    // which case there will not be an entry in the map.
+    //
+    // It is unclear whether this is desirable; it may be that we are missing
+    // updates to property nodes here because we no longer have the required
+    // ElementId to look them up. See http://crbug.com/912574 for context around
+    // why this code was rewritten.
+    auto it = element_id_map.find(static_cast<TargetProperty::Type>(property));
+    if (it == element_id_map.end())
+      continue;
+
+    const ElementId element_id = it->second;
     switch (property) {
       case TargetProperty::TRANSFORM:
         if (TransformNode* transform_node =
diff --git a/cc/trees/property_tree.h b/cc/trees/property_tree.h
index dcf8cb9..27281e0 100644
--- a/cc/trees/property_tree.h
+++ b/cc/trees/property_tree.h
@@ -657,7 +657,7 @@
   // this property tree. Returns whether a draw property update is
   // needed.
   bool ElementIsAnimatingChanged(const MutatorHost* mutator_host,
-                                 ElementId element_id,
+                                 const PropertyToElementIdMap& element_id_map,
                                  ElementListType list_type,
                                  const PropertyAnimationState& mask,
                                  const PropertyAnimationState& state,
diff --git a/cc/trees/target_property.h b/cc/trees/target_property.h
index 93fa839..f28649c 100644
--- a/cc/trees/target_property.h
+++ b/cc/trees/target_property.h
@@ -7,6 +7,8 @@
 
 #include <bitset>
 
+#include "base/containers/flat_map.h"
+
 namespace cc {
 
 static constexpr size_t kMaxTargetPropertyIndex = 32u;
@@ -32,6 +34,13 @@
 // A set of target properties.
 using TargetProperties = std::bitset<kMaxTargetPropertyIndex>;
 
+// A map of target property to ElementId.
+// flat_map was chosen because there are expected to be relatively few entries
+// in the map. For low number of entries, flat_map is known to perform better
+// than other map implementations.
+struct ElementId;
+using PropertyToElementIdMap = base::flat_map<TargetProperty::Type, ElementId>;
+
 }  // namespace cc
 
 #endif  // CC_TREES_TARGET_PROPERTY_H_
diff --git a/third_party/blink/web_tests/TestExpectations b/third_party/blink/web_tests/TestExpectations
index 6ac2cca..f4e38a4 100644
--- a/third_party/blink/web_tests/TestExpectations
+++ b/third_party/blink/web_tests/TestExpectations
@@ -393,6 +393,8 @@
 crbug.com/836886 virtual/prefer_compositing_to_lcd_text/compositing/overflow/scaled-overflow.html [ Failure ]
 crbug.com/836886 compositing/overflow/scaled-overflow.html [ Failure ]
 crbug.com/836886 compositing/scrollbars/nested-overlay-scrollbars.html [ Failure ]
+# Flaky subpixel AA difference (not necessarily incorrect, but flaky)
+crbug.com/921105 virtual/threaded/animations/skew-notsequential-compositor.html [ Failure Pass ]
 
 # ====== Paint team owned tests to here ======
 
@@ -4237,8 +4239,6 @@
 
 crbug.com/912362 external/wpt/web-animations/timing-model/timelines/timelines.html [ Failure ]
 
-crbug.com/912574 virtual/threaded/animations/responsive/transform-responsive-neutral-keyframe.html [ Crash ]
-
 # Decoding test timeout on Win7. Marked flaky to get imported in case it's flaky timeout everywhere.
 crbug.com/862938 external/wpt/encoding/textdecoder-fatal-single-byte.any.worker.html [ Pass Timeout ]