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/CSS/StyleValues/CSSMathValue.cpp
Line
Count
Source
1
/*
2
 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3
 * Copyright (c) 2021, Tobias Christiansen <tobyase@serenityos.org>
4
 * Copyright (c) 2021-2024, Sam Atkins <sam@ladybird.org>
5
 * Copyright (c) 2022-2023, MacDue <macdue@dueutil.tech>
6
 *
7
 * SPDX-License-Identifier: BSD-2-Clause
8
 */
9
10
#include "CSSMathValue.h"
11
#include <LibWeb/CSS/Percentage.h>
12
#include <LibWeb/CSS/PropertyID.h>
13
14
namespace Web::CSS {
15
16
static bool is_number(CSSMathValue::ResolvedType type)
17
0
{
18
0
    return type == CSSMathValue::ResolvedType::Number || type == CSSMathValue::ResolvedType::Integer;
19
0
}
20
21
static bool is_dimension(CSSMathValue::ResolvedType type)
22
0
{
23
0
    return type != CSSMathValue::ResolvedType::Number
24
0
        && type != CSSMathValue::ResolvedType::Integer
25
0
        && type != CSSMathValue::ResolvedType::Percentage;
26
0
}
27
28
static double resolve_value_radians(CSSMathValue::CalculationResult::Value value)
29
0
{
30
0
    return value.visit(
31
0
        [](Number const& number) { return number.value(); },
32
0
        [](Angle const& angle) { return angle.to_radians(); },
33
0
        [](auto const&) { VERIFY_NOT_REACHED(); return 0.0; });
Unexecuted instantiation: CSSMathValue.cpp:auto Web::CSS::resolve_value_radians(AK::Variant<Web::CSS::Number, Web::CSS::Angle, Web::CSS::Flex, Web::CSS::Frequency, Web::CSS::Length, Web::CSS::Percentage, Web::CSS::Resolution, Web::CSS::Time>)::$_2::operator()<Web::CSS::Flex>(Web::CSS::Flex const&) const
Unexecuted instantiation: CSSMathValue.cpp:auto Web::CSS::resolve_value_radians(AK::Variant<Web::CSS::Number, Web::CSS::Angle, Web::CSS::Flex, Web::CSS::Frequency, Web::CSS::Length, Web::CSS::Percentage, Web::CSS::Resolution, Web::CSS::Time>)::$_2::operator()<Web::CSS::Frequency>(Web::CSS::Frequency const&) const
Unexecuted instantiation: CSSMathValue.cpp:auto Web::CSS::resolve_value_radians(AK::Variant<Web::CSS::Number, Web::CSS::Angle, Web::CSS::Flex, Web::CSS::Frequency, Web::CSS::Length, Web::CSS::Percentage, Web::CSS::Resolution, Web::CSS::Time>)::$_2::operator()<Web::CSS::Length>(Web::CSS::Length const&) const
Unexecuted instantiation: CSSMathValue.cpp:auto Web::CSS::resolve_value_radians(AK::Variant<Web::CSS::Number, Web::CSS::Angle, Web::CSS::Flex, Web::CSS::Frequency, Web::CSS::Length, Web::CSS::Percentage, Web::CSS::Resolution, Web::CSS::Time>)::$_2::operator()<Web::CSS::Percentage>(Web::CSS::Percentage const&) const
Unexecuted instantiation: CSSMathValue.cpp:auto Web::CSS::resolve_value_radians(AK::Variant<Web::CSS::Number, Web::CSS::Angle, Web::CSS::Flex, Web::CSS::Frequency, Web::CSS::Length, Web::CSS::Percentage, Web::CSS::Resolution, Web::CSS::Time>)::$_2::operator()<Web::CSS::Resolution>(Web::CSS::Resolution const&) const
Unexecuted instantiation: CSSMathValue.cpp:auto Web::CSS::resolve_value_radians(AK::Variant<Web::CSS::Number, Web::CSS::Angle, Web::CSS::Flex, Web::CSS::Frequency, Web::CSS::Length, Web::CSS::Percentage, Web::CSS::Resolution, Web::CSS::Time>)::$_2::operator()<Web::CSS::Time>(Web::CSS::Time const&) const
34
0
}
35
36
static double resolve_value(CSSMathValue::CalculationResult::Value value, Optional<Length::ResolutionContext const&> context)
37
0
{
38
0
    return value.visit(
39
0
        [](Number const& number) { return number.value(); },
40
0
        [](Angle const& angle) { return angle.to_degrees(); },
41
0
        [](Flex const& flex) { return flex.to_fr(); },
42
0
        [](Frequency const& frequency) { return frequency.to_hertz(); },
43
0
        [](Percentage const& percentage) { return percentage.value(); },
44
0
        [](Resolution const& resolution) { return resolution.to_dots_per_pixel(); },
45
0
        [](Time const& time) { return time.to_seconds(); },
46
0
        [&context](Length const& length) {
47
            // Handle some common cases first, so we can resolve more without a context
48
0
            if (length.is_auto())
49
0
                return 0.0;
50
51
0
            if (length.is_absolute())
52
0
                return length.absolute_length_to_px().to_double();
53
54
            // If we dont have a context, we cant resolve the length, so return NAN
55
0
            if (!context.has_value()) {
56
0
                dbgln("Failed to resolve length, likely due to calc() being used with relative units and a property not taking it into account");
57
0
                return Number(Number::Type::Number, NAN).value();
58
0
            }
59
60
0
            return length.to_px(*context).to_double();
61
0
        });
62
0
}
63
64
static Optional<CSSNumericType> add_the_types(Vector<NonnullOwnPtr<CalculationNode>> const& nodes, PropertyID property_id)
65
0
{
66
0
    Optional<CSSNumericType> left_type;
67
0
    for (auto const& value : nodes) {
68
0
        auto right_type = value->determine_type(property_id);
69
0
        if (!right_type.has_value())
70
0
            return {};
71
72
0
        if (left_type.has_value()) {
73
0
            left_type = left_type->added_to(right_type.value());
74
0
        } else {
75
0
            left_type = right_type;
76
0
        }
77
78
0
        if (!left_type.has_value())
79
0
            return {};
80
0
    }
81
82
0
    return left_type;
83
0
}
84
85
static CSSMathValue::CalculationResult to_resolved_type(CSSMathValue::ResolvedType type, double value)
86
0
{
87
0
    switch (type) {
88
0
    case CSSMathValue::ResolvedType::Integer:
89
0
        return { Number(Number::Type::Integer, value) };
90
0
    case CSSMathValue::ResolvedType::Number:
91
0
        return { Number(Number::Type::Number, value) };
92
0
    case CSSMathValue::ResolvedType::Angle:
93
0
        return { Angle::make_degrees(value) };
94
0
    case CSSMathValue::ResolvedType::Flex:
95
0
        return { Flex::make_fr(value) };
96
0
    case CSSMathValue::ResolvedType::Frequency:
97
0
        return { Frequency::make_hertz(value) };
98
0
    case CSSMathValue::ResolvedType::Length:
99
0
        return { Length::make_px(CSSPixels::nearest_value_for(value)) };
100
0
    case CSSMathValue::ResolvedType::Percentage:
101
0
        return { Percentage(value) };
102
0
    case CSSMathValue::ResolvedType::Resolution:
103
0
        return { Resolution::make_dots_per_pixel(value) };
104
0
    case CSSMathValue::ResolvedType::Time:
105
0
        return { Time::make_seconds(value) };
106
0
    }
107
108
0
    VERIFY_NOT_REACHED();
109
0
}
110
111
Optional<CalculationNode::ConstantType> CalculationNode::constant_type_from_string(StringView string)
112
0
{
113
0
    if (string.equals_ignoring_ascii_case("e"sv))
114
0
        return CalculationNode::ConstantType::E;
115
116
0
    if (string.equals_ignoring_ascii_case("pi"sv))
117
0
        return CalculationNode::ConstantType::Pi;
118
119
0
    if (string.equals_ignoring_ascii_case("infinity"sv))
120
0
        return CalculationNode::ConstantType::Infinity;
121
122
0
    if (string.equals_ignoring_ascii_case("-infinity"sv))
123
0
        return CalculationNode::ConstantType::MinusInfinity;
124
125
0
    if (string.equals_ignoring_ascii_case("NaN"sv))
126
0
        return CalculationNode::ConstantType::NaN;
127
128
0
    return {};
129
0
}
130
131
CalculationNode::CalculationNode(Type type)
132
0
    : m_type(type)
133
0
{
134
0
}
135
136
0
CalculationNode::~CalculationNode() = default;
137
138
NonnullOwnPtr<NumericCalculationNode> NumericCalculationNode::create(NumericValue value)
139
0
{
140
0
    return adopt_own(*new (nothrow) NumericCalculationNode(move(value)));
141
0
}
142
143
NumericCalculationNode::NumericCalculationNode(NumericValue value)
144
0
    : CalculationNode(Type::Numeric)
145
0
    , m_value(move(value))
146
0
{
147
0
}
148
149
0
NumericCalculationNode::~NumericCalculationNode() = default;
150
151
String NumericCalculationNode::to_string() const
152
0
{
153
0
    return m_value.visit([](auto& value) { return value.to_string(); });
Unexecuted instantiation: CSSMathValue.cpp:auto Web::CSS::NumericCalculationNode::to_string() const::$_0::operator()<Web::CSS::Number const>(Web::CSS::Number const&) const
Unexecuted instantiation: CSSMathValue.cpp:auto Web::CSS::NumericCalculationNode::to_string() const::$_0::operator()<Web::CSS::Angle const>(Web::CSS::Angle const&) const
Unexecuted instantiation: CSSMathValue.cpp:auto Web::CSS::NumericCalculationNode::to_string() const::$_0::operator()<Web::CSS::Flex const>(Web::CSS::Flex const&) const
Unexecuted instantiation: CSSMathValue.cpp:auto Web::CSS::NumericCalculationNode::to_string() const::$_0::operator()<Web::CSS::Frequency const>(Web::CSS::Frequency const&) const
Unexecuted instantiation: CSSMathValue.cpp:auto Web::CSS::NumericCalculationNode::to_string() const::$_0::operator()<Web::CSS::Length const>(Web::CSS::Length const&) const
Unexecuted instantiation: CSSMathValue.cpp:auto Web::CSS::NumericCalculationNode::to_string() const::$_0::operator()<Web::CSS::Percentage const>(Web::CSS::Percentage const&) const
Unexecuted instantiation: CSSMathValue.cpp:auto Web::CSS::NumericCalculationNode::to_string() const::$_0::operator()<Web::CSS::Resolution const>(Web::CSS::Resolution const&) const
Unexecuted instantiation: CSSMathValue.cpp:auto Web::CSS::NumericCalculationNode::to_string() const::$_0::operator()<Web::CSS::Time const>(Web::CSS::Time const&) const
154
0
}
155
156
Optional<CSSMathValue::ResolvedType> NumericCalculationNode::resolved_type() const
157
0
{
158
0
    return m_value.visit(
159
0
        [](Number const&) { return CSSMathValue::ResolvedType::Number; },
160
0
        [](Angle const&) { return CSSMathValue::ResolvedType::Angle; },
161
0
        [](Flex const&) { return CSSMathValue::ResolvedType::Flex; },
162
0
        [](Frequency const&) { return CSSMathValue::ResolvedType::Frequency; },
163
0
        [](Length const&) { return CSSMathValue::ResolvedType::Length; },
164
0
        [](Percentage const&) { return CSSMathValue::ResolvedType::Percentage; },
165
0
        [](Resolution const&) { return CSSMathValue::ResolvedType::Resolution; },
166
0
        [](Time const&) { return CSSMathValue::ResolvedType::Time; });
167
0
}
168
169
// https://www.w3.org/TR/css-values-4/#determine-the-type-of-a-calculation
170
Optional<CSSNumericType> NumericCalculationNode::determine_type(PropertyID property_id) const
171
0
{
172
    // Anything else is a terminal value, whose type is determined based on its CSS type:
173
0
    return m_value.visit(
174
0
        [](Number const&) {
175
            // -> <number>
176
            // -> <integer>
177
            //    the type is «[ ]» (empty map)
178
0
            return CSSNumericType {};
179
0
        },
180
0
        [](Length const&) {
181
            // -> <length>
182
            //    the type is «[ "length" → 1 ]»
183
0
            return CSSNumericType { CSSNumericType::BaseType::Length, 1 };
184
0
        },
185
0
        [](Angle const&) {
186
            // -> <angle>
187
            //    the type is «[ "angle" → 1 ]»
188
0
            return CSSNumericType { CSSNumericType::BaseType::Angle, 1 };
189
0
        },
190
0
        [](Time const&) {
191
            // -> <time>
192
            //    the type is «[ "time" → 1 ]»
193
0
            return CSSNumericType { CSSNumericType::BaseType::Time, 1 };
194
0
        },
195
0
        [](Frequency const&) {
196
            // -> <frequency>
197
            //    the type is «[ "frequency" → 1 ]»
198
0
            return CSSNumericType { CSSNumericType::BaseType::Frequency, 1 };
199
0
        },
200
0
        [](Resolution const&) {
201
            // -> <resolution>
202
            //    the type is «[ "resolution" → 1 ]»
203
0
            return CSSNumericType { CSSNumericType::BaseType::Resolution, 1 };
204
0
        },
205
0
        [](Flex const&) {
206
            // -> <flex>
207
            //    the type is «[ "flex" → 1 ]»
208
0
            return CSSNumericType { CSSNumericType::BaseType::Flex, 1 };
209
0
        },
210
        // NOTE: <calc-constant> is a separate node type. (FIXME: Should it be?)
211
0
        [property_id](Percentage const&) {
212
            // -> <percentage>
213
            //    If, in the context in which the math function containing this calculation is placed,
214
            //    <percentage>s are resolved relative to another type of value (such as in width,
215
            //    where <percentage> is resolved against a <length>), and that other type is not <number>,
216
            //    the type is determined as the other type.
217
0
            auto percentage_resolved_type = property_resolves_percentages_relative_to(property_id);
218
0
            if (percentage_resolved_type.has_value() && percentage_resolved_type != ValueType::Number && percentage_resolved_type != ValueType::Percentage) {
219
0
                auto base_type = CSSNumericType::base_type_from_value_type(*percentage_resolved_type);
220
0
                VERIFY(base_type.has_value());
221
0
                return CSSNumericType { base_type.value(), 1 };
222
0
            }
223
224
            //    Otherwise, the type is «[ "percent" → 1 ]».
225
0
            return CSSNumericType { CSSNumericType::BaseType::Percent, 1 };
226
0
        });
227
    // In all cases, the associated percent hint is null.
228
0
}
229
230
bool NumericCalculationNode::contains_percentage() const
231
0
{
232
0
    return m_value.has<Percentage>();
233
0
}
234
235
CSSMathValue::CalculationResult NumericCalculationNode::resolve(Optional<Length::ResolutionContext const&>, CSSMathValue::PercentageBasis const& percentage_basis) const
236
0
{
237
0
    if (m_value.has<Percentage>()) {
238
        // NOTE: Depending on whether percentage_basis is set, the caller of resolve() is expecting a raw percentage or
239
        //       resolved length.
240
0
        return percentage_basis.visit(
241
0
            [&](Empty const&) -> CSSMathValue::CalculationResult {
242
0
                return m_value;
243
0
            },
244
0
            [&](auto const& value) {
245
0
                return CSSMathValue::CalculationResult(value.percentage_of(m_value.get<Percentage>()));
246
0
            });
Unexecuted instantiation: CSSMathValue.cpp:auto Web::CSS::NumericCalculationNode::resolve(AK::Optional<Web::CSS::Length::ResolutionContext const&>, AK::Variant<AK::Empty, Web::CSS::Angle, Web::CSS::Flex, Web::CSS::Frequency, Web::CSS::Length, Web::CSS::Time> const&) const::$_1::operator()<Web::CSS::Angle>(Web::CSS::Angle const&) const
Unexecuted instantiation: CSSMathValue.cpp:auto Web::CSS::NumericCalculationNode::resolve(AK::Optional<Web::CSS::Length::ResolutionContext const&>, AK::Variant<AK::Empty, Web::CSS::Angle, Web::CSS::Flex, Web::CSS::Frequency, Web::CSS::Length, Web::CSS::Time> const&) const::$_1::operator()<Web::CSS::Flex>(Web::CSS::Flex const&) const
Unexecuted instantiation: CSSMathValue.cpp:auto Web::CSS::NumericCalculationNode::resolve(AK::Optional<Web::CSS::Length::ResolutionContext const&>, AK::Variant<AK::Empty, Web::CSS::Angle, Web::CSS::Flex, Web::CSS::Frequency, Web::CSS::Length, Web::CSS::Time> const&) const::$_1::operator()<Web::CSS::Frequency>(Web::CSS::Frequency const&) const
Unexecuted instantiation: CSSMathValue.cpp:auto Web::CSS::NumericCalculationNode::resolve(AK::Optional<Web::CSS::Length::ResolutionContext const&>, AK::Variant<AK::Empty, Web::CSS::Angle, Web::CSS::Flex, Web::CSS::Frequency, Web::CSS::Length, Web::CSS::Time> const&) const::$_1::operator()<Web::CSS::Length>(Web::CSS::Length const&) const
Unexecuted instantiation: CSSMathValue.cpp:auto Web::CSS::NumericCalculationNode::resolve(AK::Optional<Web::CSS::Length::ResolutionContext const&>, AK::Variant<AK::Empty, Web::CSS::Angle, Web::CSS::Flex, Web::CSS::Frequency, Web::CSS::Length, Web::CSS::Time> const&) const::$_1::operator()<Web::CSS::Time>(Web::CSS::Time const&) const
247
0
    }
248
249
0
    return m_value;
250
0
}
251
252
void NumericCalculationNode::dump(StringBuilder& builder, int indent) const
253
0
{
254
0
    builder.appendff("{: >{}}NUMERIC({})\n", "", indent, m_value.visit([](auto& it) { return it.to_string(); }));
Unexecuted instantiation: CSSMathValue.cpp:auto Web::CSS::NumericCalculationNode::dump(AK::StringBuilder&, int) const::$_0::operator()<Web::CSS::Number const>(Web::CSS::Number const&) const
Unexecuted instantiation: CSSMathValue.cpp:auto Web::CSS::NumericCalculationNode::dump(AK::StringBuilder&, int) const::$_0::operator()<Web::CSS::Angle const>(Web::CSS::Angle const&) const
Unexecuted instantiation: CSSMathValue.cpp:auto Web::CSS::NumericCalculationNode::dump(AK::StringBuilder&, int) const::$_0::operator()<Web::CSS::Flex const>(Web::CSS::Flex const&) const
Unexecuted instantiation: CSSMathValue.cpp:auto Web::CSS::NumericCalculationNode::dump(AK::StringBuilder&, int) const::$_0::operator()<Web::CSS::Frequency const>(Web::CSS::Frequency const&) const
Unexecuted instantiation: CSSMathValue.cpp:auto Web::CSS::NumericCalculationNode::dump(AK::StringBuilder&, int) const::$_0::operator()<Web::CSS::Length const>(Web::CSS::Length const&) const
Unexecuted instantiation: CSSMathValue.cpp:auto Web::CSS::NumericCalculationNode::dump(AK::StringBuilder&, int) const::$_0::operator()<Web::CSS::Percentage const>(Web::CSS::Percentage const&) const
Unexecuted instantiation: CSSMathValue.cpp:auto Web::CSS::NumericCalculationNode::dump(AK::StringBuilder&, int) const::$_0::operator()<Web::CSS::Resolution const>(Web::CSS::Resolution const&) const
Unexecuted instantiation: CSSMathValue.cpp:auto Web::CSS::NumericCalculationNode::dump(AK::StringBuilder&, int) const::$_0::operator()<Web::CSS::Time const>(Web::CSS::Time const&) const
255
0
}
256
257
bool NumericCalculationNode::equals(CalculationNode const& other) const
258
0
{
259
0
    if (this == &other)
260
0
        return true;
261
0
    if (type() != other.type())
262
0
        return false;
263
0
    return m_value == static_cast<NumericCalculationNode const&>(other).m_value;
264
0
}
265
266
NonnullOwnPtr<SumCalculationNode> SumCalculationNode::create(Vector<NonnullOwnPtr<CalculationNode>> values)
267
0
{
268
0
    return adopt_own(*new (nothrow) SumCalculationNode(move(values)));
269
0
}
270
271
SumCalculationNode::SumCalculationNode(Vector<NonnullOwnPtr<CalculationNode>> values)
272
0
    : CalculationNode(Type::Sum)
273
0
    , m_values(move(values))
274
0
{
275
0
    VERIFY(!m_values.is_empty());
276
0
}
277
278
0
SumCalculationNode::~SumCalculationNode() = default;
279
280
String SumCalculationNode::to_string() const
281
0
{
282
0
    bool first = true;
283
0
    StringBuilder builder;
284
0
    for (auto& value : m_values) {
285
0
        if (!first)
286
0
            builder.append(" + "sv);
287
0
        builder.append(value->to_string());
288
0
        first = false;
289
0
    }
290
0
    return MUST(builder.to_string());
291
0
}
292
293
Optional<CSSMathValue::ResolvedType> SumCalculationNode::resolved_type() const
294
0
{
295
    // FIXME: Implement https://www.w3.org/TR/css-values-4/#determine-the-type-of-a-calculation
296
    //        For now, this is just ad-hoc, based on the old implementation.
297
298
0
    Optional<CSSMathValue::ResolvedType> type;
299
0
    for (auto const& value : m_values) {
300
0
        auto maybe_value_type = value->resolved_type();
301
0
        if (!maybe_value_type.has_value())
302
0
            return {};
303
0
        auto value_type = maybe_value_type.value();
304
305
0
        if (!type.has_value()) {
306
0
            type = value_type;
307
0
            continue;
308
0
        }
309
310
        // At + or -, check that both sides have the same type, or that one side is a <number> and the other is an <integer>.
311
        // If both sides are the same type, resolve to that type.
312
0
        if (value_type == type)
313
0
            continue;
314
315
        // If one side is a <number> and the other is an <integer>, resolve to <number>.
316
0
        if (is_number(*type) && is_number(value_type)) {
317
0
            type = CSSMathValue::ResolvedType::Number;
318
0
            continue;
319
0
        }
320
321
        // FIXME: calc() handles <percentage> by allowing them to pretend to be whatever <dimension> type is allowed at this location.
322
        //        Since we can't easily check what that type is, we just allow <percentage> to combine with any other <dimension> type.
323
0
        if (type == CSSMathValue::ResolvedType::Percentage && is_dimension(value_type)) {
324
0
            type = value_type;
325
0
            continue;
326
0
        }
327
0
        if (is_dimension(*type) && value_type == CSSMathValue::ResolvedType::Percentage)
328
0
            continue;
329
330
0
        return {};
331
0
    }
332
0
    return type;
333
0
}
334
335
// https://www.w3.org/TR/css-values-4/#determine-the-type-of-a-calculation
336
Optional<CSSNumericType> SumCalculationNode::determine_type(PropertyID property_id) const
337
0
{
338
    // At a + or - sub-expression, attempt to add the types of the left and right arguments.
339
    // If this returns failure, the entire calculation’s type is failure.
340
    // Otherwise, the sub-expression’s type is the returned type.
341
0
    return add_the_types(m_values, property_id);
342
0
}
343
344
bool SumCalculationNode::contains_percentage() const
345
0
{
346
0
    for (auto const& value : m_values) {
347
0
        if (value->contains_percentage())
348
0
            return true;
349
0
    }
350
0
    return false;
351
0
}
352
353
CSSMathValue::CalculationResult SumCalculationNode::resolve(Optional<Length::ResolutionContext const&> context, CSSMathValue::PercentageBasis const& percentage_basis) const
354
0
{
355
0
    Optional<CSSMathValue::CalculationResult> total;
356
357
0
    for (auto& additional_product : m_values) {
358
0
        auto additional_value = additional_product->resolve(context, percentage_basis);
359
0
        if (!total.has_value()) {
360
0
            total = additional_value;
361
0
            continue;
362
0
        }
363
0
        total->add(additional_value, context, percentage_basis);
364
0
    }
365
366
0
    return total.value();
367
0
}
368
369
void SumCalculationNode::for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const& callback)
370
0
{
371
0
    for (auto& item : m_values) {
372
0
        item->for_each_child_node(callback);
373
0
        callback(item);
374
0
    }
375
0
}
376
377
void SumCalculationNode::dump(StringBuilder& builder, int indent) const
378
0
{
379
0
    builder.appendff("{: >{}}SUM:\n", "", indent);
380
0
    for (auto const& item : m_values)
381
0
        item->dump(builder, indent + 2);
382
0
}
383
384
bool SumCalculationNode::equals(CalculationNode const& other) const
385
0
{
386
0
    if (this == &other)
387
0
        return true;
388
0
    if (type() != other.type())
389
0
        return false;
390
0
    if (m_values.size() != static_cast<SumCalculationNode const&>(other).m_values.size())
391
0
        return false;
392
0
    for (size_t i = 0; i < m_values.size(); ++i) {
393
0
        if (!m_values[i]->equals(*static_cast<SumCalculationNode const&>(other).m_values[i]))
394
0
            return false;
395
0
    }
396
0
    return true;
397
0
}
398
399
NonnullOwnPtr<ProductCalculationNode> ProductCalculationNode::create(Vector<NonnullOwnPtr<CalculationNode>> values)
400
0
{
401
0
    return adopt_own(*new (nothrow) ProductCalculationNode(move(values)));
402
0
}
403
404
ProductCalculationNode::ProductCalculationNode(Vector<NonnullOwnPtr<CalculationNode>> values)
405
0
    : CalculationNode(Type::Product)
