Coverage Report

Created: 2025-11-16 07:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/Userland/Libraries/LibWeb/SVG/SVGLinearGradientElement.cpp
Line
Count
Source
1
/*
2
 * Copyright (c) 2023, MacDue <macdue@dueutil.tech>
3
 *
4
 * SPDX-License-Identifier: BSD-2-Clause
5
 */
6
7
#include <LibWeb/Bindings/Intrinsics.h>
8
#include <LibWeb/Bindings/SVGLinearGradientElementPrototype.h>
9
#include <LibWeb/DOM/Document.h>
10
#include <LibWeb/Painting/PaintStyle.h>
11
#include <LibWeb/SVG/AttributeNames.h>
12
#include <LibWeb/SVG/AttributeParser.h>
13
#include <LibWeb/SVG/SVGLinearGradientElement.h>
14
#include <LibWeb/SVG/SVGStopElement.h>
15
16
namespace Web::SVG {
17
18
JS_DEFINE_ALLOCATOR(SVGLinearGradientElement);
19
20
SVGLinearGradientElement::SVGLinearGradientElement(DOM::Document& document, DOM::QualifiedName qualified_name)
21
0
    : SVGGradientElement(document, qualified_name)
22
0
{
23
0
}
24
25
void SVGLinearGradientElement::initialize(JS::Realm& realm)
26
0
{
27
0
    Base::initialize(realm);
28
0
    WEB_SET_PROTOTYPE_FOR_INTERFACE(SVGLinearGradientElement);
29
0
}
30
31
void SVGLinearGradientElement::attribute_changed(FlyString const& name, Optional<String> const& old_value, Optional<String> const& value)
32
0
{
33
0
    SVGGradientElement::attribute_changed(name, old_value, value);
34
35
    // FIXME: Should allow for `<number-percentage> | <length>` for x1, x2, y1, y2
36
0
    if (name == SVG::AttributeNames::x1) {
37
0
        m_x1 = AttributeParser::parse_number_percentage(value.value_or(String {}));
38
0
    } else if (name == SVG::AttributeNames::y1) {
39
0
        m_y1 = AttributeParser::parse_number_percentage(value.value_or(String {}));
40
0
    } else if (name == SVG::AttributeNames::x2) {
41
0
        m_x2 = AttributeParser::parse_number_percentage(value.value_or(String {}));
42
0
    } else if (name == SVG::AttributeNames::y2) {
43
0
        m_y2 = AttributeParser::parse_number_percentage(value.value_or(String {}));
44
0
    }
45
0
}
46
47
// https://www.w3.org/TR/SVG11/pservers.html#LinearGradientElementX1Attribute
48
NumberPercentage SVGLinearGradientElement::start_x() const
49
0
{
50
0
    HashTable<SVGGradientElement const*> seen_gradients;
51
0
    return start_x_impl(seen_gradients);
52
0
}
53
54
NumberPercentage SVGLinearGradientElement::start_x_impl(HashTable<SVGGradientElement const*>& seen_gradients) const
55
0
{
56
0
    if (m_x1.has_value())
57
0
        return *m_x1;
58
0
    if (auto gradient = linked_linear_gradient(seen_gradients))
59
0
        return gradient->start_x_impl(seen_gradients);
60
    // If the attribute is not specified, the effect is as if a value of '0%' were specified.
61
0
    return NumberPercentage::create_percentage(0);
62
0
}
63
64
// https://www.w3.org/TR/SVG11/pservers.html#LinearGradientElementY1Attribute
65
NumberPercentage SVGLinearGradientElement::start_y() const
66
0
{
67
0
    HashTable<SVGGradientElement const*> seen_gradients;
68
0
    return start_y_impl(seen_gradients);
69
0
}
70
71
NumberPercentage SVGLinearGradientElement::start_y_impl(HashTable<SVGGradientElement const*>& seen_gradients) const
72
0
{
73
0
    if (m_y1.has_value())
74
0
        return *m_y1;
75
0
    if (auto gradient = linked_linear_gradient(seen_gradients))
76
0
        return gradient->start_y_impl(seen_gradients);
77
    // If the attribute is not specified, the effect is as if a value of '0%' were specified.
78
0
    return NumberPercentage::create_percentage(0);
79
0
}
80
81
// https://www.w3.org/TR/SVG11/pservers.html#LinearGradientElementX2Attribute
82
NumberPercentage SVGLinearGradientElement::end_x() const
83
0
{
84
0
    HashTable<SVGGradientElement const*> seen_gradients;
85
0
    return end_x_impl(seen_gradients);
86
0
}
87
88
NumberPercentage SVGLinearGradientElement::end_x_impl(HashTable<SVGGradientElement const*>& seen_gradients) const
89
0
{
90
0
    if (m_x2.has_value())
91
0
        return *m_x2;
92
0
    if (auto gradient = linked_linear_gradient(seen_gradients))
93
0
        return gradient->end_x_impl(seen_gradients);
94
    // If the attribute is not specified, the effect is as if a value of '100%' were specified.
95
0
    return NumberPercentage::create_percentage(100);
96
0
}
97
98
// https://www.w3.org/TR/SVG11/pservers.html#LinearGradientElementY2Attribute
99
NumberPercentage SVGLinearGradientElement::end_y() const
100
0
{
101
0
    HashTable<SVGGradientElement const*> seen_gradients;
102
0
    return end_y_impl(seen_gradients);
103
0
}
104
105
NumberPercentage SVGLinearGradientElement::end_y_impl(HashTable<SVGGradientElement const*>& seen_gradients) const
106
0
{
107
0
    if (m_y2.has_value())
108
0
        return *m_y2;
109
0
    if (auto gradient = linked_linear_gradient(seen_gradients))
110
0
        return gradient->end_y_impl(seen_gradients);
111
    // If the attribute is not specified, the effect is as if a value of '0%' were specified.
112
0
    return NumberPercentage::create_percentage(0);
113
0
}
114
115
Optional<Painting::PaintStyle> SVGLinearGradientElement::to_gfx_paint_style(SVGPaintContext const& paint_context) const
116
0
{
117
0
    auto units = gradient_units();
118
    // FIXME: Resolve percentages properly
119
0
    Gfx::FloatPoint start_point {};
120
0
    Gfx::FloatPoint end_point {};
121
    // https://svgwg.org/svg2-draft/pservers.html#LinearGradientElementGradientUnitsAttribute
122
0
    if (units == GradientUnits::ObjectBoundingBox) {
123
        // If gradientUnits="objectBoundingBox", the user coordinate system for attributes ‘x1’, ‘y1’, ‘x2’ and ‘y2’
124
        // is established using the bounding box of the element to which the gradient is applied (see Object bounding
125
        // box units) and then applying the transform specified by attribute ‘gradientTransform’. Percentages represent
126
        // values relative to the bounding box for the object.
127
        // Note: For gradientUnits="objectBoundingBox" both "100%" and "1" are treated the same.
128
0
        start_point = { start_x().value(), start_y().value() };
129
0
        end_point = { end_x().value(), end_y().value() };
130
0
    } else {
131
        // GradientUnits::UserSpaceOnUse
132
        // If gradientUnits="userSpaceOnUse", ‘x1’, ‘y1’, ‘x2’, and ‘y2’ represent values in the coordinate system
133
        // that results from taking the current user coordinate system in place at the time when the gradient element
134
        // is referenced (i.e., the user coordinate system for the element referencing the gradient element via a
135
        // fill or stroke property) and then applying the transform specified by attribute ‘gradientTransform’.
136
        // Percentages represent values relative to the current SVG viewport.
137
0
        start_point = Gfx::FloatPoint {
138
0
            start_x().resolve_relative_to(paint_context.viewport.width()),
139
0
            start_y().resolve_relative_to(paint_context.viewport.height()),
140
0
        };
141
0
        end_point = Gfx::FloatPoint {
142
0
            end_x().resolve_relative_to(paint_context.viewport.width()),
143
0
            end_y().resolve_relative_to(paint_context.viewport.height()),
144
0
        };
145
0
    }
146
147
0
    if (!m_paint_style) {
148
0
        m_paint_style = Painting::SVGLinearGradientPaintStyle::create(start_point, end_point);
149
        // FIXME: Update stops in DOM changes:
150
0
        add_color_stops(*m_paint_style);
151
0
    } else {
152
0
        m_paint_style->set_start_point(start_point);
153
0
        m_paint_style->set_end_point(end_point);
154
0
    }
155
156
0
    m_paint_style->set_gradient_transform(gradient_paint_transform(paint_context));
157
0
    m_paint_style->set_spread_method(to_painting_spread_method(spread_method()));
158
0
    return *m_paint_style;
159
0
}
160
161
JS::NonnullGCPtr<SVGAnimatedLength> SVGLinearGradientElement::x1() const
162
0
{
163
    // FIXME: Implement this properly.
164
0
    return SVGAnimatedLength::create(realm(), SVGLength::create(realm(), 0, 0), SVGLength::create(realm(), 0, 0));
165
0
}
166
167
JS::NonnullGCPtr<SVGAnimatedLength> SVGLinearGradientElement::y1() const
168
0
{
169
    // FIXME: Implement this properly.
170
0
    return SVGAnimatedLength::create(realm(), SVGLength::create(realm(), 0, 0), SVGLength::create(realm(), 0, 0));
171
0
}
172
173
JS::NonnullGCPtr<SVGAnimatedLength> SVGLinearGradientElement::x2() const
174
0
{
175
    // FIXME: Implement this properly.
176
0
    return SVGAnimatedLength::create(realm(), SVGLength::create(realm(), 0, 0), SVGLength::create(realm(), 0, 0));
177
0
}
178
179
JS::NonnullGCPtr<SVGAnimatedLength> SVGLinearGradientElement::y2() const
180
0
{
181
    // FIXME: Implement this properly.
182
0
    return SVGAnimatedLength::create(realm(), SVGLength::create(realm(), 0, 0), SVGLength::create(realm(), 0, 0));
183
0
}
184
185
}