diff --git a/Tests/LibWeb/Text/expected/SVG/gradient-with-reference-cycle.txt b/Tests/LibWeb/Text/expected/SVG/gradient-with-reference-cycle.txt
new file mode 100644
index 00000000000..b7a061f8539
--- /dev/null
+++ b/Tests/LibWeb/Text/expected/SVG/gradient-with-reference-cycle.txt
@@ -0,0 +1 @@
+ PASS (didn't hang or crash)
diff --git a/Tests/LibWeb/Text/input/SVG/gradient-with-reference-cycle.html b/Tests/LibWeb/Text/input/SVG/gradient-with-reference-cycle.html
new file mode 100644
index 00000000000..d9f61d2ef6e
--- /dev/null
+++ b/Tests/LibWeb/Text/input/SVG/gradient-with-reference-cycle.html
@@ -0,0 +1,13 @@
+
+
+
+
diff --git a/Userland/Libraries/LibWeb/SVG/SVGGradientElement.cpp b/Userland/Libraries/LibWeb/SVG/SVGGradientElement.cpp
index 71bb28ed1b3..c1a724d5e63 100644
--- a/Userland/Libraries/LibWeb/SVG/SVGGradientElement.cpp
+++ b/Userland/Libraries/LibWeb/SVG/SVGGradientElement.cpp
@@ -34,29 +34,47 @@ void SVGGradientElement::attribute_changed(FlyString const& name, Optional seen_gradients;
+ return gradient_units_impl(seen_gradients);
+}
+
+GradientUnits SVGGradientElement::gradient_units_impl(HashTable& 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 seen_gradients;
+ return spread_method_impl(seen_gradients);
+}
+
+SpreadMethod SVGGradientElement::spread_method_impl(HashTable& 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 SVGGradientElement::gradient_transform() const
+{
+ HashTable seen_gradients;
+ return gradient_transform_impl(seen_gradients);
+}
+
+Optional SVGGradientElement::gradient_transform_impl(HashTable& 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::linked_gradient() const
+JS::GCPtr SVGGradientElement::linked_gradient(HashTable& seen_gradients) const
{
// FIXME: This entire function is an ad-hoc hack!
// It can only resolve # in the same document.
@@ -103,8 +121,12 @@ JS::GCPtr SVGGradientElement::linked_gradient() const
auto element = document().get_element_by_id(id.value());
if (!element)
return {};
+ if (element == this)
+ return {};
if (!is(*element))
return {};
+ if (seen_gradients.set(&verify_cast(*element)) != AK::HashSetResult::InsertedNewEntry)
+ return {};
return &verify_cast(*element);
}
return {};
diff --git a/Userland/Libraries/LibWeb/SVG/SVGGradientElement.h b/Userland/Libraries/LibWeb/SVG/SVGGradientElement.h
index ae9ac23e37c..096d9b6705b 100644
--- a/Userland/Libraries/LibWeb/SVG/SVGGradientElement.h
+++ b/Userland/Libraries/LibWeb/SVG/SVGGradientElement.h
@@ -55,12 +55,22 @@ protected:
virtual void initialize(JS::Realm&) override;
- JS::GCPtr linked_gradient() const;
+ JS::GCPtr linked_gradient(HashTable& seen_gradients) const;
Gfx::AffineTransform gradient_paint_transform(SVGPaintContext const&) const;
template Callback>
void for_each_color_stop(Callback const& callback) const
+ {
+ HashTable seen_gradients;
+ return for_each_color_stop_impl(callback, seen_gradients);
+ }
+
+ void add_color_stops(Gfx::SVGGradientPaintStyle&) const;
+
+private:
+ template Callback>
+ void for_each_color_stop_impl(Callback const& callback, HashTable& seen_gradients) const
{
bool color_stops_found = false;
for_each_child_of_type([&](auto& stop) {
@@ -68,14 +78,15 @@ protected:
callback(stop);
});
if (!color_stops_found) {
- if (auto gradient = linked_gradient())
- gradient->for_each_color_stop(callback);
+ if (auto gradient = linked_gradient(seen_gradients))
+ gradient->for_each_color_stop_impl(callback, seen_gradients);
}
}
- void add_color_stops(Gfx::SVGGradientPaintStyle&) const;
+ GradientUnits gradient_units_impl(HashTable& seen_gradients) const;
+ SpreadMethod spread_method_impl(HashTable& seen_gradients) const;
+ Optional gradient_transform_impl(HashTable& seen_gradients) const;
-private:
Optional m_gradient_units = {};
Optional m_spread_method = {};
Optional m_gradient_transform = {};
diff --git a/Userland/Libraries/LibWeb/SVG/SVGLinearGradientElement.cpp b/Userland/Libraries/LibWeb/SVG/SVGLinearGradientElement.cpp
index aa610176a2e..84d5a11e779 100644
--- a/Userland/Libraries/LibWeb/SVG/SVGLinearGradientElement.cpp
+++ b/Userland/Libraries/LibWeb/SVG/SVGLinearGradientElement.cpp
@@ -48,44 +48,68 @@ void SVGLinearGradientElement::attribute_changed(FlyString const& name, Optional
// https://www.w3.org/TR/SVG11/pservers.html#LinearGradientElementX1Attribute
NumberPercentage SVGLinearGradientElement::start_x() const
+{
+ HashTable seen_gradients;
+ return start_x_impl(seen_gradients);
+}
+
+NumberPercentage SVGLinearGradientElement::start_x_impl(HashTable& seen_gradients) const
{
if (m_x1.has_value())
return *m_x1;
- if (auto gradient = linked_linear_gradient())
- return gradient->start_x();
+ if (auto gradient = linked_linear_gradient(seen_gradients))
+ return gradient->start_x_impl(seen_gradients);
// If the attribute is not specified, the effect is as if a value of '0%' were specified.
return NumberPercentage::create_percentage(0);
}
// https://www.w3.org/TR/SVG11/pservers.html#LinearGradientElementY1Attribute
NumberPercentage SVGLinearGradientElement::start_y() const
+{
+ HashTable seen_gradients;
+ return start_y_impl(seen_gradients);
+}
+
+NumberPercentage SVGLinearGradientElement::start_y_impl(HashTable& seen_gradients) const
{
if (m_y1.has_value())
return *m_y1;
- if (auto gradient = linked_linear_gradient())
- return gradient->start_x();
+ if (auto gradient = linked_linear_gradient(seen_gradients))
+ return gradient->start_y_impl(seen_gradients);
// If the attribute is not specified, the effect is as if a value of '0%' were specified.
return NumberPercentage::create_percentage(0);
}
// https://www.w3.org/TR/SVG11/pservers.html#LinearGradientElementX2Attribute
NumberPercentage SVGLinearGradientElement::end_x() const
+{
+ HashTable seen_gradients;
+ return end_x_impl(seen_gradients);
+}
+
+NumberPercentage SVGLinearGradientElement::end_x_impl(HashTable& seen_gradients) const
{
if (m_x2.has_value())
return *m_x2;
- if (auto gradient = linked_linear_gradient())
- return gradient->start_x();
+ if (auto gradient = linked_linear_gradient(seen_gradients))
+ return gradient->end_x_impl(seen_gradients);
// If the attribute is not specified, the effect is as if a value of '100%' were specified.
return NumberPercentage::create_percentage(100);
}
// https://www.w3.org/TR/SVG11/pservers.html#LinearGradientElementY2Attribute
NumberPercentage SVGLinearGradientElement::end_y() const
+{
+ HashTable seen_gradients;
+ return end_y_impl(seen_gradients);
+}
+
+NumberPercentage SVGLinearGradientElement::end_y_impl(HashTable& seen_gradients) const
{
if (m_y2.has_value())
return *m_y2;
- if (auto gradient = linked_linear_gradient())
- return gradient->start_x();
+ if (auto gradient = linked_linear_gradient(seen_gradients))
+ return gradient->end_y_impl(seen_gradients);
// If the attribute is not specified, the effect is as if a value of '0%' were specified.
return NumberPercentage::create_percentage(0);
}
diff --git a/Userland/Libraries/LibWeb/SVG/SVGLinearGradientElement.h b/Userland/Libraries/LibWeb/SVG/SVGLinearGradientElement.h
index 88840c64b53..4b60c4e50f2 100644
--- a/Userland/Libraries/LibWeb/SVG/SVGLinearGradientElement.h
+++ b/Userland/Libraries/LibWeb/SVG/SVGLinearGradientElement.h
@@ -34,9 +34,9 @@ protected:
virtual void initialize(JS::Realm&) override;
private:
- JS::GCPtr linked_linear_gradient() const
+ JS::GCPtr linked_linear_gradient(HashTable& seen_gradients) const
{
- if (auto gradient = linked_gradient(); gradient && is(*gradient))
+ if (auto gradient = linked_gradient(seen_gradients); gradient && is(*gradient))
return &verify_cast(*gradient);
return {};
}
@@ -46,6 +46,11 @@ private:
NumberPercentage end_x() const;
NumberPercentage end_y() const;
+ NumberPercentage start_x_impl(HashTable& seen_gradients) const;
+ NumberPercentage start_y_impl(HashTable& seen_gradients) const;
+ NumberPercentage end_x_impl(HashTable& seen_gradients) const;
+ NumberPercentage end_y_impl(HashTable& seen_gradients) const;
+
Optional m_x1;
Optional m_y1;
Optional m_x2;
diff --git a/Userland/Libraries/LibWeb/SVG/SVGRadialGradientElement.cpp b/Userland/Libraries/LibWeb/SVG/SVGRadialGradientElement.cpp
index 98d1c4f3e8b..6909559164e 100644
--- a/Userland/Libraries/LibWeb/SVG/SVGRadialGradientElement.cpp
+++ b/Userland/Libraries/LibWeb/SVG/SVGRadialGradientElement.cpp
@@ -52,13 +52,19 @@ void SVGRadialGradientElement::attribute_changed(FlyString const& name, Optional
// https://svgwg.org/svg2-draft/pservers.html#RadialGradientElementFXAttribute
NumberPercentage SVGRadialGradientElement::start_circle_x() const
+{
+ HashTable seen_gradients;
+ return start_circle_x_impl(seen_gradients);
+}
+
+NumberPercentage SVGRadialGradientElement::start_circle_x_impl(HashTable& seen_gradients) const
{
if (m_fx.has_value())
return *m_fx;
// If the element references an element that specifies a value for 'fx', then the value of 'fx' is
// inherited from the referenced element.
- if (auto gradient = linked_radial_gradient())
- return gradient->start_circle_x();
+ if (auto gradient = linked_radial_gradient(seen_gradients))
+ return gradient->start_circle_x_impl(seen_gradients);
// If attribute ‘fx’ is not specified, ‘fx’ will coincide with the presentational value of ‘cx’ for
// the element whether the value for 'cx' was inherited or not.
return end_circle_x();
@@ -66,13 +72,19 @@ NumberPercentage SVGRadialGradientElement::start_circle_x() const
// https://svgwg.org/svg2-draft/pservers.html#RadialGradientElementFYAttribute
NumberPercentage SVGRadialGradientElement::start_circle_y() const
+{
+ HashTable seen_gradients;
+ return start_circle_y_impl(seen_gradients);
+}
+
+NumberPercentage SVGRadialGradientElement::start_circle_y_impl(HashTable& seen_gradients) const
{
if (m_fy.has_value())
return *m_fy;
// If the element references an element that specifies a value for 'fy', then the value of 'fy' is
// inherited from the referenced element.
- if (auto gradient = linked_radial_gradient())
- return gradient->start_circle_y();
+ if (auto gradient = linked_radial_gradient(seen_gradients))
+ return gradient->start_circle_y_impl(seen_gradients);
// If attribute ‘fy’ is not specified, ‘fy’ will coincide with the presentational value of ‘cy’ for
// the element whether the value for 'cy' was inherited or not.
return end_circle_y();
@@ -80,46 +92,70 @@ NumberPercentage SVGRadialGradientElement::start_circle_y() const
// https://svgwg.org/svg2-draft/pservers.html#RadialGradientElementFRAttribute
NumberPercentage SVGRadialGradientElement::start_circle_radius() const
+{
+ HashTable seen_gradients;
+ return start_circle_radius_impl(seen_gradients);
+}
+
+NumberPercentage SVGRadialGradientElement::start_circle_radius_impl(HashTable& seen_gradients) const
{
// Note: A negative value is an error.
if (m_fr.has_value() && m_fr->value() >= 0)
return *m_fr;
// if the element references an element that specifies a value for 'fr', then the value of
// 'fr' is inherited from the referenced element.
- if (auto gradient = linked_radial_gradient())
- return gradient->start_circle_radius();
+ if (auto gradient = linked_radial_gradient(seen_gradients))
+ return gradient->start_circle_radius_impl(seen_gradients);
// If the attribute is not specified, the effect is as if a value of '0%' were specified.
return NumberPercentage::create_percentage(0);
}
// https://svgwg.org/svg2-draft/pservers.html#RadialGradientElementCXAttribute
NumberPercentage SVGRadialGradientElement::end_circle_x() const
+{
+ HashTable seen_gradients;
+ return end_circle_x_impl(seen_gradients);
+}
+
+NumberPercentage SVGRadialGradientElement::end_circle_x_impl(HashTable& seen_gradients) const
{
if (m_cx.has_value())
return *m_cx;
- if (auto gradient = linked_radial_gradient())
- return gradient->end_circle_x();
+ if (auto gradient = linked_radial_gradient(seen_gradients))
+ return gradient->end_circle_x_impl(seen_gradients);
return NumberPercentage::create_percentage(50);
}
// https://svgwg.org/svg2-draft/pservers.html#RadialGradientElementCYAttribute
NumberPercentage SVGRadialGradientElement::end_circle_y() const
+{
+ HashTable seen_gradients;
+ return end_circle_y_impl(seen_gradients);
+}
+
+NumberPercentage SVGRadialGradientElement::end_circle_y_impl(HashTable& seen_gradients) const
{
if (m_cy.has_value())
return *m_cy;
- if (auto gradient = linked_radial_gradient())
- return gradient->end_circle_y();
+ if (auto gradient = linked_radial_gradient(seen_gradients))
+ return gradient->end_circle_y_impl(seen_gradients);
return NumberPercentage::create_percentage(50);
}
// https://svgwg.org/svg2-draft/pservers.html#RadialGradientElementRAttribute
NumberPercentage SVGRadialGradientElement::end_circle_radius() const
+{
+ HashTable seen_gradients;
+ return end_circle_radius_impl(seen_gradients);
+}
+
+NumberPercentage SVGRadialGradientElement::end_circle_radius_impl(HashTable& seen_gradients) const
{
// Note: A negative value is an error.
if (m_r.has_value() && m_r->value() >= 0)
return *m_r;
- if (auto gradient = linked_radial_gradient())
- return gradient->end_circle_radius();
+ if (auto gradient = linked_radial_gradient(seen_gradients))
+ return gradient->end_circle_radius_impl(seen_gradients);
return NumberPercentage::create_percentage(50);
}
diff --git a/Userland/Libraries/LibWeb/SVG/SVGRadialGradientElement.h b/Userland/Libraries/LibWeb/SVG/SVGRadialGradientElement.h
index 31222582ae4..8fa5c62f14e 100644
--- a/Userland/Libraries/LibWeb/SVG/SVGRadialGradientElement.h
+++ b/Userland/Libraries/LibWeb/SVG/SVGRadialGradientElement.h
@@ -36,9 +36,9 @@ protected:
virtual void initialize(JS::Realm&) override;
private:
- JS::GCPtr linked_radial_gradient() const
+ JS::GCPtr linked_radial_gradient(HashTable& seen_gradients) const
{
- if (auto gradient = linked_gradient(); gradient && is(*gradient))
+ if (auto gradient = linked_gradient(seen_gradients); gradient && is(*gradient))
return &verify_cast(*gradient);
return {};
}
@@ -50,6 +50,13 @@ private:
NumberPercentage end_circle_y() const;
NumberPercentage end_circle_radius() const;
+ NumberPercentage start_circle_x_impl(HashTable& seen_gradients) const;
+ NumberPercentage start_circle_y_impl(HashTable& seen_gradients) const;
+ NumberPercentage start_circle_radius_impl(HashTable& seen_gradients) const;
+ NumberPercentage end_circle_x_impl(HashTable& seen_gradients) const;
+ NumberPercentage end_circle_y_impl(HashTable& seen_gradients) const;
+ NumberPercentage end_circle_radius_impl(HashTable& seen_gradients) const;
+
Optional m_cx;
Optional m_cy;
Optional m_fx;