406
0
    , m_values(move(values))
407
0
{
408
0
    VERIFY(!m_values.is_empty());
409
0
}
410
411
0
ProductCalculationNode::~ProductCalculationNode() = default;
412
413
String ProductCalculationNode::to_string() const
414
0
{
415
0
    bool first = true;
416
0
    StringBuilder builder;
417
0
    for (auto& value : m_values) {
418
0
        if (!first)
419
0
            builder.append(" * "sv);
420
0
        builder.append(value->to_string());
421
0
        first = false;
422
0
    }
423
0
    return MUST(builder.to_string());
424
0
}
425
426
Optional<CSSMathValue::ResolvedType> ProductCalculationNode::resolved_type() const
427
0
{
428
    // FIXME: Implement https://www.w3.org/TR/css-values-4/#determine-the-type-of-a-calculation
429
    //        For now, this is just ad-hoc, based on the old implementation.
430
431
0
    Optional<CSSMathValue::ResolvedType> type;
432
0
    for (auto const& value : m_values) {
433
0
        auto maybe_value_type = value->resolved_type();
434
0
        if (!maybe_value_type.has_value())
435
0
            return {};
436
0
        auto value_type = maybe_value_type.value();
437
438
0
        if (!type.has_value()) {
439
0
            type = value_type;
440
0
            continue;
441
0
        }
442
443
        // At *, check that at least one side is <number>.
444
0
        if (!(is_number(*type) || is_number(value_type)))
445
0
            return {};
446
        // If both sides are <integer>, resolve to <integer>.
447
0
        if (type == CSSMathValue::ResolvedType::Integer && value_type == CSSMathValue::ResolvedType::Integer) {
448
0
            type = CSSMathValue::ResolvedType::Integer;
449
0
        } else {
450
            // Otherwise, resolve to the type of the other side.
451
0
            if (is_number(*type))
452
0
                type = value_type;
453
0
        }
454
0
    }
455
0
    return type;
456
0
}
457
458
// https://www.w3.org/TR/css-values-4/#determine-the-type-of-a-calculation
459
Optional<CSSNumericType> ProductCalculationNode::determine_type(PropertyID property_id) const
460
0
{
461
    // At a * sub-expression, multiply the types of the left and right arguments.
462
    // The sub-expression’s type is the returned result.
463
0
    Optional<CSSNumericType> left_type;
464
0
    for (auto const& value : m_values) {
465
0
        auto right_type = value->determine_type(property_id);
466
0
        if (!right_type.has_value())
467
0
            return {};
468
469
0
        if (left_type.has_value()) {
470
0
            left_type = left_type->multiplied_by(right_type.value());
471
0
        } else {
472
0
            left_type = right_type;
473
0
        }
474
475
0
        if (!left_type.has_value())
476
0
            return {};
477
0
    }
478
479
0
    return left_type;
480
0
}
481
482
bool ProductCalculationNode::contains_percentage() const
483
0
{
484
0
    for (auto const& value : m_values) {
485
0
        if (value->contains_percentage())
486
0
            return true;
487
0
    }
488
0
    return false;
489
0
}
490
491
CSSMathValue::CalculationResult ProductCalculationNode::resolve(Optional<Length::ResolutionContext const&> context, CSSMathValue::PercentageBasis const& percentage_basis) const
492
0
{
493
0
    Optional<CSSMathValue::CalculationResult> total;
494
495
0
    for (auto& additional_product : m_values) {
496
0
        auto additional_value = additional_product->resolve(context, percentage_basis);
497
0
        if (!total.has_value()) {
498
0
            total = additional_value;
499
0
            continue;
500
0
        }
501
0
        total->multiply_by(additional_value, context);
502
0
    }
503
504
0
    return total.value();
505
0
}
506
507
void ProductCalculationNode::for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const& callback)
508
0
{
509
0
    for (auto& item : m_values) {
510
0
        item->for_each_child_node(callback);
511
0
        callback(item);
512
0
    }
513
0
}
514
515
void ProductCalculationNode::dump(StringBuilder& builder, int indent) const
516
0
{
517
0
    builder.appendff("{: >{}}PRODUCT:\n", "", indent);
518
0
    for (auto const& item : m_values)
519
0
        item->dump(builder, indent + 2);
520
0
}
521
522
bool ProductCalculationNode::equals(CalculationNode const& other) const
523
0
{
524
0
    if (this == &other)
525
0
        return true;
526
0
    if (type() != other.type())
527
0
        return false;
528
0
    if (m_values.size() != static_cast<ProductCalculationNode const&>(other).m_values.size())
529
0
        return false;
530
0
    for (size_t i = 0; i < m_values.size(); ++i) {
531
0
        if (!m_values[i]->equals(*static_cast<ProductCalculationNode const&>(other).m_values[i]))
532
0
            return false;
533
0
    }
534
0
    return true;
535
0
}
536
537
NonnullOwnPtr<NegateCalculationNode> NegateCalculationNode::create(NonnullOwnPtr<Web::CSS::CalculationNode> value)
538
0
{
539
0
    return adopt_own(*new (nothrow) NegateCalculationNode(move(value)));
540
0
}
541
542
NegateCalculationNode::NegateCalculationNode(NonnullOwnPtr<CalculationNode> value)
543
0
    : CalculationNode(Type::Negate)
544
0
    , m_value(move(value))
545
0
{
546
0
}
547
548
0
NegateCalculationNode::~NegateCalculationNode() = default;
549
550
String NegateCalculationNode::to_string() const
551
0
{
552
0
    return MUST(String::formatted("(0 - {})", m_value->to_string()));
553
0
}
554
555
Optional<CSSMathValue::ResolvedType> NegateCalculationNode::resolved_type() const
556
0
{
557
0
    return m_value->resolved_type();
558
0
}
559
560
// https://www.w3.org/TR/css-values-4/#determine-the-type-of-a-calculation
561
Optional<CSSNumericType> NegateCalculationNode::determine_type(PropertyID property_id) const
562
0
{
563
    // NOTE: `- foo` doesn't change the type
564
0
    return m_value->determine_type(property_id);
565
0
}
566
567
bool NegateCalculationNode::contains_percentage() const
568
0
{
569
0
    return m_value->contains_percentage();
570
0
}
571
572
CSSMathValue::CalculationResult NegateCalculationNode::resolve(Optional<Length::ResolutionContext const&> context, CSSMathValue::PercentageBasis const& percentage_basis) const
573
0
{
574
0
    auto child_value = m_value->resolve(context, percentage_basis);
575
0
    child_value.negate();
576
0
    return child_value;
577
0
}
578
579
void NegateCalculationNode::for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const& callback)
580
0
{
581
0
    m_value->for_each_child_node(callback);
582
0
    callback(m_value);
583
0
}
584
585
void NegateCalculationNode::dump(StringBuilder& builder, int indent) const
586
0
{
587
0
    builder.appendff("{: >{}}NEGATE:\n", "", indent);
588
0
    m_value->dump(builder, indent + 2);
589
0
}
590
591
bool NegateCalculationNode::equals(CalculationNode const& other) const
592
0
{
593
0
    if (this == &other)
594
0
        return true;
595
0
    if (type() != other.type())
596
0
        return false;
597
0
    return m_value->equals(*static_cast<NegateCalculationNode const&>(other).m_value);
598
0
}
599
600
NonnullOwnPtr<InvertCalculationNode> InvertCalculationNode::create(NonnullOwnPtr<Web::CSS::CalculationNode> value)
601
0
{
602
0
    return adopt_own(*new (nothrow) InvertCalculationNode(move(value)));
603
0
}
604
605
InvertCalculationNode::InvertCalculationNode(NonnullOwnPtr<CalculationNode> value)
606
0
    : CalculationNode(Type::Invert)
607
0
    , m_value(move(value))
