Просмотр исходного кода

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.
Andreas Kling 1 год назад
Родитель
Сommit
2e0297d703

+ 1 - 0
Tests/LibWeb/Text/expected/SVG/gradient-with-reference-cycle.txt

@@ -0,0 +1 @@
+       PASS (didn't hang or crash)

+ 13 - 0
Tests/LibWeb/Text/input/SVG/gradient-with-reference-cycle.html

@@ -0,0 +1,13 @@
+<!doctype html>
+<script src="../include.js"></script>
+<svg>
+    <linearGradient id="lol" href="#lmao"/>
+    <linearGradient id="lmao" href="#even"/>
+    <linearGradient id="even" href="#lol"/>
+    <rect fill="url(#lol)" />
+</svg>
+<script>
+    test(() => {
+        println("PASS (didn't hang or crash)");
+    });
+</script>

+ 29 - 7
Userland/Libraries/LibWeb/SVG/SVGGradientElement.cpp

@@ -34,29 +34,47 @@ void SVGGradientElement::attribute_changed(FlyString const& name, Optional<Strin
 }
 }
 
 
 GradientUnits SVGGradientElement::gradient_units() const
 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())
     if (m_gradient_units.has_value())
         return *m_gradient_units;
         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;
     return GradientUnits::ObjectBoundingBox;
 }
 }
 
 
 SpreadMethod SVGGradientElement::spread_method() const
 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())
     if (m_spread_method.has_value())
         return *m_spread_method;
         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;
     return SpreadMethod::Pad;
 }
 }
 
 
 Optional<Gfx::AffineTransform> SVGGradientElement::gradient_transform() const
 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())
     if (m_gradient_transform.has_value())
         return m_gradient_transform;
         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 {};
     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!
     // FIXME: This entire function is an ad-hoc hack!
     // It can only resolve #<ids> in the same document.
     // 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());
         auto element = document().get_element_by_id(id.value());
         if (!element)
         if (!element)
             return {};
             return {};
+        if (element == this)
+            return {};
         if (!is<SVGGradientElement>(*element))
         if (!is<SVGGradientElement>(*element))
             return {};
             return {};
+        if (seen_gradients.set(&verify_cast<SVGGradientElement>(*element)) != AK::HashSetResult::InsertedNewEntry)
+            return {};
         return &verify_cast<SVGGradientElement>(*element);
         return &verify_cast<SVGGradientElement>(*element);
     }
     }
     return {};
     return {};

+ 16 - 5
Userland/Libraries/LibWeb/SVG/SVGGradientElement.h

@@ -55,12 +55,22 @@ protected:
 
 
     virtual void initialize(JS::Realm&) override;
     virtual void initialize(JS::Realm&) override;
 
 
-    JS::GCPtr<SVGGradientElement const> linked_gradient() const;
+    JS::GCPtr<SVGGradientElement const> linked_gradient(HashTable<SVGGradientElement const*>& seen_gradients) const;
 
 
     Gfx::AffineTransform gradient_paint_transform(SVGPaintContext const&) const;
     Gfx::AffineTransform gradient_paint_transform(SVGPaintContext const&) const;
 
 
     template<VoidFunction<SVGStopElement> Callback>
     template<VoidFunction<SVGStopElement> Callback>
     void for_each_color_stop(Callback const& callback) const
     void for_each_color_stop(Callback const& callback) const
