1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-05-31 10:48:11 +00:00

LibWeb: Handle reference cycles in SVG gradient linking

Since SVG gradients can reference each other, we have to keep track of
visited gradients when traversing the link chain, or we will recurse
infinitely when there's a reference cycle.
This commit is contained in:
Andreas Kling 2024-03-10 12:18:09 +01:00
parent 1b8d8c7bbc
commit 2e0297d703
8 changed files with 155 additions and 36 deletions

View file

@ -34,29 +34,47 @@ void SVGGradientElement::attribute_changed(FlyString const& name, Optional<Strin
}
GradientUnits SVGGradientElement::gradient_units() const
{
HashTable<SVGGradientElement const*> seen_gradients;
return gradient_units_impl(seen_gradients);
}
GradientUnits SVGGradientElement::gradient_units_impl(HashTable<SVGGradientElement const*>& seen_gradients) const
{
if (m_gradient_units.has_value())
return *m_gradient_units;
if (auto gradient = linked_gradient())
return gradient->gradient_units();
if (auto gradient = linked_gradient(seen_gradients))
return gradient->gradient_units_impl(seen_gradients);
return GradientUnits::ObjectBoundingBox;
}
SpreadMethod SVGGradientElement::spread_method() const
{
HashTable<SVGGradientElement const*> seen_gradients;
return spread_method_impl(seen_gradients);
}
SpreadMethod SVGGradientElement::spread_method_impl(HashTable<SVGGradientElement const*>& seen_gradients) const
{
if (m_spread_method.has_value())
return *m_spread_method;
if (auto gradient = linked_gradient())
return gradient->spread_method();
if (auto gradient = linked_gradient(seen_gradients))
return gradient->spread_method_impl(seen_gradients);
return SpreadMethod::Pad;
}
Optional<Gfx::AffineTransform> SVGGradientElement::gradient_transform() const
{
HashTable<SVGGradientElement const*> seen_gradients;
return gradient_transform_impl(seen_gradients);
}
Optional<Gfx::AffineTransform> SVGGradientElement::gradient_transform_impl(HashTable<SVGGradientElement const*>& seen_gradients) const
{
if (m_gradient_transform.has_value())
return m_gradient_transform;
if (auto gradient = linked_gradient())
return gradient->gradient_transform();
if (auto gradient = linked_gradient(seen_gradients))
return gradient->gradient_transform_impl(seen_gradients);
return {};
}
@ -89,7 +107,7 @@ void SVGGradientElement::add_color_stops(Gfx::SVGGradientPaintStyle& paint_style
});
}
JS::GCPtr<SVGGradientElement const> SVGGradientElement::linked_gradient() const
JS::GCPtr<SVGGradientElement const> SVGGradientElement::linked_gradient(HashTable<SVGGradientElement const*>& seen_gradients) const
{
// FIXME: This entire function is an ad-hoc hack!
// It can only resolve #<ids> in the same document.
@ -103,8 +121,12 @@ JS::GCPtr<SVGGradientElement const> SVGGradientElement::linked_gradient() const
auto element = document().get_element_by_id(id.value());
if (!element)
return {};
if (element == this)
return {};
if (!is<SVGGradientElement>(*element))
return {};
if (seen_gradients.set(&verify_cast<SVGGradientElement>(*element)) != AK::HashSetResult::InsertedNewEntry)
return {};
return &verify_cast<SVGGradientElement>(*element);
}
return {};