608
0
{
609
0
}
610
611
0
InvertCalculationNode::~InvertCalculationNode() = default;
612
613
String InvertCalculationNode::to_string() const
614
0
{
615
0
    return MUST(String::formatted("(1 / {})", m_value->to_string()));
616
0
}
617
618
Optional<CSSMathValue::ResolvedType> InvertCalculationNode::resolved_type() const
619
0
{
620
0
    auto type = m_value->resolved_type();
621
0
    if (type == CSSMathValue::ResolvedType::Integer)
622
0
        return CSSMathValue::ResolvedType::Number;
623
0
    return type;
624
0
}
625
626
// https://www.w3.org/TR/css-values-4/#determine-the-type-of-a-calculation
627
Optional<CSSNumericType> InvertCalculationNode::determine_type(PropertyID property_id) const
628
0
{
629
    // At a / sub-expression, let left type be the result of finding the types of its left argument,
630
    // and right type be the result of finding the types of its right argument and then inverting it.
631
    // The sub-expression’s type is the result of multiplying the left type and right type.
632
    // NOTE: An InvertCalculationNode only represents the right argument here, and the multiplication
633
    //       is handled in the parent ProductCalculationNode.
634
0
    return m_value->determine_type(property_id).map([](auto& it) { return it.inverted(); });
635
0
}
636
637
bool InvertCalculationNode::contains_percentage() const
638
0
{
639
0
    return m_value->contains_percentage();
640
0
}
641
642
CSSMathValue::CalculationResult InvertCalculationNode::resolve(Optional<Length::ResolutionContext const&> context, CSSMathValue::PercentageBasis const& percentage_basis) const
643
0
{
644
0
    auto child_value = m_value->resolve(context, percentage_basis);
645
0
    child_value.invert();
646
0
    return child_value;
647
0
}
648
649
void InvertCalculationNode::for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const& callback)
650
0
{
651
0
    m_value->for_each_child_node(callback);
652
0
    callback(m_value);
653
0
}
654
655
void InvertCalculationNode::dump(StringBuilder& builder, int indent) const
656
0
{
657
0
    builder.appendff("{: >{}}INVERT:\n", "", indent);
658
0
    m_value->dump(builder, indent + 2);
659
0
}
660
661
bool InvertCalculationNode::equals(CalculationNode const& other) const
662
0
{
663
0
    if (this == &other)
664
0
        return true;
665
0
    if (type() != other.type())
666
0
        return false;
667
0
    return m_value->equals(*static_cast<InvertCalculationNode const&>(other).m_value);
668
0
}
669
670
NonnullOwnPtr<MinCalculationNode> MinCalculationNode::create(Vector<NonnullOwnPtr<Web::CSS::CalculationNode>> values)
671
0
{
672
0
    return adopt_own(*new (nothrow) MinCalculationNode(move(values)));
673
0
}
674
675
MinCalculationNode::MinCalculationNode(Vector<NonnullOwnPtr<CalculationNode>> values)
676
0
    : CalculationNode(Type::Min)
677
0
    , m_values(move(values))
678
0
{
679
0
}
680
681
0
MinCalculationNode::~MinCalculationNode() = default;
682
683
String MinCalculationNode::to_string() const
684
0
{
685
0
    StringBuilder builder;
686
0
    builder.append("min("sv);
687
0
    for (size_t i = 0; i < m_values.size(); ++i) {
688
0
        if (i != 0)
689
0
            builder.append(", "sv);
690
0
        builder.append(m_values[i]->to_string());
691
0
    }
692
0
    builder.append(")"sv);
693
0
    return MUST(builder.to_string());
694
0
}
695
696
Optional<CSSMathValue::ResolvedType> MinCalculationNode::resolved_type() const
697
0
{
698
    // NOTE: We check during parsing that all values have the same type.
699
0
    return m_values[0]->resolved_type();
700
0
}
701
702
// https://www.w3.org/TR/css-values-4/#determine-the-type-of-a-calculation
703
Optional<CSSNumericType> MinCalculationNode::determine_type(PropertyID property_id) const
704
0
{
705
    // The result of adding the types of its comma-separated calculations.
706
0
    return add_the_types(m_values, property_id);
707
0
}
708
709
bool MinCalculationNode::contains_percentage() const
710
0
{
711
0
    for (auto const& value : m_values) {
712
0
        if (value->contains_percentage())
713
0
            return true;
714
0
    }
715
716
0
    return false;
717
0
}
718
719
CSSMathValue::CalculationResult MinCalculationNode::resolve(Optional<Length::ResolutionContext const&> context, CSSMathValue::PercentageBasis const& percentage_basis) const
720
0
{
721
0
    CSSMathValue::CalculationResult smallest_node = m_values.first()->resolve(context, percentage_basis);
722
0
    auto smallest_value = resolve_value(smallest_node.value(), context);
723
724
0
    for (size_t i = 1; i < m_values.size(); i++) {
725
0
        auto child_resolved = m_values[i]->resolve(context, percentage_basis);
726
0
        auto child_value = resolve_value(child_resolved.value(), context);
727
728
0
        if (child_value < smallest_value) {
729
0
            smallest_value = child_value;
730
0
            smallest_node = child_resolved;
731
0
        }
732
0
    }
733
734
0
    return smallest_node;
735
0
}
736
737
void MinCalculationNode::for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const& callback)
738
0
{
739
0
    for (auto& value : m_values) {
740
0
        value->for_each_child_node(callback);
741
0
        callback(value);
742
0
    }
743
0
}
744
745
void MinCalculationNode::dump(StringBuilder& builder, int indent) const
746
0
{
747
0
    builder.appendff("{: >{}}MIN:\n", "", indent);
748
0
    for (auto const& value : m_values)
749
0
        value->dump(builder, indent + 2);
750
0
}
751
752
bool MinCalculationNode::equals(CalculationNode const& other) const
753
0
{
754
0
    if (this == &other)
755
0
        return true;
756
0
    if (type() != other.type())
757
0
        return false;
758
0
    if (m_values.size() != static_cast<MinCalculationNode const&>(other).m_values.size())
759
0
        return false;
760
0
    for (size_t i = 0; i < m_values.size(); ++i) {
761
0
        if (!m_values[i]->equals(*static_cast<MinCalculationNode const&>(other).m_values[i]))
762
0
            return false;
763
0
    }
764
0
    return true;
765
0
}
766
767
NonnullOwnPtr<MaxCalculationNode> MaxCalculationNode::create(Vector<NonnullOwnPtr<Web::CSS::CalculationNode>> values)
768
0
{
769
0
    return adopt_own(*new (nothrow) MaxCalculationNode(move(values)));
770
0
}
771
772
MaxCalculationNode::MaxCalculationNode(Vector<NonnullOwnPtr<CalculationNode>> values)
773
0
    : CalculationNode(Type::Max)
774
0
    , m_values(move(values))
775
0
{
776
0
}
777
778
0
MaxCalculationNode::~MaxCalculationNode() = default;
779
780
String MaxCalculationNode::to_string() const
781
0
{
782
0
    StringBuilder builder;
783
0
    builder.append("max("sv);
784
0
    for (size_t i = 0; i < m_values.size(); ++i) {
785
0
        if (i != 0)
786
0
            builder.append(", "sv);
787
0
        builder.append(m_values[i]->to_string());
788
0
    }
789
0
    builder.append(")"sv);
790
0
    return MUST(builder.to_string());
791
0
}
792
793
Optional<CSSMathValue::ResolvedType> MaxCalculationNode::resolved_type() const
794
0
{
795
    // NOTE: We check during parsing that all values have the same type.
796
0
    return m_values[0]->resolved_type();
797
0
}
798
799
// https://www.w3.org/TR/css-values-4/#determine-the-type-of-a-calculation
800
Optional<CSSNumericType> MaxCalculationNode::determine_type(PropertyID property_id) const
801
0
{
802
    // The result of adding the types of its comma-separated calculations.
803
0
    return add_the_types(m_values, property_id);
804
0
}
805
806
bool MaxCalculationNode::contains_percentage() const
807
0
{
808
0
    for (auto const& value : m_values) {
809
0
        if (value->contains_percentage())
810
0
            return true;
811
0
    }
812
813
0
    return false;
814
0
}
815
816
CSSMathValue::CalculationResult MaxCalculationNode::resolve(Optional<Length::ResolutionContext const&> context, CSSMathValue::PercentageBasis const& percentage_basis) const
817
0
{
818
0
    CSSMathValue::CalculationResult largest_node = m_values.first()->resolve(context, percentage_basis);
819
0
    auto largest_value = resolve_value(largest_node.value(), context);
820
821
0
    for (size_t i = 1; i < m_values.size(); i++) {
822
0
        auto child_resolved = m_values[i]->resolve(context, percentage_basis);
823
0
        auto child_value = resolve_value(child_resolved.value(), context);
824
825
0
        if (child_value > largest_value) {
826
0
            largest_value = child_value;
827
0
            largest_node = child_resolved;
828
0
        }
829
0
    }
830
831
0
    return largest_node;
832
0
}
833
834
void MaxCalculationNode::for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const& callback)
835
0
{
836
0
    for (auto& value : m_values) {
837
0
        value->for_each_child_node(callback);
838
0
        callback(value);
839
0
    }
840
0
}
841
842
void MaxCalculationNode::dump(StringBuilder& builder, int indent) const
843
0
{
844
0
    builder.appendff("{: >{}}MAX:\n", "", indent);
845
0
    for (auto const& value : m_values)
846
0
        value->dump(builder, indent + 2);
847
0
}
848
849
bool MaxCalculationNode::equals(CalculationNode const& other) const
850
0
{
851
0
    if (this == &other)
852
0
        return true;
853
0
    if (type() != other.type())
854
0
        return false;
855
0
    if (m_values.size() != static_cast<MaxCalculationNode const&>(other).m_values.size())
856
0
        return false;
857
0
    for (size_t i = 0; i < m_values.size(); ++i) {
858
0
        if (!m_values[i]->equals(*static_cast<MaxCalculationNode const&>(other).m_values[i]))
859
0
            return false;
860
0
    }
861
0
    return true;
862
0
}
863
864
NonnullOwnPtr<ClampCalculationNode> ClampCalculationNode::create(NonnullOwnPtr<CalculationNode> min, NonnullOwnPtr<CalculationNode> center, NonnullOwnPtr<CalculationNode> max)
865
0
{
866
0
    return adopt_own(*new (nothrow) ClampCalculationNode(move(min), move(center), move(max)));
867
0
}
868
869
ClampCalculationNode::ClampCalculationNode(NonnullOwnPtr<CalculationNode> min, NonnullOwnPtr<CalculationNode> center, NonnullOwnPtr<CalculationNode> max)
870
0
    : CalculationNode(Type::Clamp)
871
0
    , m_min_value(move(min))
872
0
    , m_center_value(move(center))
873
0
    , m_max_value(move(max))
874
0
{
875
0
}
876
877
0
ClampCalculationNode::~ClampCalculationNode() = default;
878
879
String ClampCalculationNode::to_string() const
880
0
{
881
0
    StringBuilder builder;
882
0
    builder.append("clamp("sv);
883
0
    builder.append(m_min_value->to_string());
884
0
    builder.append(", "sv);
885
0
    builder.append(m_center_value->to_string());
886
0
    builder.append(", "sv);
887
0
    builder.append(m_max_value->to_string());
888
0
    builder.append(")"sv);
889
0
    return MUST(builder.to_string());
890
0
}
891
892
Optional<CSSMathValue::ResolvedType> ClampCalculationNode::resolved_type() const
893
0
{
894
    // NOTE: We check during parsing that all values have the same type.
895
0
    return m_min_value->resolved_type();
896
0
}
897
898
// https://www.w3.org/TR/css-values-4/#determine-the-type-of-a-calculation
899
Optional<CSSNumericType> ClampCalculationNode::determine_type(PropertyID property_id) const
900
0
{
901
    // The result of adding the types of its comma-separated calculations.
902
0
    auto min_type = m_min_value->determine_type(property_id);
903
0
    auto center_type = m_center_value->determine_type(property_id);
904
0
    auto max_type = m_max_value->determine_type(property_id);
905
906
0
    if (!min_type.has_value() || !center_type.has_value() || !max_type.has_value())
907
0
        return {};
908
909
0
    auto result = min_type->added_to(*center_type);
910
0
    if (!result.has_value())
911
0
        return {};
912
0
    return result->added_to(*max_type);
913
0
}
914
915
bool ClampCalculationNode::contains_percentage() const
916
0
{
917
0
    return m_min_value->contains_percentage() || m_center_value->contains_percentage() || m_max_value->contains_percentage();
918
0
}
919
920
CSSMathValue::CalculationResult ClampCalculationNode::resolve(Optional<Length::ResolutionContext const&> context, CSSMathValue::PercentageBasis const& percentage_basis) const
921
0
{
922
0
    auto min_node = m_min_value->resolve(context, percentage_basis);
923
0
    auto center_node = m_center_value->resolve(context, percentage_basis);
924
0
    auto max_node = m_max_value->resolve(context, percentage_basis);
925
926
0
    auto min_value = resolve_value(min_node.value(), context);
927
0
    auto center_value = resolve_value(center_node.value(), context);
928
0
    auto max_value = resolve_value(max_node.value(), context);
929
930
    // NOTE: The value should be returned as "max(MIN, min(VAL, MAX))"
931
0
    auto chosen_value = max(min_value, min(center_value, max_value));
932
0
    if (chosen_value == min_value)
933
0
        return min_node;
934
0
    if (chosen_value == center_value)
935
0
        return center_node;
936
0
    if (chosen_value == max_value)
937
0
        return max_node;
938
939
0
    VERIFY_NOT_REACHED();
940
0
}
941
942
void ClampCalculationNode::for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const& callback)
943
0
{
944
0
    m_min_value->for_each_child_node(callback);
945
0
    m_center_value->for_each_child_node(callback);
946
0
    m_max_value->for_each_child_node(callback);
947
0
    callback(m_min_value);
948
0
    callback(m_center_value);
949
0
    callback(m_max_value);
950
0
}
951
952
void ClampCalculationNode::dump(StringBuilder& builder, int indent) const
953
0
{
954
0
    builder.appendff("{: >{}}CLAMP:\n", "", indent);
955
0
    m_min_value->dump(builder, indent + 2);
956
0
    m_center_value->dump(builder, indent + 2);
957
0
    m_max_value->dump(builder, indent + 2);
958
0
}
959
960
bool ClampCalculationNode::equals(CalculationNode const& other) const
961
0
{
962
0
    if (this == &other)
963
0
        return true;
964
0
    if (type() != other.type())
965
0
        return false;
966
0
    return m_min_value->equals(*static_cast<ClampCalculationNode const&>(other).m_min_value)
967
0
        && m_center_value->equals(*static_cast<ClampCalculationNode const&>(other).m_center_value)
968
0
        && m_max_value->equals(*static_cast<ClampCalculationNode const&>(other).m_max_value);
969
0
}
970
971
NonnullOwnPtr<AbsCalculationNode> AbsCalculationNode::create(NonnullOwnPtr<CalculationNode> value)
972
0
{
973
0
    return adopt_own(*new (nothrow) AbsCalculationNode(move(value)));
974
0
}
975
976
AbsCalculationNode::AbsCalculationNode(NonnullOwnPtr<CalculationNode> value)
977
0
    : CalculationNode(Type::Abs)
978
0
    , m_value(move(value))
979
0
{
980
0
}
981
982
0
AbsCalculationNode::~AbsCalculationNode() = default;
983
984
String AbsCalculationNode::to_string() const
985
0
{
986
0
    StringBuilder builder;
987
0
    builder.append("abs("sv);
988
0
    builder.append(m_value->to_string());
989
0
    builder.append(")"sv);
990
0
    return MUST(builder.to_string());
991
0
}
992
993
Optional<CSSMathValue::ResolvedType> AbsCalculationNode::resolved_type() const
994
0
{
995
0
    return m_value->resolved_type();
996
0
}
997
998
// https://www.w3.org/TR/css-values-4/#determine-the-type-of-a-calculation
999
Optional<CSSNumericType> AbsCalculationNode::determine_type(PropertyID property_id) const
1000
0
{
1001
    // The type of its contained calculation.
1002
0
    return m_value->determine_type(property_id);
1003
0
}
1004
1005
bool AbsCalculationNode::contains_percentage() const
1006
0
{
1007
0
    return m_value->contains_percentage();
1008
0
}
1009
1010
CSSMathValue::CalculationResult AbsCalculationNode::resolve(Optional<Length::ResolutionContext const&> context, CSSMathValue::PercentageBasis const& percentage_basis) const
1011
0
{
1012
0
    auto resolved_type = m_value->resolved_type().value();
1013
0
    auto node_a = m_value->resolve(context, percentage_basis);
1014
0
    auto node_a_value = resolve_value(node_a.value(), context);
1015
1016
0
    if (node_a_value < 0)
1017
0
        return to_resolved_type(resolved_type, -node_a_value);
1018
1019
0
    return node_a;
1020
0
}
1021
1022
void AbsCalculationNode::for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const& callback)
1023
0
{
1024
0
    m_value->for_each_child_node(callback);
1025
0
    callback(m_value);
1026
0
}
1027
1028
void AbsCalculationNode::dump(StringBuilder& builder, int indent) const
1029
0
{
1030
0
    builder.appendff("{: >{}}ABS: {}\n", "", indent, to_string());
1031
0
}
1032
1033
bool AbsCalculationNode::equals(CalculationNode const& other) const
1034
0
{
1035
0
    if (this == &other)
1036
0
        return true;
1037
0
    if (type() != other.type())
1038
0
        return false;
1039
0
    return m_value->equals(*static_cast<AbsCalculationNode const&>(other).m_value);
1040
0
}
1041
1042
NonnullOwnPtr<SignCalculationNode> SignCalculationNode::create(NonnullOwnPtr<CalculationNode> value)
1043
0
{
1044
0
    return adopt_own(*new (nothrow) SignCalculationNode(move(value)));
1045
0
}
1046
1047
SignCalculationNode::SignCalculationNode(NonnullOwnPtr<CalculationNode> value)
1048
0
    : CalculationNode(Type::Sign)