+    {
+        HashTable<SVGGradientElement const*> seen_gradients;
+        return for_each_color_stop_impl(callback, seen_gradients);
+    }
+
+    void add_color_stops(Gfx::SVGGradientPaintStyle&) const;
+
+private:
+    template<VoidFunction<SVGStopElement> Callback>
+    void for_each_color_stop_impl(Callback const& callback, HashTable<SVGGradientElement const*>& seen_gradients) const
     {
     {
         bool color_stops_found = false;
         bool color_stops_found = false;
         for_each_child_of_type<SVG::SVGStopElement>([&](auto& stop) {
         for_each_child_of_type<SVG::SVGStopElement>([&](auto& stop) {
@@ -68,14 +78,15 @@ protected:
             callback(stop);
             callback(stop);
         });
         });
         if (!color_stops_found) {
         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<SVGGradientElement const*>& seen_gradients) const;
+    SpreadMethod spread_method_impl(HashTable<SVGGradientElement const*>& seen_gradients) const;
+    Optional<Gfx::AffineTransform> gradient_transform_impl(HashTable<SVGGradientElement const*>& seen_gradients) const;
 
 
-private:
     Optional<GradientUnits> m_gradient_units = {};
     Optional<GradientUnits> m_gradient_units = {};
     Optional<SpreadMethod> m_spread_method = {};
     Optional<SpreadMethod> m_spread_method = {};
     Optional<Gfx::AffineTransform> m_gradient_transform = {};
     Optional<Gfx::AffineTransform> m_gradient_transform = {};

+ 32 - 8
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
 // https://www.w3.org/TR/SVG11/pservers.html#LinearGradientElementX1Attribute
 NumberPercentage SVGLinearGradientElement::start_x() const
 NumberPercentage SVGLinearGradientElement::start_x() const
+{
+    HashTable<SVGGradientElement const*> seen_gradients;
+    return start_x_impl(seen_gradients);
+}
+
+NumberPercentage SVGLinearGradientElement::start_x_impl(HashTable<SVGGradientElement const*>& seen_gradients) const
 {
 {
     if (m_x1.has_value())
     if (m_x1.has_value())
         return *m_x1;
         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.
     // If the attribute is not specified, the effect is as if a value of '0%' were specified.
     return NumberPercentage::create_percentage(0);
     return NumberPercentage::create_percentage(0);
 }
 }
 
 
 // https://www.w3.org/TR/SVG11/pservers.html#LinearGradientElementY1Attribute
 // https://www.w3.org/TR/SVG11/pservers.html#LinearGradientElementY1Attribute
 NumberPercentage SVGLinearGradientElement::start_y() const
 NumberPercentage SVGLinearGradientElement::start_y() const
+{
+    HashTable<SVGGradientElement const*> seen_gradients;
+    return start_y_impl(seen_gradients);
+}
+
+NumberPercentage SVGLinearGradientElement::start_y_impl(HashTable<SVGGradientElement const*>& seen_gradients) const
 {
 {
     if (m_y1.has_value())
     if (m_y1.has_value())
         return *m_y1;
         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.
     // If the attribute is not specified, the effect is as if a value of '0%' were specified.
     return NumberPercentage::create_percentage(0);
     return NumberPercentage::create_percentage(0);
 }
 }
 
 
 // https://www.w3.org/TR/SVG11/pservers.html#LinearGradientElementX2Attribute
 // https://www.w3.org/TR/SVG11/pservers.html#LinearGradientElementX2Attribute
 NumberPercentage SVGLinearGradientElement::end_x() const
 NumberPercentage SVGLinearGradientElement::end_x() const
+{
+    HashTable<SVGGradientElement const*> seen_gradients;
+    return end_x_impl(seen_gradients);
+}
+
+NumberPercentage SVGLinearGradientElement::end_x_impl(HashTable<SVGGradientElement const*>& seen_gradients) const
 {
 {
     if (m_x2.has_value())
     if (m_x2.has_value())
         return *m_x2;
         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.
     // If the attribute is not specified, the effect is as if a value of '100%' were specified.
     return NumberPercentage::create_percentage(100);
     return NumberPercentage::create_percentage(100);
 }
 }
 
 
 // https://www.w3.org/TR/SVG11/pservers.html#LinearGradientElementY2Attribute
 // https://www.w3.org/TR/SVG11/pservers.html#LinearGradientElementY2Attribute
 NumberPercentage SVGLinearGradientElement::end_y() const
 NumberPercentage SVGLinearGradientElement::end_y() const
+{
+    HashTable<SVGGradientElement const*> seen_gradients;
+    return end_y_impl(seen_gradients);
+}
+
+NumberPercentage SVGLinearGradientElement::end_y_impl(HashTable<SVGGradientElement const*>& seen_gradients) const
 {
 {
     if (m_y2.has_value())
     if (m_y2.has_value())
         return *m_y2;
         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.
     // If the attribute is not specified, the effect is as if a value of '0%' were specified.
     return NumberPercentage::create_percentage(0);
     return NumberPercentage::create_percentage(0);
 }
 }

+ 7 - 2
Userland/Libraries/LibWeb/SVG/SVGLinearGradientElement.h

@@ -34,9 +34,9 @@ protected:
     virtual void initialize(JS::Realm&) override;
     virtual void initialize(JS::Realm&) override;
 
 
 private:
 private:
-    JS::GCPtr<SVGLinearGradientElement const> linked_linear_gradient() const
+    JS::GCPtr<SVGLinearGradientElement const> linked_linear_gradient(HashTable<SVGGradientElement const*>& seen_gradients) const
     {
     {
-        if (auto gradient = linked_gradient(); gradient && is<SVGLinearGradientElement>(*gradient))
+        if (auto gradient = linked_gradient(seen_gradients); gradient && is<SVGLinearGradientElement>(*gradient))
             return &verify_cast<SVGLinearGradientElement>(*gradient);
             return &verify_cast<SVGLinearGradientElement>(*gradient);
         return {};
         return {};
     }
     }
@@ -46,6 +46,11 @@ private:
     NumberPercentage end_x() const;
     NumberPercentage end_x() const;
     NumberPercentage end_y() const;
     NumberPercentage end_y() const;
 
 
+    NumberPercentage start_x_impl(HashTable<SVGGradientElement const*>& seen_gradients) const;
+    NumberPercentage start_y_impl(HashTable<SVGGradientElement const*>& seen_gradients) const;
+    NumberPercentage end_x_impl(HashTable<SVGGradientElement const*>& seen_gradients) const;
+    NumberPercentage end_y_impl(HashTable<SVGGradientElement const*>& seen_gradients) const;
+
     Optional<NumberPercentage> m_x1;
     Optional<NumberPercentage> m_x1;
     Optional<NumberPercentage> m_y1;
     Optional<NumberPercentage> m_y1;
     Optional<NumberPercentage> m_x2;
     Optional<NumberPercentage> m_x2;

+ 48 - 12
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
 // https://svgwg.org/svg2-draft/pservers.html#RadialGradientElementFXAttribute
 NumberPercentage SVGRadialGradientElement::start_circle_x() const
 NumberPercentage SVGRadialGradientElement::start_circle_x() const
+{
+    HashTable<SVGGradientElement const*> seen_gradients;
+    return start_circle_x_impl(seen_gradients);
+}
+
+NumberPercentage SVGRadialGradientElement::start_circle_x_impl(HashTable<SVGGradientElement const*>& seen_gradients) const
 {
 {
     if (m_fx.has_value())
     if (m_fx.has_value())
         return *m_fx;
         return *m_fx;
     // If the element references an element that specifies a value for 'fx', then the value of 'fx' is
     // If the element references an element that specifies a value for 'fx', then the value of 'fx' is
     // inherited from the referenced element.
     // 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
     // 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.
     // the element whether the value for 'cx' was inherited or not.
     return end_circle_x();
     return end_circle_x();
@@ -66,13 +72,19 @@ NumberPercentage SVGRadialGradientElement::start_circle_x() const
 
 
 // https://svgwg.org/svg2-draft/pservers.html#RadialGradientElementFYAttribute
 // https://svgwg.org/svg2-draft/pservers.html#RadialGradientElementFYAttribute
 NumberPercentage SVGRadialGradientElement::start_circle_y() const
 NumberPercentage SVGRadialGradientElement::start_circle_y() const
+{
+    HashTable<SVGGradientElement const*> seen_gradients;
+    return start_circle_y_impl(seen_gradients);
+}
+
+NumberPercentage SVGRadialGradientElement::start_circle_y_impl(HashTable<SVGGradientElement const*>& seen_gradients) const
 {
 {
     if (m_fy.has_value())
     if (m_fy.has_value())
         return *m_fy;
         return *m_fy;
     // If the element references an element that specifies a value for 'fy', then the value of 'fy' is
     // If the element references an element that specifies a value for 'fy', then the value of 'fy' is
     // inherited from the referenced element.
     // 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
     // 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.
     // the element whether the value for 'cy' was inherited or not.
     return end_circle_y();
     return end_circle_y();
@@ -80,46 +92,70 @@ NumberPercentage SVGRadialGradientElement::start_circle_y() const
 
 
 // https://svgwg.org/svg2-draft/pservers.html#RadialGradientElementFRAttribute
 // https://svgwg.org/svg2-draft/pservers.html#RadialGradientElementFRAttribute
 NumberPercentage SVGRadialGradientElement::start_circle_radius() const
 NumberPercentage SVGRadialGradientElement::start_circle_radius() const
+{
+    HashTable<SVGGradientElement const*> seen_gradients;
+    return start_circle_radius_impl(seen_gradients);
+}
+
+NumberPercentage SVGRadialGradientElement::start_circle_radius_impl(HashTable<SVGGradientElement const*>& seen_gradients) const
 {
 {
     // Note: A negative value is an error.
     // Note: A negative value is an error.
     if (m_fr.has_value() && m_fr->value() >= 0)
     if (m_fr.has_value() && m_fr->value() >= 0)
         return *m_fr;
         return *m_fr;
     // if the element references an element that specifies a value for 'fr', then the value of
     // if the element references an element that specifies a value for 'fr', then the value of
     // 'fr' is inherited from the referenced element.
     // '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.
     // If the attribute is not specified, the effect is as if a value of '0%' were specified.
     return NumberPercentage::create_percentage(0);
     return NumberPercentage::create_percentage(0);
 }
 }
 
 
 // https://svgwg.org/svg2-draft/pservers.html#RadialGradientElementCXAttribute
 // https://svgwg.org/svg2-draft/pservers.html#RadialGradientElementCXAttribute
 NumberPercentage SVGRadialGradientElement::end_circle_x() const
 NumberPercentage SVGRadialGradientElement::end_circle_x() const
+{
+    HashTable<SVGGradientElement const*> seen_gradients;
+    return end_circle_x_impl(seen_gradients);
+}
+
+NumberPercentage SVGRadialGradientElement::end_circle_x_impl(HashTable<SVGGradientElement const*>& seen_gradients) const
 {
 {
     if (m_cx.has_value())
     if (m_cx.has_value())
         return *m_cx;
         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);
     return NumberPercentage::create_percentage(50);
 }
 }
 
 
 // https://svgwg.org/svg2-draft/pservers.html#RadialGradientElementCYAttribute
 // https://svgwg.org/svg2-draft/pservers.html#RadialGradientElementCYAttribute
 NumberPercentage SVGRadialGradientElement::end_circle_y() const
 NumberPercentage SVGRadialGradientElement::end_circle_y() const
+{
+    HashTable<SVGGradientElement const*> seen_gradients;
+    return end_circle_y_impl(seen_gradients);
+}
+
+NumberPercentage SVGRadialGradientElement::end_circle_y_impl(HashTable<SVGGradientElement const*>& seen_gradients) const
 {
 {
     if (m_cy.has_value())
     if (m_cy.has_value())
         return *m_cy;
         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);
     return NumberPercentage::create_percentage(50);
 }
 }
 
 
 // https://svgwg.org/svg2-draft/pservers.html#RadialGradientElementRAttribute
 // https://svgwg.org/svg2-draft/pservers.html#RadialGradientElementRAttribute
 NumberPercentage SVGRadialGradientElement::end_circle_radius() const
 NumberPercentage SVGRadialGradientElement::end_circle_radius() const
+{
+    HashTable<SVGGradientElement const*> seen_gradients;
+    return end_circle_radius_impl(seen_gradients);
+}
+
+NumberPercentage SVGRadialGradientElement::end_circle_radius_impl(HashTable<SVGGradientElement const*>& seen_gradients) const
 {
 {
     // Note: A negative value is an error.
     // Note: A negative value is an error.
     if (m_r.has_value() && m_r->value() >= 0)
     if (m_r.has_value() && m_r->value() >= 0)
         return *m_r;
         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);
     return NumberPercentage::create_percentage(50);
 }
 }
 
 

+ 9 - 2
Userland/Libraries/LibWeb/SVG/SVGRadialGradientElement.h

@@ -36,9 +36,9 @@ protected:
     virtual void initialize(JS::Realm&) override;
     virtual void initialize(JS::Realm&) override;
 
 
 private:
 private:
-    JS::GCPtr<SVGRadialGradientElement const> linked_radial_gradient() const
+    JS::GCPtr<SVGRadialGradientElement const> linked_radial_gradient(HashTable<SVGGradientElement const*>& seen_gradients) const
     {
     {
-        if (auto gradient = linked_gradient(); gradient && is<SVGRadialGradientElement>(*gradient))
+        if (auto gradient = linked_gradient(seen_gradients); gradient && is<SVGRadialGradientElement>(*gradient))
             return &verify_cast<SVGRadialGradientElement>(*gradient);
             return &verify_cast<SVGRadialGradientElement>(*gradient);
         return {};
         return {};
     }
     }
@@ -50,6 +50,13 @@ private:
     NumberPercentage end_circle_y() const;
     NumberPercentage end_circle_y() const;
     NumberPercentage end_circle_radius() const;
     NumberPercentage end_circle_radius() const;
 
 
+    NumberPercentage start_circle_x_impl(HashTable<SVGGradientElement const*>& seen_gradients) const;
+    NumberPercentage start_circle_y_impl(HashTable<SVGGradientElement const*>& seen_gradients) const;
+    NumberPercentage start_circle_radius_impl(HashTable<SVGGradientElement const*>& seen_gradients) const;
+    NumberPercentage end_circle_x_impl(HashTable<SVGGradientElement const*>& seen_gradients) const;
+    NumberPercentage end_circle_y_impl(HashTable<SVGGradientElement const*>& seen_gradients) const;
+    NumberPercentage end_circle_radius_impl(HashTable<SVGGradientElement const*>& seen_gradients) const;
+
     Optional<NumberPercentage> m_cx;
     Optional<NumberPercentage> m_cx;
     Optional<NumberPercentage> m_cy;
     Optional<NumberPercentage> m_cy;
     Optional<NumberPercentage> m_fx;
     Optional<NumberPercentage> m_fx;