Have the cycle solver traverse <feImage> element references
Allows breaking cycles caused by feImage references earlier (before
reaching paint). This should allow getting rid of the cycle-breaking
machinery in FilterState in the future.
Bug: 1028063
Change-Id: I038dcb35870973d61648248fdda8260b362419af
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2120498
Reviewed-by: Philip Rogers <pdr@chromium.org>
Reviewed-by: Stephen Chenney <schenney@chromium.org>
Commit-Queue: Fredrik Söderquist <fs@opera.com>
Cr-Commit-Position: refs/heads/master@{#763742}
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.cc
index da7fd93..de5ed70 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.cc
@@ -97,7 +97,7 @@
 
 bool LayoutSVGResourceContainer::FindCycleInResources(
     SVGResourcesCycleSolver& solver,
-    const LayoutObject& layout_object) const {
+    const LayoutObject& layout_object) {
   SVGResources* resources =
       SVGResourcesCache::CachedResourcesForLayoutObject(layout_object);
   if (!resources)
@@ -116,28 +116,35 @@
 
 bool LayoutSVGResourceContainer::FindCycleFromSelf(
     SVGResourcesCycleSolver& solver) const {
-  if (FindCycleInResources(solver, *this))
-    return true;
-  return FindCycleInDescendants(solver);
+  return FindCycleInSubtree(solver, *this);
 }
 
 bool LayoutSVGResourceContainer::FindCycleInDescendants(
-    SVGResourcesCycleSolver& solver) const {
-  LayoutObject* node = FirstChild();
+    SVGResourcesCycleSolver& solver,
+    const LayoutObject& root) {
+  LayoutObject* node = root.SlowFirstChild();
   while (node) {
     // Skip subtrees which are themselves resources. (They will be
     // processed - if needed - when they are actually referenced.)
     if (node->IsSVGResourceContainer()) {
-      node = node->NextInPreOrderAfterChildren(this);
+      node = node->NextInPreOrderAfterChildren(&root);
       continue;
     }
     if (FindCycleInResources(solver, *node))
       return true;
-    node = node->NextInPreOrder(this);
+    node = node->NextInPreOrder(&root);
   }
   return false;
 }
 
+bool LayoutSVGResourceContainer::FindCycleInSubtree(
+    SVGResourcesCycleSolver& solver,
+    const LayoutObject& root) {
+  if (FindCycleInResources(solver, root))
+    return true;
+  return FindCycleInDescendants(solver, root);
+}
+
 void LayoutSVGResourceContainer::MarkAllClientsForInvalidation(
     InvalidationModeMask invalidation_mask) {
   if (is_invalidating_)
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.h b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.h
index 9e5ef61..6874d6e 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.h
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_container.h
@@ -81,10 +81,13 @@
   // Used from RemoveAllClientsFromCache methods.
   void MarkAllClientsForInvalidation(InvalidationModeMask);
 
-  bool FindCycleFromSelf(SVGResourcesCycleSolver&) const;
-  bool FindCycleInDescendants(SVGResourcesCycleSolver&) const;
-  bool FindCycleInResources(SVGResourcesCycleSolver&,
-                            const LayoutObject&) const;
+  virtual bool FindCycleFromSelf(SVGResourcesCycleSolver&) const;
+  static bool FindCycleInDescendants(SVGResourcesCycleSolver&,
+                                     const LayoutObject& root);
+  static bool FindCycleInResources(SVGResourcesCycleSolver&,
+                                   const LayoutObject& object);
+  static bool FindCycleInSubtree(SVGResourcesCycleSolver&,
+                                 const LayoutObject& root);
 
   void StyleDidChange(StyleDifference, const ComputedStyle* old_style) override;
   void WillBeDestroyed() override;
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_filter.cc b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_filter.cc
index 09784e4..f275e56 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_filter.cc
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_filter.cc
@@ -23,6 +23,8 @@
 
 #include "third_party/blink/renderer/core/layout/svg/layout_svg_resource_filter.h"
 
+#include "third_party/blink/renderer/core/dom/element_traversal.h"
+#include "third_party/blink/renderer/core/svg/svg_fe_image_element.h"
 #include "third_party/blink/renderer/core/svg/svg_filter_element.h"
 
 namespace blink {
@@ -63,6 +65,23 @@
       ->EnumValue();
 }
 
+bool LayoutSVGResourceFilter::FindCycleFromSelf(
+    SVGResourcesCycleSolver& solver) const {
+  // Traverse and check all <feImage> 'href' element references.
+  for (auto& feimage_element :
+       Traversal<SVGFEImageElement>::ChildrenOf(*GetElement())) {
+    const SVGElement* target = feimage_element.TargetElement();
+    if (!target)
+      continue;
+    const LayoutObject* target_layout_object = target->GetLayoutObject();
+    if (!target_layout_object)
+      continue;
+    if (FindCycleInSubtree(solver, *target_layout_object))
+      return true;
+  }
+  return false;
+}
+
 LayoutSVGResourceFilter* GetFilterResourceForSVG(const ComputedStyle& style) {
   if (!style.HasFilter())
     return nullptr;
diff --git a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_filter.h b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_filter.h
index e1584b2..467c193 100644
--- a/third_party/blink/renderer/core/layout/svg/layout_svg_resource_filter.h
+++ b/third_party/blink/renderer/core/layout/svg/layout_svg_resource_filter.h
@@ -49,6 +49,9 @@
 
   static const LayoutSVGResourceType kResourceType = kFilterResourceType;
   LayoutSVGResourceType ResourceType() const override { return kResourceType; }
+
+ private:
+  bool FindCycleFromSelf(SVGResourcesCycleSolver&) const override;
 };
 
 // Get the LayoutSVGResourceFilter from the 'filter' property iff the 'filter'
diff --git a/third_party/blink/renderer/core/svg/svg_fe_image_element.cc b/third_party/blink/renderer/core/svg/svg_fe_image_element.cc
index e56a87a..daa126e 100644
--- a/third_party/blink/renderer/core/svg/svg_fe_image_element.cc
+++ b/third_party/blink/renderer/core/svg/svg_fe_image_element.cc
@@ -150,6 +150,13 @@
     MarkForLayoutAndParentResourceInvalidation(*layout_object);
 }
 
+const SVGElement* SVGFEImageElement::TargetElement() const {
+  if (cached_image_)
+    return nullptr;
+  return DynamicTo<SVGElement>(
+      TargetElementFromIRIString(HrefString(), GetTreeScope()));
+}
+
 FilterEffect* SVGFEImageElement::Build(SVGFilterBuilder*, Filter* filter) {
   if (cached_image_) {
     // Don't use the broken image icon on image loading errors.
@@ -158,9 +165,7 @@
     return MakeGarbageCollected<FEImage>(
         filter, image, preserve_aspect_ratio_->CurrentValue());
   }
-  const SVGElement* target = DynamicTo<SVGElement>(
-      TargetElementFromIRIString(HrefString(), GetTreeScope()));
-  return MakeGarbageCollected<FEImage>(filter, target,
+  return MakeGarbageCollected<FEImage>(filter, TargetElement(),
                                        preserve_aspect_ratio_->CurrentValue());
 }
 
diff --git a/third_party/blink/renderer/core/svg/svg_fe_image_element.h b/third_party/blink/renderer/core/svg/svg_fe_image_element.h
index ce245c2c..2366d7b 100644
--- a/third_party/blink/renderer/core/svg/svg_fe_image_element.h
+++ b/third_party/blink/renderer/core/svg/svg_fe_image_element.h
@@ -48,6 +48,8 @@
     return preserve_aspect_ratio_.Get();
   }
 
+  const SVGElement* TargetElement() const;
+
   void Dispose();
 
   void Trace(Visitor*) override;