1049
0
    , m_value(move(value))
1050
0
{
1051
0
}
1052
1053
0
SignCalculationNode::~SignCalculationNode() = default;
1054
1055
String SignCalculationNode::to_string() const
1056
0
{
1057
0
    StringBuilder builder;
1058
0
    builder.append("sign("sv);
1059
0
    builder.append(m_value->to_string());
1060
0
    builder.append(")"sv);
1061
0
    return MUST(builder.to_string());
1062
0
}
1063
1064
Optional<CSSMathValue::ResolvedType> SignCalculationNode::resolved_type() const
1065
0
{
1066
0
    return CSSMathValue::ResolvedType::Integer;
1067
0
}
1068
1069
// https://www.w3.org/TR/css-values-4/#determine-the-type-of-a-calculation
1070
Optional<CSSNumericType> SignCalculationNode::determine_type(PropertyID) const
1071
0
{
1072
    // «[ ]» (empty map).
1073
0
    return CSSNumericType {};
1074
0
}
1075
1076
bool SignCalculationNode::contains_percentage() const
1077
0
{
1078
0
    return m_value->contains_percentage();
1079
0
}
1080
1081
CSSMathValue::CalculationResult SignCalculationNode::resolve(Optional<Length::ResolutionContext const&> context, CSSMathValue::PercentageBasis const& percentage_basis) const
1082
0
{
1083
0
    auto node_a = m_value->resolve(context, percentage_basis);
1084
0
    auto node_a_value = resolve_value(node_a.value(), context);
1085
1086
0
    if (node_a_value < 0)
1087
0
        return { Number(Number::Type::Integer, -1) };
1088
1089
0
    if (node_a_value > 0)
1090
0
        return { Number(Number::Type::Integer, 1) };
1091
1092
0
    return { Number(Number::Type::Integer, 0) };
1093
0
}
1094
1095
void SignCalculationNode::for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const& callback)
1096
0
{
1097
0
    m_value->for_each_child_node(callback);
1098
0
    callback(m_value);
1099
0
}
1100
1101
void SignCalculationNode::dump(StringBuilder& builder, int indent) const
1102
0
{
1103
0
    builder.appendff("{: >{}}SIGN: {}\n", "", indent, to_string());
1104
0
}
1105
1106
bool SignCalculationNode::equals(CalculationNode const& other) const
1107
0
{
1108
0
    if (this == &other)
1109
0
        return true;
1110
0
    if (type() != other.type())
1111
0
        return false;
1112
0
    return m_value->equals(*static_cast<SignCalculationNode const&>(other).m_value);
1113
0
}
1114
1115
NonnullOwnPtr<ConstantCalculationNode> ConstantCalculationNode::create(ConstantType constant)
1116
0
{
1117
0
    return adopt_own(*new (nothrow) ConstantCalculationNode(constant));
1118
0
}
1119
1120
ConstantCalculationNode::ConstantCalculationNode(ConstantType constant)
1121
0
    : CalculationNode(Type::Constant)
1122
0
    , m_constant(constant)
1123
0
{
1124
0
}
1125
1126
ConstantCalculationNode::~ConstantCalculationNode() = default;
1127
1128
String ConstantCalculationNode::to_string() const
1129
0
{
1130
0
    switch (m_constant) {
1131
0
    case CalculationNode::ConstantType::E:
1132
0
        return "e"_string;
1133
0
    case CalculationNode::ConstantType::Pi:
1134
0
        return "pi"_string;
1135
0
    case CalculationNode::ConstantType::Infinity:
1136
0
        return "infinity"_string;
1137
0
    case CalculationNode::ConstantType::MinusInfinity:
1138
0
        return "-infinity"_string;
1139
0
    case CalculationNode::ConstantType::NaN:
1140
0
        return "NaN"_string;
1141
0
    }
1142
1143
0
    VERIFY_NOT_REACHED();
1144
0
}
1145
Optional<CSSMathValue::ResolvedType> ConstantCalculationNode::resolved_type() const
1146
0
{
1147
0
    return CSSMathValue::ResolvedType::Number;
1148
0
}
1149
1150
// https://www.w3.org/TR/css-values-4/#determine-the-type-of-a-calculation
1151
Optional<CSSNumericType> ConstantCalculationNode::determine_type(PropertyID) const
1152
0
{
1153
    // Anything else is a terminal value, whose type is determined based on its CSS type:
1154
    // -> <calc-constant>
1155
    //    the type is «[ ]» (empty map)
1156
0
    return CSSNumericType {};
1157
0
}
1158
1159
CSSMathValue::CalculationResult ConstantCalculationNode::resolve([[maybe_unused]] Optional<Length::ResolutionContext const&> context, [[maybe_unused]] CSSMathValue::PercentageBasis const& percentage_basis) const
1160
0
{
1161
0
    switch (m_constant) {
1162
0
    case CalculationNode::ConstantType::E:
1163
0
        return { Number(Number::Type::Number, M_E) };
1164
0
    case CalculationNode::ConstantType::Pi:
1165
0
        return { Number(Number::Type::Number, M_PI) };
1166
    // FIXME: We need to keep track of Infinity and NaN across all nodes, since they require special handling.
1167
0
    case CalculationNode::ConstantType::Infinity:
1168
0
        return { Number(Number::Type::Number, NumericLimits<double>::max()) };
1169
0
    case CalculationNode::ConstantType::MinusInfinity:
1170
0
        return { Number(Number::Type::Number, NumericLimits<double>::lowest()) };
1171
0
    case CalculationNode::ConstantType::NaN:
1172
0
        return { Number(Number::Type::Number, NAN) };
1173
0
    }
1174
1175
0
    VERIFY_NOT_REACHED();
1176
0
}
1177
1178
void ConstantCalculationNode::dump(StringBuilder& builder, int indent) const
1179
0
{
1180
0
    builder.appendff("{: >{}}CONSTANT: {}\n", "", indent, to_string());
1181
0
}
1182
1183
bool ConstantCalculationNode::equals(CalculationNode const& other) const
1184
0
{
1185
0
    if (this == &other)
1186
0
        return true;
1187
0
    if (type() != other.type())
1188
0
        return false;
1189
0
    return m_constant == static_cast<ConstantCalculationNode const&>(other).m_constant;
1190
0
}
1191
1192
NonnullOwnPtr<SinCalculationNode> SinCalculationNode::create(NonnullOwnPtr<CalculationNode> value)
1193
0
{
1194
0
    return adopt_own(*new (nothrow) SinCalculationNode(move(value)));
1195
0
}
1196
1197
SinCalculationNode::SinCalculationNode(NonnullOwnPtr<CalculationNode> value)
1198
0
    : CalculationNode(Type::Sin)
1199
0
    , m_value(move(value))
1200
0
{
1201
0
}
1202
1203
0
SinCalculationNode::~SinCalculationNode() = default;
1204
1205
String SinCalculationNode::to_string() const
1206
0
{
1207
0
    StringBuilder builder;
1208
0
    builder.append("sin("sv);
1209
0
    builder.append(m_value->to_string());
1210
0
    builder.append(")"sv);
1211
0
    return MUST(builder.to_string());
1212
0
}
1213
1214
Optional<CSSMathValue::ResolvedType> SinCalculationNode::resolved_type() const
1215
0
{
1216
0
    return CSSMathValue::ResolvedType::Number;
1217
0
}
1218
1219
// https://www.w3.org/TR/css-values-4/#determine-the-type-of-a-calculation
1220
Optional<CSSNumericType> SinCalculationNode::determine_type(PropertyID) const
1221
0
{
1222
    // «[ ]» (empty map).
1223
0
    return CSSNumericType {};
1224
0
}
1225
1226
bool SinCalculationNode::contains_percentage() const
1227
0
{
1228
0
    return m_value->contains_percentage();
1229
0
}
1230
1231
CSSMathValue::CalculationResult SinCalculationNode::resolve(Optional<Length::ResolutionContext const&> context, CSSMathValue::PercentageBasis const& percentage_basis) const
1232
0
{
1233
0
    auto node_a = m_value->resolve(context, percentage_basis);
1234
0
    auto node_a_value = resolve_value_radians(node_a.value());
1235
0
    auto result = sin(node_a_value);
1236
1237
0
    return { Number(Number::Type::Number, result) };
1238
0
}
1239
1240
void SinCalculationNode::for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const& callback)
1241
0
{
1242
0
    m_value->for_each_child_node(callback);
1243
0
    callback(m_value);
1244
0
}
1245
1246
void SinCalculationNode::dump(StringBuilder& builder, int indent) const
1247
0
{
1248
0
    builder.appendff("{: >{}}SIN: {}\n", "", indent, to_string());
1249
0
}
1250
1251
bool SinCalculationNode::equals(CalculationNode const& other) const
1252
0
{
1253
0
    if (this == &other)
1254
0
        return true;
1255
0
    if (type() != other.type())
1256
0
        return false;
1257
0
    return m_value->equals(*static_cast<SinCalculationNode const&>(other).m_value);
1258
0
}
1259
1260
NonnullOwnPtr<CosCalculationNode> CosCalculationNode::create(NonnullOwnPtr<CalculationNode> value)
1261
0
{
1262
0
    return adopt_own(*new (nothrow) CosCalculationNode(move(value)));
1263
0
}
1264
1265
CosCalculationNode::CosCalculationNode(NonnullOwnPtr<CalculationNode> value)
1266
0
    : CalculationNode(Type::Cos)
1267
0
    , m_value(move(value))
1268
0
{
1269
0
}
1270
1271
0
CosCalculationNode::~CosCalculationNode() = default;
1272
1273
String CosCalculationNode::to_string() const
1274
0
{
1275
0
    StringBuilder builder;
1276
0
    builder.append("cos("sv);
1277
0
    builder.append(m_value->to_string());
1278
0
    builder.append(")"sv);
1279
0
    return MUST(builder.to_string());
1280
0
}
1281
1282
Optional<CSSMathValue::ResolvedType> CosCalculationNode::resolved_type() const
1283
0
{
1284
0
    return CSSMathValue::ResolvedType::Number;
1285
0
}
1286
1287
// https://www.w3.org/TR/css-values-4/#determine-the-type-of-a-calculation
1288
Optional<CSSNumericType> CosCalculationNode::determine_type(PropertyID) const
1289
0
{
1290
    // «[ ]» (empty map).
1291
0
    return CSSNumericType {};
1292
0
}
1293
1294
bool CosCalculationNode::contains_percentage() const
1295
0
{
1296
0
    return m_value->contains_percentage();
1297
0
}
1298
1299
CSSMathValue::CalculationResult CosCalculationNode::resolve(Optional<Length::ResolutionContext const&> context, CSSMathValue::PercentageBasis const& percentage_basis) const
1300
0
{
1301
0
    auto node_a = m_value->resolve(context, percentage_basis);
1302
0
    auto node_a_value = resolve_value_radians(node_a.value());
1303
0
    auto result = cos(node_a_value);
1304
1305
0
    return { Number(Number::Type::Number, result) };
1306
0
}
1307
1308
void CosCalculationNode::for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const& callback)
1309
0
{
1310
0
    m_value->for_each_child_node(callback);
1311
0
    callback(m_value);
1312
0
}
1313
1314
void CosCalculationNode::dump(StringBuilder& builder, int indent) const
1315
0
{
1316
0
    builder.appendff("{: >{}}COS: {}\n", "", indent, to_string());
1317
0
}
1318
1319
bool CosCalculationNode::equals(CalculationNode const& other) const
1320
0
{
1321
0
    if (this == &other)
1322
0
        return true;
1323
0
    if (type() != other.type())
1324
0
        return false;
1325
0
    return m_value->equals(*static_cast<CosCalculationNode const&>(other).m_value);
1326
0
}
1327
1328
NonnullOwnPtr<TanCalculationNode> TanCalculationNode::create(NonnullOwnPtr<CalculationNode> value)
1329
0
{
1330
0
    return adopt_own(*new (nothrow) TanCalculationNode(move(value)));
1331
0
}
1332
1333
TanCalculationNode::TanCalculationNode(NonnullOwnPtr<CalculationNode> value)
1334
0
    : CalculationNode(Type::Tan)
1335
0
    , m_value(move(value))
1336
0
{
1337
0
}
1338
1339
0
TanCalculationNode::~TanCalculationNode() = default;
1340
1341
String TanCalculationNode::to_string() const
1342
0
{
1343
0
    StringBuilder builder;
1344
0
    builder.append("tan("sv);
1345
0
    builder.append(m_value->to_string());
1346
0
    builder.append(")"sv);
1347
0
    return MUST(builder.to_string());
1348
0
}
1349
1350
Optional<CSSMathValue::ResolvedType> TanCalculationNode::resolved_type() const
1351
0
{
1352
0
    return CSSMathValue::ResolvedType::Number;
1353
0
}
1354
1355
// https://www.w3.org/TR/css-values-4/#determine-the-type-of-a-calculation
1356
Optional<CSSNumericType> TanCalculationNode::determine_type(PropertyID) const
1357
0
{
1358
    // «[ ]» (empty map).
1359
0
    return CSSNumericType {};
1360
0
}
1361
1362
bool TanCalculationNode::contains_percentage() const
1363
0
{
1364
0
    return m_value->contains_percentage();
1365
0
}
1366
1367
CSSMathValue::CalculationResult TanCalculationNode::resolve(Optional<Length::ResolutionContext const&> context, CSSMathValue::PercentageBasis const& percentage_basis) const
1368
0
{
1369
0
    auto node_a = m_value->resolve(context, percentage_basis);
1370
0
    auto node_a_value = resolve_value_radians(node_a.value());
1371
0
    auto result = tan(node_a_value);
1372
1373
0
    return { Number(Number::Type::Number, result) };
1374
0
}
1375
1376
void TanCalculationNode::for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const& callback)
1377
0
{
1378
0
    m_value->for_each_child_node(callback);
1379
0
    callback(m_value);
1380
0
}
1381
1382
void TanCalculationNode::dump(StringBuilder& builder, int indent) const
1383
0
{
1384
0
    builder.appendff("{: >{}}TAN: {}\n", "", indent, to_string());
1385
0
}
1386
1387
bool TanCalculationNode::equals(CalculationNode const& other) const
1388
0
{
1389
0
    if (this == &other)
1390
0
        return true;
1391
0
    if (type() != other.type())
1392
0
        return false;
1393
0
    return m_value->equals(*static_cast<TanCalculationNode const&>(other).m_value);
1394
0
}
1395
1396
NonnullOwnPtr<AsinCalculationNode> AsinCalculationNode::create(NonnullOwnPtr<CalculationNode> value)
1397
0
{
1398
0
    return adopt_own(*new (nothrow) AsinCalculationNode(move(value)));
1399
0
}
1400
1401
AsinCalculationNode::AsinCalculationNode(NonnullOwnPtr<CalculationNode> value)
1402
0
    : CalculationNode(Type::Asin)
1403
0
    , m_value(move(value))
1404
0
{
1405
0
}
1406
1407
0
AsinCalculationNode::~AsinCalculationNode() = default;
1408
1409
String AsinCalculationNode::to_string() const
1410
0
{
1411
0
    StringBuilder builder;
1412
0
    builder.append("asin("sv);
1413
0
    builder.append(m_value->to_string());
1414
0
    builder.append(")"sv);
1415
0
    return MUST(builder.to_string());
1416
0
}
1417
1418
Optional<CSSMathValue::ResolvedType> AsinCalculationNode::resolved_type() const
1419
0
{
1420
0
    return CSSMathValue::ResolvedType::Angle;
1421
0
}
1422
1423
// https://www.w3.org/TR/css-values-4/#determine-the-type-of-a-calculation
1424
Optional<CSSNumericType> AsinCalculationNode::determine_type(PropertyID) const
1425
0
{
1426
    // «[ "angle" → 1 ]».
1427
0
    return CSSNumericType { CSSNumericType::BaseType::Angle, 1 };
1428
0
}
1429
1430
bool AsinCalculationNode::contains_percentage() const
1431
0
{
1432
0
    return m_value->contains_percentage();
1433
0
}
1434
1435
CSSMathValue::CalculationResult AsinCalculationNode::resolve(Optional<Length::ResolutionContext const&> context, CSSMathValue::PercentageBasis const& percentage_basis) const
1436
0
{
1437
0
    auto node_a = m_value->resolve(context, percentage_basis);
1438
0
    auto node_a_value = resolve_value(node_a.value(), context);
1439
0
    auto result = asin(node_a_value);
1440
1441
0
    return { Angle(result, Angle::Type::Rad) };
1442
0
}
1443
1444
void AsinCalculationNode::for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const& callback)
1445
0
{
1446
0
    m_value->for_each_child_node(callback);
1447
0
    callback(m_value);
1448
0
}
1449
1450
void AsinCalculationNode::dump(StringBuilder& builder, int indent) const
1451
0
{
1452
0
    builder.appendff("{: >{}}ASIN: {}\n", "", indent, to_string());
1453
0
}
1454
1455
bool AsinCalculationNode::equals(CalculationNode const& other) const
1456
0
{
1457
0
    if (this == &other)
1458
0
        return true;
1459
0
    if (type() != other.type())
1460
0
        return false;
1461
0
    return m_value->equals(*static_cast<AsinCalculationNode const&>(other).m_value);
1462
0
}
1463
1464
NonnullOwnPtr<AcosCalculationNode> AcosCalculationNode::create(NonnullOwnPtr<CalculationNode> value)
1465
0
{
1466
0
    return adopt_own(*new (nothrow) AcosCalculationNode(move(value)));
1467
0
}
1468
1469
AcosCalculationNode::AcosCalculationNode(NonnullOwnPtr<CalculationNode> value)
1470
0
    : CalculationNode(Type::Acos)
1471
0
    , m_value(move(value))
1472
0
{
1473
0
}
1474
1475
0
AcosCalculationNode::~AcosCalculationNode() = default;
1476
1477
String AcosCalculationNode::to_string() const
1478
0
{
1479
0
    StringBuilder builder;
1480
0
    builder.append("acos("sv);
1481
0
    builder.append(m_value->to_string());
1482
0
    builder.append(")"sv);
1483
0
    return MUST(builder.to_string());
1484
0
}
1485
1486
Optional<CSSMathValue::ResolvedType> AcosCalculationNode::resolved_type() const
1487
0
{
1488
0
    return CSSMathValue::ResolvedType::Angle;
1489
0
}
1490
1491
// https://www.w3.org/TR/css-values-4/#determine-the-type-of-a-calculation
1492
Optional<CSSNumericType> AcosCalculationNode::determine_type(PropertyID) const
1493
0
{
1494
    // «[ "angle" → 1 ]».
1495
0
    return CSSNumericType { CSSNumericType::BaseType::Angle, 1 };
1496
0
}
1497
1498
bool AcosCalculationNode::contains_percentage() const
1499
0
{
1500
0
    return m_value->contains_percentage();
1501
0
}
1502
1503
CSSMathValue::CalculationResult AcosCalculationNode::resolve(Optional<Length::ResolutionContext const&> context, CSSMathValue::PercentageBasis const& percentage_basis) const
1504
0
{
1505
0
    auto node_a = m_value->resolve(context, percentage_basis);
1506
0
    auto node_a_value = resolve_value(node_a.value(), context);
1507
0
    auto result = acos(node_a_value);
1508
1509
0
    return { Angle(result, Angle::Type::Rad) };
1510
0
}
1511
1512
void AcosCalculationNode::for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const& callback)
1513
0
{
1514
0
    m_value->for_each_child_node(callback);
1515
0
    callback(m_value);
1516
0
}
1517
1518
void AcosCalculationNode::dump(StringBuilder& builder, int indent) const
1519
0
{
1520
0
    builder.appendff("{: >{}}ACOS: {}\n", "", indent, to_string());
1521
0
}
1522
1523
bool AcosCalculationNode::equals(CalculationNode const& other) const
1524
0
{
1525
0
    if (this == &other)
1526
0
        return true;
1527
0
    if (type() != other.type())
1528
0
        return false;
1529
0
    return m_value->equals(*static_cast<AcosCalculationNode const&>(other).m_value);
1530
0
}
1531
1532
NonnullOwnPtr<AtanCalculationNode> AtanCalculationNode::create(NonnullOwnPtr<CalculationNode> value)
1533
0
{
1534
0
    return adopt_own(*new (nothrow) AtanCalculationNode(move(value)));
1535
0
}
1536
1537
AtanCalculationNode::AtanCalculationNode(NonnullOwnPtr<CalculationNode> value)
1538
0
    : CalculationNode(Type::Atan)
1539
0
    , m_value(move(value))
1540
0
{
1541
0
}
1542
1543
0
AtanCalculationNode::~AtanCalculationNode() = default;
1544
1545
String AtanCalculationNode::to_string() const
1546
0
{
1547
0
    StringBuilder builder;
1548
0
    builder.append("atan("sv);
1549
0
    builder.append(m_value->to_string());
1550
0
    builder.append(")"sv);
1551
0
    return MUST(builder.to_string());
1552
0
}
1553
1554
Optional<CSSMathValue::ResolvedType> AtanCalculationNode::resolved_type() const
1555
0
{
1556
0
    return CSSMathValue::ResolvedType::Angle;
1557
0
}
1558
1559
// https://www.w3.org/TR/css-values-4/#determine-the-type-of-a-calculation
1560
Optional<CSSNumericType> AtanCalculationNode::determine_type(PropertyID) const
1561
0
{
1562
    // «[ "angle" → 1 ]».
1563
0
    return CSSNumericType { CSSNumericType::BaseType::Angle, 1 };
1564
0
}
1565
1566
bool AtanCalculationNode::contains_percentage() const
1567
0
{
1568
0
    return m_value->contains_percentage();
1569
0
}
1570
1571
CSSMathValue::CalculationResult AtanCalculationNode::resolve(Optional<Length::ResolutionContext const&> context, CSSMathValue::PercentageBasis const& percentage_basis) const
1572
0
{
1573
0
    auto node_a = m_value->resolve(context, percentage_basis);
1574
0
    auto node_a_value = resolve_value(node_a.value(), context);
1575
0
    auto result = atan(node_a_value);
1576
1577
0
    return { Angle(result, Angle::Type::Rad) };
1578
0
}
1579
1580
void AtanCalculationNode::for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const& callback)
1581
0
{
1582
0
    m_value->for_each_child_node(callback);
1583
0
    callback(m_value);
1584
0
}
1585
1586
void AtanCalculationNode::dump(StringBuilder& builder, int indent) const
1587
0
{
1588
0
    builder.appendff("{: >{}}ATAN: {}\n", "", indent, to_string());
1589
0
}
1590
1591
bool AtanCalculationNode::equals(CalculationNode const& other) const
1592
0
{
1593
0
    if (this == &other)
1594
0
        return true;
1595
0
    if (type() != other.type())
1596
0
        return false;
1597
0
    return m_value->equals(*static_cast<AtanCalculationNode const&>(other).m_value);
1598
0
}
1599
1600
NonnullOwnPtr<Atan2CalculationNode> Atan2CalculationNode::create(NonnullOwnPtr<CalculationNode> y, NonnullOwnPtr<CalculationNode> x)
1601
0
{
1602
0
    return adopt_own(*new (nothrow) Atan2CalculationNode(move(y), move(x)));
1603
0
}
1604
1605
Atan2CalculationNode::Atan2CalculationNode(NonnullOwnPtr<CalculationNode> y, NonnullOwnPtr<CalculationNode> x)
1606
0
    : CalculationNode(Type::Atan2)
1607
0
    , m_y(move(y))
1608
0
    , m_x(move(x))
1609
0
{
1610
0
}
1611
1612
0
Atan2CalculationNode::~Atan2CalculationNode() = default;
1613
1614
String Atan2CalculationNode::to_string() const
1615
0
{
1616
0
    StringBuilder builder;
1617
0
    builder.append("atan2("sv);
1618
0
    builder.append(m_y->to_string());
1619
0
    builder.append(", "sv);
1620
0
    builder.append(m_x->to_string());
1621
0
    builder.append(")"sv);
1622
0
    return MUST(builder.to_string());
1623
0
}
1624
1625
Optional<CSSMathValue::ResolvedType> Atan2CalculationNode::resolved_type() const
1626
0
{
1627
0
    return CSSMathValue::ResolvedType::Angle;
1628
0
}
1629
1630
// https://www.w3.org/TR/css-values-4/#determine-the-type-of-a-calculation
1631
Optional<CSSNumericType> Atan2CalculationNode::determine_type(PropertyID) const
1632
0
{
1633
    // «[ "angle" → 1 ]».
1634
0
    return CSSNumericType { CSSNumericType::BaseType::Angle, 1 };
1635
0
}
1636
1637
bool Atan2CalculationNode::contains_percentage() const
1638
0
{
1639
0
    return m_y->contains_percentage() || m_x->contains_percentage();
1640
0
}
1641
1642
CSSMathValue::CalculationResult Atan2CalculationNode::resolve(Optional<Length::ResolutionContext const&> context, CSSMathValue::PercentageBasis const& percentage_basis) const
1643
0
{
1644
0
    auto node_a = m_y->resolve(context, percentage_basis);
1645
0
    auto node_a_value = resolve_value(node_a.value(), context);
1646
1647
0
    auto node_b = m_x->resolve(context, percentage_basis);
1648
0
    auto node_b_value = resolve_value(node_b.value(), context);
1649
1650
0
    auto result = atan2(node_a_value, node_b_value);
1651
1652
0
    return { Angle(result, Angle::Type::Rad) };
1653
0
}
1654
1655
void Atan2CalculationNode::for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const& callback)
1656
0
{
1657
0
    m_y->for_each_child_node(callback);
1658
0
    m_x->for_each_child_node(callback);
1659
0
    callback(m_y);
1660
0
    callback(m_x);
1661
0
}
1662
1663
void Atan2CalculationNode::dump(StringBuilder& builder, int indent) const
1664
0
{
1665
0
    builder.appendff("{: >{}}ATAN2: {}\n", "", indent, to_string());
1666
0
}
1667
1668
bool Atan2CalculationNode::equals(CalculationNode const& other) const
1669
0
{
1670
0
    if (this == &other)
1671
0
        return true;
1672
0
    if (type() != other.type())
1673
0
        return false;
1674
0
    return m_x->equals(*static_cast<Atan2CalculationNode const&>(other).m_x)
1675
0
        && m_y->equals(*static_cast<Atan2CalculationNode const&>(other).m_y);
1676
0
}
1677
1678
NonnullOwnPtr<PowCalculationNode> PowCalculationNode::create(NonnullOwnPtr<CalculationNode> x, NonnullOwnPtr<CalculationNode> y)
1679
0
{
1680
0
    return adopt_own(*new (nothrow) PowCalculationNode(move(x), move(y)));
1681
0
}
1682
1683
PowCalculationNode::PowCalculationNode(NonnullOwnPtr<CalculationNode> x, NonnullOwnPtr<CalculationNode> y)
1684
0
    : CalculationNode(Type::Pow)
1685
0
    , m_x(move(x))
1686
0
    , m_y(move(y))
1687
0
{
1688
0
}
1689
1690
0
PowCalculationNode::~PowCalculationNode() = default;
1691
1692
String PowCalculationNode::to_string() const
1693
0
{
1694
0
    StringBuilder builder;
1695
0
    builder.append("pow("sv);
1696
0
    builder.append(m_x->to_string());
1697
0
    builder.append(", "sv);
1698
0
    builder.append(m_y->to_string());
1699
0
    builder.append(")"sv);
1700
0
    return MUST(builder.to_string());
1701
0
}
1702
1703
Optional<CSSMathValue::ResolvedType> PowCalculationNode::resolved_type() const
1704
0
{
1705
0
    return CSSMathValue::ResolvedType::Number;
1706
0
}
1707
1708
// https://www.w3.org/TR/css-values-4/#determine-the-type-of-a-calculation
1709
Optional<CSSNumericType> PowCalculationNode::determine_type(PropertyID) const
1710
0
{
1711
    // «[ ]» (empty map).
1712
0
    return CSSNumericType {};
1713
0
}
1714
1715
CSSMathValue::CalculationResult PowCalculationNode::resolve(Optional<Length::ResolutionContext const&> context, CSSMathValue::PercentageBasis const& percentage_basis) const
1716
0
{
1717
0
    auto node_a = m_x->resolve(context, percentage_basis);
1718
0
    auto node_a_value = resolve_value(node_a.value(), context);
1719
1720
0
    auto node_b = m_y->resolve(context, percentage_basis);
1721
0
    auto node_b_value = resolve_value(node_b.value(), context);
1722
1723
0
    auto result = pow(node_a_value, node_b_value);
1724
1725
0
    return { Number(Number::Type::Number, result) };
1726
0
}
1727
1728
void PowCalculationNode::for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const& callback)
1729
0
{
1730
0
    m_x->for_each_child_node(callback);
1731
0
    m_y->for_each_child_node(callback);
1732
0
    callback(m_x);
1733
0
    callback(m_y);
1734
0
}
1735
1736
void PowCalculationNode::dump(StringBuilder& builder, int indent) const
1737
0
{
1738
0
    builder.appendff("{: >{}}POW: {}\n", "", indent, to_string());
1739
0
}
1740
1741
bool PowCalculationNode::equals(CalculationNode const& other) const
1742
0
{
1743
0
    if (this == &other)
1744
0
        return true;
1745
0
    if (type() != other.type())
1746
0
        return false;
1747
0
    return m_x->equals(*static_cast<PowCalculationNode const&>(other).m_x)
1748
0
        && m_y->equals(*static_cast<PowCalculationNode const&>(other).m_y);
1749
0
}
1750
1751
NonnullOwnPtr<SqrtCalculationNode> SqrtCalculationNode::create(NonnullOwnPtr<CalculationNode> value)
1752
0
{
1753
0
    return adopt_own(*new (nothrow) SqrtCalculationNode(move(value)));
1754
0
}
1755
1756
SqrtCalculationNode::SqrtCalculationNode(NonnullOwnPtr<CalculationNode> value)
1757
0
    : CalculationNode(Type::Sqrt)
1758
0
    , m_value(move(value))
1759
0
{
1760
0
}
1761
1762
0
SqrtCalculationNode::~SqrtCalculationNode() = default;
1763
1764
String SqrtCalculationNode::to_string() const
1765
0
{
1766
0
    StringBuilder builder;
1767
0
    builder.append("sqrt("sv);
1768
0
    builder.append(m_value->to_string());
1769
0
    builder.append(")"sv);
1770
0
    return MUST(builder.to_string());
1771
0
}
1772
1773
Optional<CSSMathValue::ResolvedType> SqrtCalculationNode::resolved_type() const
1774
0
{
1775
0
    return CSSMathValue::ResolvedType::Number;
1776
0
}
1777
1778
// https://www.w3.org/TR/css-values-4/#determine-the-type-of-a-calculation
1779
Optional<CSSNumericType> SqrtCalculationNode::determine_type(PropertyID) const
1780
0
{
1781
    // «[ ]» (empty map).
1782
0
    return CSSNumericType {};
1783
0
}
1784
1785
CSSMathValue::CalculationResult SqrtCalculationNode::resolve(Optional<Length::ResolutionContext const&> context, CSSMathValue::PercentageBasis const& percentage_basis) const
1786
0
{
1787
0
    auto node_a = m_value->resolve(context, percentage_basis);
1788
0
    auto node_a_value = resolve_value(node_a.value(), context);
1789
0
    auto result = sqrt(node_a_value);
1790
1791
0
    return { Number(Number::Type::Number, result) };
1792
0
}
1793
1794
void SqrtCalculationNode::for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const& callback)
1795
0
{
1796
0
    m_value->for_each_child_node(callback);
1797
0
    callback(m_value);
1798
0
}
1799
1800
void SqrtCalculationNode::dump(StringBuilder& builder, int indent) const
1801
0
{
1802
0
    builder.appendff("{: >{}}SQRT: {}\n", "", indent, to_string());
1803
0
}
1804
1805
bool SqrtCalculationNode::equals(CalculationNode const& other) const
1806
0
{
1807
0
    if (this == &other)
1808
0
        return true;
1809
0
    if (type() != other.type())
1810
0
        return false;
1811
0
    return m_value->equals(*static_cast<SqrtCalculationNode const&>(other).m_value);
1812
0
}
1813
1814
NonnullOwnPtr<HypotCalculationNode> HypotCalculationNode::create(Vector<NonnullOwnPtr<Web::CSS::CalculationNode>> values)
1815
0
{
1816
0
    return adopt_own(*new (nothrow) HypotCalculationNode(move(values)));
1817
0
}
1818
1819
HypotCalculationNode::HypotCalculationNode(Vector<NonnullOwnPtr<CalculationNode>> values)
1820
0
    : CalculationNode(Type::Hypot)
1821
0
    , m_values(move(values))
