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:
parent
1b8d8c7bbc
commit
2e0297d703
8 changed files with 155 additions and 36 deletions
|
@ -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 {};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue