forked from LadybirdBrowser/ladybird
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCSSMathSum.cpp
More file actions
223 lines (189 loc) · 7.86 KB
/
Copy pathCSSMathSum.cpp
File metadata and controls
223 lines (189 loc) · 7.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
/*
* Copyright (c) 2025, Sam Atkins <sam@ladybird.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "CSSMathSum.h"
#include <LibWeb/Bindings/CSSMathSumPrototype.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/CSS/CSSMathNegate.h>
#include <LibWeb/CSS/CSSNumericArray.h>
#include <LibWeb/CSS/StyleValues/CalculatedStyleValue.h>
#include <LibWeb/WebIDL/DOMException.h>
#include <LibWeb/WebIDL/ExceptionOr.h>
namespace Web::CSS {
GC_DEFINE_ALLOCATOR(CSSMathSum);
GC::Ref<CSSMathSum> CSSMathSum::create(JS::Realm& realm, NumericType type, GC::Ref<CSSNumericArray> values)
{
return realm.create<CSSMathSum>(realm, move(type), move(values));
}
WebIDL::ExceptionOr<GC::Ref<CSSMathSum>> CSSMathSum::add_all_types_into_math_sum(JS::Realm& realm, GC::RootVector<GC::Ref<CSSNumericValue>> const& values)
{
auto type = values.first()->type();
bool first = true;
for (auto const& value : values) {
if (first) {
first = false;
continue;
}
if (auto added_types = type.added_to(value->type()); added_types.has_value()) {
type = added_types.release_value();
} else {
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Cannot create a CSSMathSum with values of incompatible types"sv };
}
}
auto values_array = CSSNumericArray::create(realm, { values });
return CSSMathSum::create(realm, type, values_array);
}
// https://drafts.css-houdini.org/css-typed-om-1/#dom-cssmathsum-cssmathsum
WebIDL::ExceptionOr<GC::Ref<CSSMathSum>> CSSMathSum::construct_impl(JS::Realm& realm, Vector<CSSNumberish> values)
{
// The CSSMathSum(...args) constructor must, when called, perform the following steps:
// 1. Replace each item of args with the result of rectifying a numberish value for the item.
GC::RootVector<GC::Ref<CSSNumericValue>> converted_values { realm.heap() };
converted_values.ensure_capacity(values.size());
for (auto const& value : values) {
converted_values.append(rectify_a_numberish_value(realm, value));
}
// 2. If args is empty, throw a SyntaxError.
if (converted_values.is_empty())
return WebIDL::SyntaxError::create(realm, "Cannot create an empty CSSMathSum"_utf16);
// 3. Let type be the result of adding the types of all the items of args. If type is failure, throw a TypeError.
// 4. Return a new CSSMathSum whose values internal slot is set to args.
return add_all_types_into_math_sum(realm, converted_values);
}
CSSMathSum::CSSMathSum(JS::Realm& realm, NumericType type, GC::Ref<CSSNumericArray> values)
: CSSMathValue(realm, Bindings::CSSMathOperator::Sum, move(type))
, m_values(move(values))
{
}
CSSMathSum::~CSSMathSum() = default;
void CSSMathSum::initialize(JS::Realm& realm)
{
WEB_SET_PROTOTYPE_FOR_INTERFACE(CSSMathSum);
Base::initialize(realm);
}
void CSSMathSum::visit_edges(Visitor& visitor)
{
Base::visit_edges(visitor);
visitor.visit(m_values);
}
// https://drafts.css-houdini.org/css-typed-om-1/#serialize-a-cssmathvalue
void CSSMathSum::serialize_math_value(StringBuilder& s, Nested nested, Parens parens) const
{
// NB: Only steps 1 and 3 apply here.
// 1. Let s initially be the empty string.
// 3. Otherwise, if this is a CSSMathSum:
{
// 1. If paren-less is true, continue to the next step; otherwise, if nested is true, append "(" to s;
// otherwise, append "calc(" to s.
if (parens == Parens::With) {
if (nested == Nested::Yes) {
s.append('(');
} else {
s.append("calc("sv);
}
}
// 2. Serialize the first item in this’s values internal slot with nested set to true, and append the result
// to s.
m_values->values().first()->serialize(s, { .nested = true });
// 3. For each arg in this's values internal slot beyond the first:
bool first = true;
for (auto const& arg : m_values->values()) {
if (first) {
first = false;
continue;
}
// 1. If arg is a CSSMathNegate, append " - " to s, then serialize arg’s value internal slot with nested
// set to true, and append the result to s.
if (auto* negate = as_if<CSSMathNegate>(*arg)) {
s.append(" - "sv);
negate->value()->serialize(s, { .nested = true });
}
// 2. Otherwise, append " + " to s, then serialize arg with nested set to true, and append the result to s.
else {
s.append(" + "sv);
arg->serialize(s, { .nested = true });
}
}
// 4. If paren-less is false, append ")" to s,
if (parens == Parens::With)
s.append(')');
// 5. Return s.
}
}
// https://drafts.css-houdini.org/css-typed-om-1/#dom-cssmathsum-values
GC::Ref<CSSNumericArray> CSSMathSum::values() const
{
return m_values;
}
// https://drafts.css-houdini.org/css-typed-om-1/#equal-numeric-value
bool CSSMathSum::is_equal_numeric_value(GC::Ref<CSSNumericValue> other) const
{
// NB: Only steps 1 and 3 are relevant.
// 1. If value1 and value2 are not members of the same interface, return false.
auto* other_sum = as_if<CSSMathSum>(*other);
if (!other_sum)
return false;
// 3. If value1 and value2 are both CSSMathSums, CSSMathProducts, CSSMathMins, or CSSMathMaxs:
// NB: Substeps are implemented in CSSNumericArray.
return m_values->is_equal_numeric_values(other_sum->m_values);
}
// https://drafts.css-houdini.org/css-typed-om-1/#create-a-sum-value
Optional<SumValue> CSSMathSum::create_a_sum_value() const
{
// 1. Let values initially be an empty list.
SumValue values;
// 2. For each item in this’s values internal slot:
for (auto const item : m_values->values()) {
// 1. Let value be the result of creating a sum value from item. If value is failure, return failure.
auto maybe_value = item->create_a_sum_value();
if (!maybe_value.has_value())
return {};
auto const& value = maybe_value.value();
// 2. For each subvalue of value:
for (auto const& subvalue : value) {
// 1. If values already contains an item with the same unit map as subvalue, increment that item’s value by
// the value of subvalue.
auto existing_item = values.find_if([&subvalue](SumValueItem const& other) {
return subvalue.unit_map == other.unit_map;
});
if (existing_item != values.end()) {
existing_item->value += subvalue.value;
}
// 2. Otherwise, append subvalue to values.
else {
values.append(subvalue);
}
}
}
// 3. Create a type from the unit map of each item of values, and add all the types together.
// If the result is failure, return failure.
auto added_type = NumericType::create_from_unit_map(values.first().unit_map);
if (!added_type.has_value())
return {};
bool first = true;
for (auto const& [_, unit_map] : values) {
if (first) {
first = false;
continue;
}
auto type = NumericType::create_from_unit_map(unit_map);
if (!type.has_value())
return {};
added_type = added_type->added_to(type.value());
if (!added_type.has_value())
return {};
}
// 4. Return values.
return values;
}
WebIDL::ExceptionOr<NonnullRefPtr<CalculationNode const>> CSSMathSum::create_calculation_node(CalculationContext const& context) const
{
Vector<NonnullRefPtr<CalculationNode const>> child_nodes;
for (auto const& child_value : m_values->values()) {
child_nodes.append(TRY(child_value->create_calculation_node(context)));
}
return SumCalculationNode::create(move(child_nodes));
}
}