1822
0
{
1823
0
}
1824
1825
0
HypotCalculationNode::~HypotCalculationNode() = default;
1826
1827
String HypotCalculationNode::to_string() const
1828
0
{
1829
0
    StringBuilder builder;
1830
0
    builder.append("hypot("sv);
1831
0
    for (size_t i = 0; i < m_values.size(); ++i) {
1832
0
        if (i != 0)
1833
0
            builder.append(", "sv);
1834
0
        builder.append(m_values[i]->to_string());
1835
0
    }
1836
0
    builder.append(")"sv);
1837
0
    return MUST(builder.to_string());
1838
0
}
1839
1840
Optional<CSSMathValue::ResolvedType> HypotCalculationNode::resolved_type() const
1841
0
{
1842
    // NOTE: We check during parsing that all values have the same type.
1843
0
    return m_values[0]->resolved_type();
1844
0
}
1845
1846
// https://www.w3.org/TR/css-values-4/#determine-the-type-of-a-calculation
1847
Optional<CSSNumericType> HypotCalculationNode::determine_type(PropertyID property_id) const
1848
0
{
1849
    // The result of adding the types of its comma-separated calculations.
1850
0
    return add_the_types(m_values, property_id);
1851
0
}
1852
1853
bool HypotCalculationNode::contains_percentage() const
1854
0
{
1855
0
    for (auto const& value : m_values) {
1856
0
        if (value->contains_percentage())
1857
0
            return true;
1858
0
    }
1859
1860
0
    return false;
1861
0
}
1862
1863
CSSMathValue::CalculationResult HypotCalculationNode::resolve(Optional<Length::ResolutionContext const&> context, CSSMathValue::PercentageBasis const& percentage_basis) const
1864
0
{
1865
0
    double square_sum = 0.0;
1866
1867
0
    for (auto const& value : m_values) {
1868
0
        auto child_resolved = value->resolve(context, percentage_basis);
1869
0
        auto child_value = resolve_value(child_resolved.value(), context);
1870
1871
0
        square_sum += child_value * child_value;
1872
0
    }
1873
1874
0
    auto result = sqrt(square_sum);
1875
1876
0
    return to_resolved_type(resolved_type().value(), result);
1877
0
}
1878
1879
void HypotCalculationNode::for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const& callback)
1880
0
{
1881
0
    for (auto& value : m_values) {
1882
0
        value->for_each_child_node(callback);
1883
0
        callback(value);
1884
0
    }
1885
0
}
1886
1887
void HypotCalculationNode::dump(StringBuilder& builder, int indent) const
1888
0
{
1889
0
    builder.appendff("{: >{}}HYPOT:\n", "", indent);
1890
0
    for (auto const& value : m_values)
1891
0
        value->dump(builder, indent + 2);
1892
0
}
1893
1894
bool HypotCalculationNode::equals(CalculationNode const& other) const
1895
0
{
1896
0
    if (this == &other)
1897
0
        return true;
1898
0
    if (type() != other.type())
1899
0
        return false;
1900
0
    for (size_t i = 0; i < m_values.size(); ++i) {
1901
0
        if (!m_values[i]->equals(*static_cast<HypotCalculationNode const&>(other).m_values[i]))
1902
0
            return false;
1903
0
    }
1904
0
    return true;
1905
0
}
1906
1907
NonnullOwnPtr<LogCalculationNode> LogCalculationNode::create(NonnullOwnPtr<CalculationNode> x, NonnullOwnPtr<CalculationNode> y)
1908
0
{
1909
0
    return adopt_own(*new (nothrow) LogCalculationNode(move(x), move(y)));
1910
0
}
1911
1912
LogCalculationNode::LogCalculationNode(NonnullOwnPtr<CalculationNode> x, NonnullOwnPtr<CalculationNode> y)
1913
0
    : CalculationNode(Type::Log)
1914
0
    , m_x(move(x))
1915
0
    , m_y(move(y))
1916
0
{
1917
0
}
1918
1919
0
LogCalculationNode::~LogCalculationNode() = default;
1920
1921
String LogCalculationNode::to_string() const
1922
0
{
1923
0
    StringBuilder builder;
1924
0
    builder.append("log("sv);
1925
0
    builder.append(m_x->to_string());
1926
0
    builder.append(", "sv);
1927
0
    builder.append(m_y->to_string());
1928
0
    builder.append(")"sv);
1929
0
    return MUST(builder.to_string());
1930
0
}
1931
1932
Optional<CSSMathValue::ResolvedType> LogCalculationNode::resolved_type() const
1933
0
{
1934
0
    return CSSMathValue::ResolvedType::Number;
1935
0
}
1936
1937
// https://www.w3.org/TR/css-values-4/#determine-the-type-of-a-calculation
1938
Optional<CSSNumericType> LogCalculationNode::determine_type(PropertyID) const
1939
0
{
1940
    // «[ ]» (empty map).
1941
0
    return CSSNumericType {};
1942
0
}
1943
1944
CSSMathValue::CalculationResult LogCalculationNode::resolve(Optional<Length::ResolutionContext const&> context, CSSMathValue::PercentageBasis const& percentage_basis) const
1945
0
{
1946
0
    auto node_a = m_x->resolve(context, percentage_basis);
1947
0
    auto node_a_value = resolve_value(node_a.value(), context);
1948
1949
0
    auto node_b = m_y->resolve(context, percentage_basis);
1950
0
    auto node_b_value = resolve_value(node_b.value(), context);
1951
1952
0
    auto result = log2(node_a_value) / log2(node_b_value);
1953
1954
0
    return { Number(Number::Type::Number, result) };
1955
0
}
1956
1957
void LogCalculationNode::for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const& callback)
1958
0
{
1959
0
    m_x->for_each_child_node(callback);
1960
0
    m_y->for_each_child_node(callback);
1961
0
    callback(m_x);
1962
0
    callback(m_y);
1963
0
}
1964
1965
void LogCalculationNode::dump(StringBuilder& builder, int indent) const
1966
0
{
1967
0
    builder.appendff("{: >{}}LOG: {}\n", "", indent, to_string());
1968
0
}
1969
1970
bool LogCalculationNode::equals(CalculationNode const& other) const
1971
0
{
1972
0
    if (this == &other)
1973
0
        return true;
1974
0
    if (type() != other.type())
1975
0
        return false;
1976
0
    return m_x->equals(*static_cast<LogCalculationNode const&>(other).m_x)
1977
0
        && m_y->equals(*static_cast<LogCalculationNode const&>(other).m_y);
1978
0
}
1979
1980
NonnullOwnPtr<ExpCalculationNode> ExpCalculationNode::create(NonnullOwnPtr<CalculationNode> value)
1981
0
{
1982
0
    return adopt_own(*new (nothrow) ExpCalculationNode(move(value)));
1983
0
}
1984
1985
ExpCalculationNode::ExpCalculationNode(NonnullOwnPtr<CalculationNode> value)
1986
0
    : CalculationNode(Type::Exp)
1987
0
    , m_value(move(value))
1988
0
{
1989
0
}
1990
1991
0
ExpCalculationNode::~ExpCalculationNode() = default;
1992
1993
String ExpCalculationNode::to_string() const
1994
0
{
1995
0
    StringBuilder builder;
1996
0
    builder.append("exp("sv);
1997
0
    builder.append(m_value->to_string());
1998
0
    builder.append(")"sv);
1999
0
    return MUST(builder.to_string());
2000
0
}
2001
2002
Optional<CSSMathValue::ResolvedType> ExpCalculationNode::resolved_type() const
2003
0
{
2004
0
    return CSSMathValue::ResolvedType::Number;
2005
0
}
2006
2007
// https://www.w3.org/TR/css-values-4/#determine-the-type-of-a-calculation
2008
Optional<CSSNumericType> ExpCalculationNode::determine_type(PropertyID) const
2009
0
{
2010
    // «[ ]» (empty map).
2011
0
    return CSSNumericType {};
2012
0
}
2013
2014
CSSMathValue::CalculationResult ExpCalculationNode::resolve(Optional<Length::ResolutionContext const&> context, CSSMathValue::PercentageBasis const& percentage_basis) const
2015
0
{
2016
0
    auto node_a = m_value->resolve(context, percentage_basis);
2017
0
    auto node_a_value = resolve_value(node_a.value(), context);
2018
0
    auto result = exp(node_a_value);
2019
2020
0
    return { Number(Number::Type::Number, result) };
2021
0
}
2022
2023
void ExpCalculationNode::for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const& callback)
2024
0
{
2025
0
    m_value->for_each_child_node(callback);
2026
0
    callback(m_value);
2027
0
}
2028
2029
void ExpCalculationNode::dump(StringBuilder& builder, int indent) const
2030
0
{
2031
0
    builder.appendff("{: >{}}EXP: {}\n", "", indent, to_string());
2032
0
}
2033
2034
bool ExpCalculationNode::equals(CalculationNode const& other) const
2035
0
{
2036
0
    if (this == &other)
2037
0
        return true;
2038
0
    if (type() != other.type())
2039
0
        return false;
2040
0
    return m_value->equals(*static_cast<ExpCalculationNode const&>(other).m_value);
2041
0
}
2042
2043
NonnullOwnPtr<RoundCalculationNode> RoundCalculationNode::create(RoundingStrategy strategy, NonnullOwnPtr<CalculationNode> x, NonnullOwnPtr<CalculationNode> y)
2044
0
{
2045
0
    return adopt_own(*new (nothrow) RoundCalculationNode(strategy, move(x), move(y)));
2046
0
}
2047
2048
RoundCalculationNode::RoundCalculationNode(RoundingStrategy mode, NonnullOwnPtr<CalculationNode> x, NonnullOwnPtr<CalculationNode> y)
2049
0
    : CalculationNode(Type::Round)
2050
0
    , m_strategy(mode)
2051
0
    , m_x(move(x))
2052
0
    , m_y(move(y))
2053
0
{
2054
0
}
2055
2056
0
RoundCalculationNode::~RoundCalculationNode() = default;
2057
2058
String RoundCalculationNode::to_string() const
2059
0
{
2060
0
    StringBuilder builder;
2061
0
    builder.append("round("sv);
2062
0
    builder.append(CSS::to_string(m_strategy));
2063
0
    builder.append(", "sv);
2064
0
    builder.append(m_x->to_string());
2065
0
    builder.append(", "sv);
2066
0
    builder.append(m_y->to_string());
2067
0
    builder.append(")"sv);
2068
0
    return MUST(builder.to_string());
2069
0
}
2070
2071
Optional<CSSMathValue::ResolvedType> RoundCalculationNode::resolved_type() const
2072
0
{
2073
    // Note: We check during parsing that all values have the same type
2074
0
    return m_x->resolved_type();
2075
0
}
2076
2077
// https://www.w3.org/TR/css-values-4/#determine-the-type-of-a-calculation
2078
Optional<CSSNumericType> RoundCalculationNode::determine_type(PropertyID property_id) const
2079
0
{
2080
    // The result of adding the types of its comma-separated calculations.
2081
0
    auto x_type = m_x->determine_type(property_id);
2082
0
    auto y_type = m_y->determine_type(property_id);
2083
2084
0
    if (!x_type.has_value() || !y_type.has_value())
2085
0
        return {};
2086
2087
0
    return x_type->added_to(*y_type);
2088
0
}
2089
2090
bool RoundCalculationNode::contains_percentage() const
2091
0
{
2092
0
    return m_x->contains_percentage() || m_y->contains_percentage();
2093
0
}
2094
2095
CSSMathValue::CalculationResult RoundCalculationNode::resolve(Optional<Length::ResolutionContext const&> context, CSSMathValue::PercentageBasis const& percentage_basis) const
2096
0
{
2097
0
    auto node_a = m_x->resolve(context, percentage_basis);
2098
0
    auto node_b = m_y->resolve(context, percentage_basis);
2099
2100
0
    auto node_a_value = resolve_value(node_a.value(), context);
2101
0
    auto node_b_value = resolve_value(node_b.value(), context);
2102
2103
0
    auto upper_b = ceil(node_a_value / node_b_value) * node_b_value;
2104
0
    auto lower_b = floor(node_a_value / node_b_value) * node_b_value;
2105
2106
0
    auto resolved_type = node_a.resolved_type();
2107
2108
0
    if (m_strategy == RoundingStrategy::Nearest) {
2109
0
        auto upper_diff = fabs(upper_b - node_a_value);
2110
0
        auto lower_diff = fabs(node_a_value - lower_b);
2111
0
        auto rounded_value = upper_diff < lower_diff ? upper_b : lower_b;
2112
0
        return to_resolved_type(resolved_type, rounded_value);
2113
0
    }
2114
2115
0
    if (m_strategy == RoundingStrategy::Up) {
2116
0
        return to_resolved_type(resolved_type, upper_b);
2117
0
    }
2118
2119
0
    if (m_strategy == RoundingStrategy::Down) {
2120
0
        return to_resolved_type(resolved_type, lower_b);
2121
0
    }
2122
2123
0
    if (m_strategy == RoundingStrategy::ToZero) {
2124
0
        auto upper_diff = fabs(upper_b);
2125
0
        auto lower_diff = fabs(lower_b);
2126
0
        auto rounded_value = upper_diff < lower_diff ? upper_b : lower_b;
2127
0
        return to_resolved_type(resolved_type, rounded_value);
2128
0
    }
2129
2130
0
    VERIFY_NOT_REACHED();
2131
0
}
2132
2133
void RoundCalculationNode::for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const& callback)
2134
0
{
2135
0
    m_x->for_each_child_node(callback);
2136
0
    m_y->for_each_child_node(callback);
2137
0
    callback(m_x);
2138
0
    callback(m_y);
2139
0
}
2140
2141
void RoundCalculationNode::dump(StringBuilder& builder, int indent) const
2142
0
{
2143
0
    builder.appendff("{: >{}}ROUND: {}\n", "", indent, to_string());
2144
0
}
2145
2146
bool RoundCalculationNode::equals(CalculationNode const& other) const
2147
0
{
2148
0
    if (this == &other)
2149
0
        return true;
2150
0
    if (type() != other.type())
2151
0
        return false;
2152
0
    return m_strategy == static_cast<RoundCalculationNode const&>(other).m_strategy
2153
0
        && m_x->equals(*static_cast<RoundCalculationNode const&>(other).m_x)
2154
0
        && m_y->equals(*static_cast<RoundCalculationNode const&>(other).m_y);
2155
0
}
2156
2157
NonnullOwnPtr<ModCalculationNode> ModCalculationNode::create(NonnullOwnPtr<CalculationNode> x, NonnullOwnPtr<CalculationNode> y)
2158
0
{
2159
0
    return adopt_own(*new (nothrow) ModCalculationNode(move(x), move(y)));
2160
0
}
2161
2162
ModCalculationNode::ModCalculationNode(NonnullOwnPtr<CalculationNode> x, NonnullOwnPtr<CalculationNode> y)
2163
0
    : CalculationNode(Type::Mod)
2164
0
    , m_x(move(x))
2165
0
    , m_y(move(y))
2166
0
{
2167
0
}
2168
2169
0
ModCalculationNode::~ModCalculationNode() = default;
2170
2171
String ModCalculationNode::to_string() const
2172
0
{
2173
0
    StringBuilder builder;
2174
0
    builder.append("mod("sv);
2175
0
    builder.append(m_x->to_string());
2176
0
    builder.append(", "sv);
2177
0
    builder.append(m_y->to_string());
2178
0
    builder.append(")"sv);
2179
0
    return MUST(builder.to_string());
2180
0
}
2181
2182
Optional<CSSMathValue::ResolvedType> ModCalculationNode::resolved_type() const
2183
0
{
2184
    // Note: We check during parsing that all values have the same type
2185
0
    return m_x->resolved_type();
2186
0
}
2187
2188
// https://www.w3.org/TR/css-values-4/#determine-the-type-of-a-calculation
2189
Optional<CSSNumericType> ModCalculationNode::determine_type(PropertyID property_id) const
2190
0
{
2191
    // The result of adding the types of its comma-separated calculations.
2192
0
    auto x_type = m_x->determine_type(property_id);
2193
0
    auto y_type = m_y->determine_type(property_id);
2194
2195
0
    if (!x_type.has_value() || !y_type.has_value())
2196
0
        return {};
2197
2198
0
    return x_type->added_to(*y_type);
2199
0
}
2200
2201
bool ModCalculationNode::contains_percentage() const
2202
0
{
2203
0
    return m_x->contains_percentage() || m_y->contains_percentage();
2204
0
}
2205
2206
CSSMathValue::CalculationResult ModCalculationNode::resolve(Optional<Length::ResolutionContext const&> context, CSSMathValue::PercentageBasis const& percentage_basis) const
2207
0
{
2208
0
    auto resolved_type = m_x->resolved_type().value();
2209
0
    auto node_a = m_x->resolve(context, percentage_basis);
2210
0
    auto node_b = m_y->resolve(context, percentage_basis);
2211
2212
0
    auto node_a_value = resolve_value(node_a.value(), context);
2213
0
    auto node_b_value = resolve_value(node_b.value(), context);
2214
2215
0
    auto quotient = floor(node_a_value / node_b_value);
2216
0
    auto value = node_a_value - (node_b_value * quotient);
2217
0
    return to_resolved_type(resolved_type, value);
2218
0
}
2219
2220
void ModCalculationNode::for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const& callback)
2221
0
{
2222
0
    m_x->for_each_child_node(callback);
2223
0
    m_y->for_each_child_node(callback);
2224
0
    callback(m_x);
2225
0
    callback(m_y);
2226
0
}
2227
2228
void ModCalculationNode::dump(StringBuilder& builder, int indent) const
2229
0
{
2230
0
    builder.appendff("{: >{}}MOD: {}\n", "", indent, to_string());
2231
0
}
2232
2233
bool ModCalculationNode::equals(CalculationNode const& other) const
2234
0
{
2235
0
    if (this == &other)
2236
0
        return true;
2237
0
    if (type() != other.type())
2238
0
        return false;
2239
0
    return m_x->equals(*static_cast<ModCalculationNode const&>(other).m_x)
2240
0
        && m_y->equals(*static_cast<ModCalculationNode const&>(other).m_y);
2241
0
}
2242
2243
NonnullOwnPtr<RemCalculationNode> RemCalculationNode::create(NonnullOwnPtr<CalculationNode> x, NonnullOwnPtr<CalculationNode> y)
2244
0
{
2245
0
    return adopt_own(*new (nothrow) RemCalculationNode(move(x), move(y)));
2246
0
}
2247
2248
RemCalculationNode::RemCalculationNode(NonnullOwnPtr<CalculationNode> x, NonnullOwnPtr<CalculationNode> y)
2249
0
    : CalculationNode(Type::Rem)
2250
0
    , m_x(move(x))
2251
0
    , m_y(move(y))
2252
0
{
2253
0
}
2254
2255
0
RemCalculationNode::~RemCalculationNode() = default;
2256
2257
String RemCalculationNode::to_string() const
2258
0
{
2259
0
    StringBuilder builder;
2260
0
    builder.append("rem("sv);
2261
0
    builder.append(m_x->to_string());
2262
0
    builder.append(", "sv);
2263
0
    builder.append(m_y->to_string());
2264
0
    builder.append(")"sv);
2265
0
    return MUST(builder.to_string());
2266
0
}
2267
2268
Optional<CSSMathValue::ResolvedType> RemCalculationNode::resolved_type() const
2269
0
{
2270
    // Note: We check during parsing that all values have the same type
2271
0
    return m_x->resolved_type();
2272
0
}
2273
2274
// https://www.w3.org/TR/css-values-4/#determine-the-type-of-a-calculation
2275
Optional<CSSNumericType> RemCalculationNode::determine_type(PropertyID property_id) const
2276
0
{
2277
    // The result of adding the types of its comma-separated calculations.
2278
0
    auto x_type = m_x->determine_type(property_id);
2279
0
    auto y_type = m_y->determine_type(property_id);
2280
2281
0
    if (!x_type.has_value() || !y_type.has_value())
2282
0
        return {};
2283
2284
0
    return x_type->added_to(*y_type);
2285
0
}
2286
2287
bool RemCalculationNode::contains_percentage() const
2288
0
{
2289
0
    return m_x->contains_percentage() || m_y->contains_percentage();
2290
0
}
2291
2292
CSSMathValue::CalculationResult RemCalculationNode::resolve(Optional<Length::ResolutionContext const&> context, CSSMathValue::PercentageBasis const& percentage_basis) const
2293
0
{
2294
0
    auto resolved_type = m_x->resolved_type().value();
2295
0
    auto node_a = m_x->resolve(context, percentage_basis);
2296
0
    auto node_b = m_y->resolve(context, percentage_basis);
2297
2298
0
    auto node_a_value = resolve_value(node_a.value(), context);
2299
0
    auto node_b_value = resolve_value(node_b.value(), context);
2300
2301
0
    auto value = fmod(node_a_value, node_b_value);
2302
0
    return to_resolved_type(resolved_type, value);
2303
0
}
2304
2305
void RemCalculationNode::for_each_child_node(Function<void(NonnullOwnPtr<CalculationNode>&)> const& callback)
2306
0
{
2307
0
    m_x->for_each_child_node(callback);
2308
0
    m_y->for_each_child_node(callback);
2309
0
    callback(m_x);
2310
0
    callback(m_y);
2311
0
}
2312
2313
void RemCalculationNode::dump(StringBuilder& builder, int indent) const
2314
0
{
2315
0
    builder.appendff("{: >{}}REM: {}\n", "", indent, to_string());
2316
0
}
2317
2318
bool RemCalculationNode::equals(CalculationNode const& other) const
2319
0
{
2320
0
    if (this == &other)
2321
0
        return true;
2322
0
    if (type() != other.type())
2323
0
        return false;
2324
0
    return m_x->equals(*static_cast<RemCalculationNode const&>(other).m_x)
2325
0
        && m_y->equals(*static_cast<RemCalculationNode const&>(other).m_y);
2326
0
}
2327
2328
void CSSMathValue::CalculationResult::add(CalculationResult const& other, Optional<Length::ResolutionContext const&> context, PercentageBasis const& percentage_basis)
2329
0
{
2330
0
    add_or_subtract_internal(SumOperation::Add, other, context, percentage_basis);
2331
0
}
2332
2333
void CSSMathValue::CalculationResult::subtract(CalculationResult const& other, Optional<Length::ResolutionContext const&> context, PercentageBasis const& percentage_basis)
2334
0
{
2335
0
    add_or_subtract_internal(SumOperation::Subtract, other, context, percentage_basis);
2336
0
}
2337
2338
void CSSMathValue::CalculationResult::add_or_subtract_internal(SumOperation op, CalculationResult const& other, Optional<Length::ResolutionContext const&> context, PercentageBasis const& percentage_basis)
2339
0
{
2340
    // We know from validation when resolving the type, that "both sides have the same type, or that one side is a <number> and the other is an <integer>".
2341
    // Though, having the same type may mean that one side is a <dimension> and the other a <percentage>.
2342
    // Note: This is almost identical to ::add()
2343
2344
0
    m_value.visit(
2345
0
        [&](Number const& number) {
2346
0
            auto other_number = other.m_value.get<Number>();
2347
0
            if (op == SumOperation::Add) {
2348
0
                m_value = number + other_number;
2349
0
            } else {
2350
0
                m_value = number - other_number;
2351
0
            }
2352
0
        },
2353
0
        [&](Angle const& angle) {
2354
0
            auto this_degrees = angle.to_degrees();
2355
0
            if (other.m_value.has<Angle>()) {
2356
0
                auto other_degrees = other.m_value.get<Angle>().to_degrees();
2357
0
                if (op == SumOperation::Add)
2358
0
                    m_value = Angle::make_degrees(this_degrees + other_degrees);
2359
0
                else
2360
0
                    m_value = Angle::make_degrees(this_degrees - other_degrees);
2361
0
            } else {
2362
0
                VERIFY(percentage_basis.has<Angle>());
2363
2364
0
                auto other_degrees = percentage_basis.get<Angle>().percentage_of(other.m_value.get<Percentage>()).to_degrees();
2365
0
                if (op == SumOperation::Add)
2366
0
                    m_value = Angle::make_degrees(this_degrees + other_degrees);
2367
0
                else
2368
0
                    m_value = Angle::make_degrees(this_degrees - other_degrees);
2369
0
            }
2370
0
        },
2371
0
        [&](Flex const& flex) {
2372
0
            auto this_fr = flex.to_fr();
2373
0
            if (other.m_value.has<Flex>()) {
2374
0
                auto other_fr = other.m_value.get<Flex>().to_fr();
2375
0
                if (op == SumOperation::Add)
2376
0
                    m_value = Flex::make_fr(this_fr + other_fr);
2377
0
                else
2378
0
                    m_value = Flex::make_fr(this_fr - other_fr);
2379
0
            } else {
2380
0
                VERIFY(percentage_basis.has<Flex>());
2381
2382
0
                auto other_fr = percentage_basis.get<Flex>().percentage_of(other.m_value.get<Percentage>()).to_fr();
2383
0
                if (op == SumOperation::Add)
2384
0
                    m_value = Flex::make_fr(this_fr + other_fr);
2385
0
                else
2386
0
                    m_value = Flex::make_fr(this_fr - other_fr);
2387
0
            }
2388
0
        },
2389
0
        [&](Frequency const& frequency) {
2390
0
            auto this_hertz = frequency.to_hertz();
2391
0
            if (other.m_value.has<Frequency>()) {
2392
0
                auto other_hertz = other.m_value.get<Frequency>().to_hertz();
2393
0
                if (op == SumOperation::Add)
2394
0
                    m_value = Frequency::make_hertz(this_hertz + other_hertz);
2395
0
                else
2396
0
                    m_value = Frequency::make_hertz(this_hertz - other_hertz);
2397
0
            } else {
2398
0
                VERIFY(percentage_basis.has<Frequency>());
2399
2400
0
                auto other_hertz = percentage_basis.get<Frequency>().percentage_of(other.m_value.get<Percentage>()).to_hertz();
2401
0
                if (op == SumOperation::Add)
2402
0
                    m_value = Frequency::make_hertz(this_hertz + other_hertz);
2403
0
                else
2404
0
                    m_value = Frequency::make_hertz(this_hertz - other_hertz);
2405
0
            }
2406
0
        },
2407
0
        [&](Length const& length) {
2408
0
            if (!context.has_value()) {
2409
0
                dbgln("CSSMathValue::CalculationResult::add_or_subtract_internal: Length without context");
2410
0
                m_value = Length::make_px(0);
2411
0
                return;
2412
0
            }
2413
2414
0
            auto this_px = length.to_px(*context);
2415
0
            if (other.m_value.has<Length>()) {
2416
0
                auto other_px = other.m_value.get<Length>().to_px(*context);
2417
0
                if (op == SumOperation::Add)
2418
0
                    m_value = Length::make_px(this_px + other_px);
2419
0
                else
2420
0
                    m_value = Length::make_px(this_px - other_px);
2421
0
            } else {
2422
0
                VERIFY(percentage_basis.has<Length>());
2423
2424
0
                auto other_px = percentage_basis.get<Length>().percentage_of(other.m_value.get<Percentage>()).to_px(*context);
2425
0
                if (op == SumOperation::Add)
2426
0
                    m_value = Length::make_px(this_px + other_px);
2427
0
                else
2428
0
                    m_value = Length::make_px(this_px - other_px);
2429
0
            }
2430
0
        },
2431
0
        [&](Resolution const& resolution) {
2432
0
            auto this_dots_per_pixel = resolution.to_dots_per_pixel();
2433
            // NOTE: <resolution-percentage> is not a type, so we don't have to worry about percentages.
2434
0
            auto other_dots_per_pixel = other.m_value.get<Resolution>().to_dots_per_pixel();
2435
0
            if (op == SumOperation::Add)
2436
0
                m_value = Resolution::make_dots_per_pixel(this_dots_per_pixel + other_dots_per_pixel);
2437
0
            else
2438
0
                m_value = Resolution::make_dots_per_pixel(this_dots_per_pixel - other_dots_per_pixel);
2439
0
        },
2440
0
        [&](Time const& time) {
2441
0
            auto this_seconds = time.to_seconds();
2442
0
            if (other.m_value.has<Time>()) {
2443
0
                auto other_seconds = other.m_value.get<Time>().to_seconds();
2444
0
                if (op == SumOperation::Add)
2445
0
                    m_value = Time::make_seconds(this_seconds + other_seconds);
2446
0
                else
2447
0
                    m_value = Time::make_seconds(this_seconds - other_seconds);
2448
0
            } else {
2449
0
                VERIFY(percentage_basis.has<Time>());
2450
2451
0
                auto other_seconds = percentage_basis.get<Time>().percentage_of(other.m_value.get<Percentage>()).to_seconds();
2452
0
                if (op == SumOperation::Add)
2453
0
                    m_value = Time::make_seconds(this_seconds + other_seconds);
2454
0
                else
2455
0
                    m_value = Time::make_seconds(this_seconds - other_seconds);
2456
0
            }
2457
0
        },
2458
0
        [&](Percentage const& percentage) {
2459
0
            if (other.m_value.has<Percentage>()) {
2460
0
                if (op == SumOperation::Add)
2461
0
                    m_value = Percentage { percentage.value() + other.m_value.get<Percentage>().value() };
2462
0
                else
2463
0
                    m_value = Percentage { percentage.value() - other.m_value.get<Percentage>().value() };
2464
0
                return;
2465
0
            }
2466
2467
            // Other side isn't a percentage, so the easiest way to handle it without duplicating all the logic, is just to swap `this` and `other`.
2468
0
            CalculationResult new_value = other;
2469
0
            if (op == SumOperation::Add) {
2470
0
                new_value.add(*this, context, percentage_basis);
2471
0
            } else {
2472
                // Turn 'this - other' into '-other + this', as 'A + B == B + A', but 'A - B != B - A'
2473
0
                new_value.multiply_by({ Number { Number::Type::Integer, -1.0f } }, context);
2474
0
                new_value.add(*this, context, percentage_basis);
2475
0
            }
2476
2477
0
            *this = new_value;
2478
0
        });
2479
0
}
2480
2481
void CSSMathValue::CalculationResult::multiply_by(CalculationResult const& other, Optional<Length::ResolutionContext const&> context)
2482
0
{
2483
    // We know from validation when resolving the type, that at least one side must be a <number> or <integer>.
2484
    // Both of these are represented as a double.
2485
0
    VERIFY(m_value.has<Number>() || other.m_value.has<Number>());
2486
0
    bool other_is_number = other.m_value.has<Number>();
2487
2488
0
    m_value.visit(
2489
0
        [&](Number const& number) {
2490
0
            if (other_is_number) {
2491
0
                m_value = number * other.m_value.get<Number>();
2492
0
            } else {
2493
                // Avoid duplicating all the logic by swapping `this` and `other`.
2494
0
                CalculationResult new_value = other;
2495
0
                new_value.multiply_by(*this, context);
2496
0
                *this = new_value;
2497
0
            }
2498
0
        },
2499
0
        [&](Angle const& angle) {
2500
0
            m_value = Angle::make_degrees(angle.to_degrees() * other.m_value.get<Number>().value());
2501
0
        },
2502
0
        [&](Flex const& flex) {
2503
0
            m_value = Flex::make_fr(flex.to_fr() * other.m_value.get<Number>().value());
2504
0
        },
2505
0
        [&](Frequency const& frequency) {
2506
0
            m_value = Frequency::make_hertz(frequency.to_hertz() * other.m_value.get<Number>().value());
2507
0
        },
2508
0
        [&](Length const& length) {
2509
0
            m_value = Length::make_px(CSSPixels::nearest_value_for(length.to_px(*context) * static_cast<double>(other.m_value.get<Number>().value())));
2510
0
        },
2511
0
        [&](Resolution const& resolution) {
2512
0
            m_value = Resolution::make_dots_per_pixel(resolution.to_dots_per_pixel() * other.m_value.get<Number>().value());
2513
0
        },
2514
0
        [&](Time const& time) {
2515
0
            m_value = Time::make_seconds(time.to_seconds() * other.m_value.get<Number>().value());
2516
0
        },
2517
0
        [&](Percentage const& percentage) {
2518
0
            m_value = Percentage { percentage.value() * other.m_value.get<Number>().value() };
2519
0
        });
2520
0
}
2521
2522
void CSSMathValue::CalculationResult::divide_by(CalculationResult const& other, Optional<Length::ResolutionContext const&> context)
2523
0
{
2524
    // We know from validation when resolving the type, that `other` must be a <number> or <integer>.
2525
    // Both of these are represented as a Number.
2526
0
    auto denominator = other.m_value.get<Number>().value();
2527
    // FIXME: Dividing by 0 is invalid, and should be caught during parsing.
2528
0
    VERIFY(denominator != 0.0);
2529
2530
0
    m_value.visit(
2531
0
        [&](Number const& number) {
2532
0
            m_value = Number {
2533
0
                Number::Type::Number,
2534
0
                number.value() / denominator
2535
0
            };
2536
0
        },
2537
0
        [&](Angle const& angle) {
2538
0
            m_value = Angle::make_degrees(angle.to_degrees() / denominator);
2539
0
        },
2540
0
        [&](Flex const& flex) {
2541
0
            m_value = Flex::make_fr(flex.to_fr() / denominator);
2542
0
        },
2543
0
        [&](Frequency const& frequency) {
2544
0
            m_value = Frequency::make_hertz(frequency.to_hertz() / denominator);
2545
0
        },
2546
0
        [&](Length const& length) {
2547
0
            m_value = Length::make_px(CSSPixels::nearest_value_for(length.to_px(*context) / static_cast<double>(denominator)));
2548
0
        },
2549
0
        [&](Resolution const& resolution) {
2550
0
            m_value = Resolution::make_dots_per_pixel(resolution.to_dots_per_pixel() / denominator);
2551
0
        },
2552
0
        [&](Time const& time) {
2553
0
            m_value = Time::make_seconds(time.to_seconds() / denominator);
2554
0
        },
2555
0
        [&](Percentage const& percentage) {
2556
0
            m_value = Percentage { percentage.value() / denominator };
2557
0
        });
2558
0
}
2559
2560
void CSSMathValue::CalculationResult::negate()
2561
0
{
2562
0
    m_value.visit(
2563
0
        [&](Number const& number) {
2564
0
            m_value = Number { number.type(), 0 - number.value() };
2565
0
        },
2566
0
        [&](Angle const& angle) {
2567
0
            m_value = Angle { 0 - angle.raw_value(), angle.type() };
2568
0
        },
2569
0
        [&](Flex const& flex) {
2570
0
            m_value = Flex { 0 - flex.raw_value(), flex.type() };
2571
0
        },
2572
0
        [&](Frequency const& frequency) {
2573
0
            m_value = Frequency { 0 - frequency.raw_value(), frequency.type() };
2574
0
        },
2575
0
        [&](Length const& length) {
2576
0
            m_value = Length { 0 - length.raw_value(), length.type() };
2577
0
        },
2578
0
        [&](Resolution const& resolution) {
2579
0
            m_value = Resolution { 0 - resolution.raw_value(), resolution.type() };
2580
0
        },
2581
0
        [&](Time const& time) {
2582
0
            m_value = Time { 0 - time.raw_value(), time.type() };
2583
0
        },
2584
0
        [&](Percentage const& percentage) {
2585
0
            m_value = Percentage { 0 - percentage.value() };
2586
0
        });
2587
0
}
2588
2589
void CSSMathValue::CalculationResult::invert()
2590
0
{
2591
    // FIXME: Correctly handle division by zero.
2592
0
    m_value.visit(
2593
0
        [&](Number const& number) {
2594
0
            m_value = Number { Number::Type::Number, 1 / number.value() };
2595
0
        },
2596
0
        [&](Angle const& angle) {
2597
0
            m_value = Angle { 1 / angle.raw_value(), angle.type() };
2598
0
        },
2599
0
        [&](Flex const& flex) {
2600
0
            m_value = Flex { 1 / flex.raw_value(), flex.type() };
2601
0
        },
2602
0
        [&](Frequency const& frequency) {
2603
0
            m_value = Frequency { 1 / frequency.raw_value(), frequency.type() };
2604
0
        },
2605
0
        [&](Length const& length) {
2606
0
            m_value = Length { 1 / length.raw_value(), length.type() };
2607
0
        },
2608
0
        [&](Resolution const& resolution) {
2609
0
            m_value = Resolution { 1 / resolution.raw_value(), resolution.type() };
2610
0
        },
2611
0
        [&](Time const& time) {
2612
0
            m_value = Time { 1 / time.raw_value(), time.type() };
2613
0
        },
2614
0
        [&](Percentage const& percentage) {
2615
0
            m_value = Percentage { 1 / percentage.value() };
2616
0
        });
2617
0
}
2618
2619
CSSMathValue::ResolvedType CSSMathValue::CalculationResult::resolved_type() const
2620
0
{
2621
0
    return m_value.visit(
2622
0
        [](Number const&) { return ResolvedType::Number; },
2623
0
        [](Angle const&) { return ResolvedType::Angle; },
2624
0
        [](Flex const&) { return ResolvedType::Flex; },
2625
0
        [](Frequency const&) { return ResolvedType::Frequency; },
2626
0
        [](Length const&) { return ResolvedType::Length; },
2627
0
        [](Percentage const&) { return ResolvedType::Percentage; },
2628
0
        [](Resolution const&) { return ResolvedType::Resolution; },
2629
0
        [](Time const&) { return ResolvedType::Time; });
2630
0
}
2631
2632
String CSSMathValue::to_string() const
2633
0
{
2634
    // FIXME: Implement this according to https://www.w3.org/TR/css-values-4/#calc-serialize once that stabilizes.
2635
0
    return MUST(String::formatted("calc({})", m_calculation->to_string()));
2636
0
}
2637
2638
bool CSSMathValue::equals(CSSStyleValue const& other) const
2639
0
{
2640
0
    if (type() != other.type())
2641
0
        return false;
2642
2643
0
    return m_calculation->equals(*static_cast<CSSMathValue const&>(other).m_calculation);
2644
0
}
2645
2646
Optional<Angle> CSSMathValue::resolve_angle() const
2647
0
{
2648
0
    auto result = m_calculation->resolve({}, {});
2649
2650
0
    if (result.value().has<Angle>())
2651
0
        return result.value().get<Angle>();
2652
0
    return {};
2653
0
}
2654
2655
Optional<Angle> CSSMathValue::resolve_angle(Layout::Node const& layout_node) const
2656
0
{
2657
0
    return resolve_angle(Length::ResolutionContext::for_layout_node(layout_node));
2658
0
}
2659
2660
Optional<Angle> CSSMathValue::resolve_angle(Length::ResolutionContext const& context) const
2661
0
{
2662
0
    auto result = m_calculation->resolve(context, {});
2663
2664
0
    if (result.value().has<Angle>())
2665
0
        return result.value().get<Angle>();
2666
0
    return {};
2667
0
}
2668
2669
Optional<Angle> CSSMathValue::resolve_angle_percentage(Angle const& percentage_basis) const
2670
0
{
2671
0
    auto result = m_calculation->resolve({}, percentage_basis);
2672
2673
0
    return result.value().visit(
2674
0
        [&](Angle const& angle) -> Optional<Angle> {
2675
0
            return angle;
2676
0
        },
2677
0
        [&](Percentage const& percentage) -> Optional<Angle> {
2678
0
            return percentage_basis.percentage_of(percentage);
2679
0
        },
2680
0
        [&](auto const&) -> Optional<Angle> {
2681
0
            return {};
2682
0
        });
Unexecuted instantiation: CSSMathValue.cpp:AK::Optional<Web::CSS::Angle> Web::CSS::CSSMathValue::resolve_angle_percentage(Web::CSS::Angle const&) const::$_2::operator()<Web::CSS::Number>(Web::CSS::Number const&) const
Unexecuted instantiation: CSSMathValue.cpp:AK::Optional<Web::CSS::Angle> Web::CSS::CSSMathValue::resolve_angle_percentage(Web::CSS::Angle const&) const::$_2::operator()<Web::CSS::Flex>(Web::CSS::Flex const&) const
Unexecuted instantiation: CSSMathValue.cpp:AK::Optional<Web::CSS::Angle> Web::CSS::CSSMathValue::resolve_angle_percentage(Web::CSS::Angle const&) const::$_2::operator()<Web::CSS::Frequency>(Web::CSS::Frequency const&) const
Unexecuted instantiation: CSSMathValue.cpp:AK::Optional<Web::CSS::Angle> Web::CSS::CSSMathValue::resolve_angle_percentage(Web::CSS::Angle const&) const::$_2::operator()<Web::CSS::Length>(Web::CSS::Length const&) const
Unexecuted instantiation: CSSMathValue.cpp:AK::Optional<Web::CSS::Angle> Web::CSS::CSSMathValue::resolve_angle_percentage(Web::CSS::Angle const&) const::$_2::operator()<Web::CSS::Resolution>(Web::CSS::Resolution const&) const
Unexecuted instantiation: CSSMathValue.cpp:AK::Optional<Web::CSS::Angle> Web::CSS::CSSMathValue::resolve_angle_percentage(Web::CSS::Angle const&) const::$_2::operator()<Web::CSS::Time>(Web::CSS::Time const&) const
2683
0
}
2684
2685
Optional<Flex> CSSMathValue::resolve_flex() const
2686
0
{
2687
0
    auto result = m_calculation->resolve({}, {});
2688
2689
0
    if (result.value().has<Flex>())
2690
0
        return result.value().get<Flex>();
2691
0
    return {};
2692
0
}
2693
2694
Optional<Frequency> CSSMathValue::resolve_frequency() const
2695
0
{
2696
0
    auto result = m_calculation->resolve({}, {});
2697
2698
0
    if (result.value().has<Frequency>())
2699
0
        return result.value().get<Frequency>();
2700
0
    return {};
2701
0
}
2702
2703
Optional<Frequency> CSSMathValue::resolve_frequency_percentage(Frequency const& percentage_basis) const
2704
0
{
2705
0
    auto result = m_calculation->resolve({}, percentage_basis);
2706
2707
0
    return result.value().visit(
2708
0
        [&](Frequency const& frequency) -> Optional<Frequency> {
2709
0
            return frequency;
2710
0
        },
2711
0
        [&](Percentage const& percentage) -> Optional<Frequency> {
2712
0
            return percentage_basis.percentage_of(percentage);
2713
0
        },
2714
0
        [&](auto const&) -> Optional<Frequency> {
2715
0
            return {};
2716
0
        });
Unexecuted instantiation: CSSMathValue.cpp:AK::Optional<Web::CSS::Frequency> Web::CSS::CSSMathValue::resolve_frequency_percentage(Web::CSS::Frequency const&) const::$_2::operator()<Web::CSS::Number>(Web::CSS::Number const&) const
Unexecuted instantiation: CSSMathValue.cpp:AK::Optional<Web::CSS::Frequency> Web::CSS::CSSMathValue::resolve_frequency_percentage(Web::CSS::Frequency const&) const::$_2::operator()<Web::CSS::Angle>(Web::CSS::Angle const&) const
Unexecuted instantiation: CSSMathValue.cpp:AK::Optional<Web::CSS::Frequency> Web::CSS::CSSMathValue::resolve_frequency_percentage(Web::CSS::Frequency const&) const::$_2::operator()<Web::CSS::Flex>(Web::CSS::Flex const&) const
Unexecuted instantiation: CSSMathValue.cpp:AK::Optional<Web::CSS::Frequency> Web::CSS::CSSMathValue::resolve_frequency_percentage(Web::CSS::Frequency const&) const::$_2::operator()<Web::CSS::Length>(Web::CSS::Length const&) const
Unexecuted instantiation: CSSMathValue.cpp:AK::Optional<Web::CSS::Frequency> Web::CSS::CSSMathValue::resolve_frequency_percentage(Web::CSS::Frequency const&) const::$_2::operator()<Web::CSS::Resolution>(Web::CSS::Resolution const&) const
Unexecuted instantiation: CSSMathValue.cpp:AK::Optional<Web::CSS::Frequency> Web::CSS::CSSMathValue::resolve_frequency_percentage(Web::CSS::Frequency const&) const::$_2::operator()<Web::CSS::Time>(Web::CSS::Time const&) const
2717
0
}
2718
2719
Optional<Length> CSSMathValue::resolve_length(Length::ResolutionContext const& context) const
2720
0
{
2721
0
    auto result = m_calculation->resolve(context, {});
2722
2723
0
    if (result.value().has<Length>())
2724
0
        return result.value().get<Length>();
2725
0
    return {};
2726
0
}
2727
2728
Optional<Length> CSSMathValue::resolve_length(Layout::Node const& layout_node) const
2729
0
{
2730
0
    return resolve_length(Length::ResolutionContext::for_layout_node(layout_node));
2731
0
}
2732
2733
Optional<Length> CSSMathValue::resolve_length_percentage(Layout::Node const& layout_node, Length const& percentage_basis) const
2734
0
{
2735
0
    return resolve_length_percentage(Length::ResolutionContext::for_layout_node(layout_node), percentage_basis);
2736
0
}
2737
2738
Optional<Length> CSSMathValue::resolve_length_percentage(Layout::Node const& layout_node, CSSPixels percentage_basis) const
2739
0
{
2740
0
    return resolve_length_percentage(Length::ResolutionContext::for_layout_node(layout_node), Length::make_px(percentage_basis));
2741
0
}
2742
2743
Optional<Length> CSSMathValue::resolve_length_percentage(Length::ResolutionContext const& resolution_context, Length const& percentage_basis) const
2744
0
{
2745
0
    auto result = m_calculation->resolve(resolution_context, percentage_basis);
2746
2747
0
    return result.value().visit(
2748
0
        [&](Length const& length) -> Optional<Length> {
2749
0
            return length;
2750
0
        },
2751
0
        [&](Percentage const& percentage) -> Optional<Length> {
2752
0
            return percentage_basis.percentage_of(percentage);
2753
0
        },
2754
0
        [&](auto const&) -> Optional<Length> {
2755
0
            return {};
2756
0
        });
Unexecuted instantiation: CSSMathValue.cpp:AK::Optional<Web::CSS::Length> Web::CSS::CSSMathValue::resolve_length_percentage(Web::CSS::Length::ResolutionContext const&, Web::CSS::Length const&) const::$_2::operator()<Web::CSS::Number>(Web::CSS::Number const&) const
Unexecuted instantiation: CSSMathValue.cpp:AK::Optional<Web::CSS::Length> Web::CSS::CSSMathValue::resolve_length_percentage(Web::CSS::Length::ResolutionContext const&, Web::CSS::Length const&) const::$_2::operator()<Web::CSS::Angle>(Web::CSS::Angle const&) const
Unexecuted instantiation: CSSMathValue.cpp:AK::Optional<Web::CSS::Length> Web::CSS::CSSMathValue::resolve_length_percentage(Web::CSS::Length::ResolutionContext const&, Web::CSS::Length const&) const::$_2::operator()<Web::CSS::Flex>(Web::CSS::Flex const&) const
Unexecuted instantiation: CSSMathValue.cpp:AK::Optional<Web::CSS::Length> Web::CSS::CSSMathValue::resolve_length_percentage(Web::CSS::Length::ResolutionContext const&, Web::CSS::Length const&) const::$_2::operator()<Web::CSS::Frequency>(Web::CSS::Frequency const&) const
Unexecuted instantiation: CSSMathValue.cpp:AK::Optional<Web::CSS::Length> Web::CSS::CSSMathValue::resolve_length_percentage(Web::CSS::Length::ResolutionContext const&, Web::CSS::Length const&) const::$_2::operator()<Web::CSS::Resolution>(Web::CSS::Resolution const&) const
Unexecuted instantiation: CSSMathValue.cpp:AK::Optional<Web::CSS::Length> Web::CSS::CSSMathValue::resolve_length_percentage(Web::CSS::Length::ResolutionContext const&, Web::CSS::Length const&) const::$_2::operator()<Web::CSS::Time>(Web::CSS::Time const&) const
2757
0
}
2758
2759
Optional<Percentage> CSSMathValue::resolve_percentage() const
2760
0
{
2761
0
    auto result = m_calculation->resolve({}, {});
2762
0
    if (result.value().has<Percentage>())
2763
0
        return result.value().get<Percentage>();
2764
0
    return {};
2765
0
}
2766
2767
Optional<Resolution> CSSMathValue::resolve_resolution() const
2768
0
{
2769
0
    auto result = m_calculation->resolve({}, {});
2770
0
    if (result.value().has<Resolution>())
2771
0
        return result.value().get<Resolution>();
2772
0
    return {};
2773
0
}
2774
2775
Optional<Time> CSSMathValue::resolve_time() const
2776
0
{
2777
0
    auto result = m_calculation->resolve({}, {});
2778
2779
0
    if (result.value().has<Time>())
2780
0
        return result.value().get<Time>();
2781
0
    return {};
2782
0
}
2783
2784
Optional<Time> CSSMathValue::resolve_time_percentage(Time const& percentage_basis) const
2785
0
{
2786
0
    auto result = m_calculation->resolve({}, percentage_basis);
2787
2788
0
    return result.value().visit(
2789
0
        [&](Time const& time) -> Optional<Time> {
2790
0
            return time;
2791
0
        },
2792
0
        [&](auto const&) -> Optional<Time> {
2793
0
            return {};
2794
0
        });
Unexecuted instantiation: CSSMathValue.cpp:AK::Optional<Web::CSS::Time> Web::CSS::CSSMathValue::resolve_time_percentage(Web::CSS::Time const&) const::$_1::operator()<Web::CSS::Number>(Web::CSS::Number const&) const
Unexecuted instantiation: CSSMathValue.cpp:AK::Optional<Web::CSS::Time> Web::CSS::CSSMathValue::resolve_time_percentage(Web::CSS::Time const&) const::$_1::operator()<Web::CSS::Angle>(Web::CSS::Angle const&) const
Unexecuted instantiation: CSSMathValue.cpp:AK::Optional<Web::CSS::Time> Web::CSS::CSSMathValue::resolve_time_percentage(Web::CSS::Time const&) const::$_1::operator()<Web::CSS::Flex>(Web::CSS::Flex const&) const
Unexecuted instantiation: CSSMathValue.cpp:AK::Optional<Web::CSS::Time> Web::CSS::CSSMathValue::resolve_time_percentage(Web::CSS::Time const&) const::$_1::operator()<Web::CSS::Frequency>(Web::CSS::Frequency const&) const
Unexecuted instantiation: CSSMathValue.cpp:AK::Optional<Web::CSS::Time> Web::CSS::CSSMathValue::resolve_time_percentage(Web::CSS::Time const&) const::$_1::operator()<Web::CSS::Length>(Web::CSS::Length const&) const
Unexecuted instantiation: CSSMathValue.cpp:AK::Optional<Web::CSS::Time> Web::CSS::CSSMathValue::resolve_time_percentage(Web::CSS::Time const&) const::$_1::operator()<Web::CSS::Percentage>(Web::CSS::Percentage const&) const
Unexecuted instantiation: CSSMathValue.cpp:AK::Optional<Web::CSS::Time> Web::CSS::CSSMathValue::resolve_time_percentage(Web::CSS::Time const&) const::$_1::operator()<Web::CSS::Resolution>(Web::CSS::Resolution const&) const
2795
0
}
2796
2797
Optional<double> CSSMathValue::resolve_number() const
2798
0
{
2799
0
    auto result = m_calculation->resolve({}, {});
2800
0
    if (result.value().has<Number>())
2801
0
        return result.value().get<Number>().value();
2802
0
    return {};
2803
0
}
2804
2805
Optional<i64> CSSMathValue::resolve_integer() const
2806
0
{
2807
0
    auto result = m_calculation->resolve({}, {});
2808
0
    if (result.value().has<Number>())
2809
0
        return result.value().get<Number>().integer_value();
2810
0
    return {};
2811
0
}
2812
2813
bool CSSMathValue::contains_percentage() const
2814
0
{
2815
0
    return m_calculation->contains_percentage();
2816
0
}
2817
2818
String CSSMathValue::dump() const
2819
0
{
2820
0
    StringBuilder builder;
2821
0
    m_calculation->dump(builder, 0);
2822
0
    return builder.to_string_without_validation();
2823
0
}
2824
2825
}