Coverage Report

Created: 2025-03-04 07:22

/src/serenity/Userland/Libraries/LibWeb/Geometry/DOMMatrixReadOnly.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2023, Luke Wilde <lukew@serenityos.org>
3
 * Copyright (c) 2023, Bastiaan van der Plaat <bastiaan.v.d.plaat@gmail.com>
4
 * Copyright (c) 2024, Kenneth Myhra <kennethmyhra@serenityos.org>
5
 *
6
 * SPDX-License-Identifier: BSD-2-Clause
7
 */
8
9
#include <LibJS/Runtime/TypedArray.h>
10
#include <LibWeb/Bindings/DOMMatrixReadOnlyPrototype.h>
11
#include <LibWeb/CSS/Parser/Parser.h>
12
#include <LibWeb/CSS/StyleProperties.h>
13
#include <LibWeb/CSS/StyleValues/ShorthandStyleValue.h>
14
#include <LibWeb/Geometry/DOMMatrix.h>
15
#include <LibWeb/Geometry/DOMMatrixReadOnly.h>
16
#include <LibWeb/Geometry/DOMPoint.h>
17
#include <LibWeb/HTML/StructuredSerialize.h>
18
#include <LibWeb/HTML/Window.h>
19
#include <LibWeb/WebIDL/Buffers.h>
20
#include <LibWeb/WebIDL/ExceptionOr.h>
21
22
namespace Web::Geometry {
23
24
JS_DEFINE_ALLOCATOR(DOMMatrixReadOnly);
25
26
// https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-dommatrixreadonly
27
WebIDL::ExceptionOr<JS::NonnullGCPtr<DOMMatrixReadOnly>> DOMMatrixReadOnly::construct_impl(JS::Realm& realm, Optional<Variant<String, Vector<double>>> const& init)
28
0
{
29
0
    auto& vm = realm.vm();
30
31
    // -> If init is omitted
32
0
    if (!init.has_value()) {
33
        // Return the result of invoking create a 2d matrix of type DOMMatrixReadOnly or DOMMatrix as appropriate, with the sequence [1, 0, 0, 1, 0, 0].
34
0
        return realm.heap().allocate<DOMMatrixReadOnly>(realm, realm, 1, 0, 0, 1, 0, 0);
35
0
    }
36
37
0
    auto const& init_value = init.value();
38
39
    // -> If init is a DOMString
40
0
    if (init_value.has<String>()) {
41
        // 1. If current global object is not a Window object, then throw a TypeError exception.
42
0
        if (!is<HTML::Window>(realm.global_object()))
43
0
            return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "This can only be used in a Window context"_string };
44
45
        // 2. Parse init into an abstract matrix, and let matrix and 2dTransform be the result. If the result is failure, then throw a "SyntaxError" DOMException.
46
0
        auto result = TRY(parse_dom_matrix_init_string(realm, init_value.get<String>()));
47
0
        auto* elements = result.matrix.elements();
48
49
        // If 2dTransform is true
50
0
        if (result.is_2d_transform) {
51
            // Return the result of invoking create a 2d matrix of type DOMMatrixReadOnly or DOMMatrix as appropriate, with a sequence of numbers, the values being the elements m11, m12, m21, m22, m41 and m42 of matrix.
52
0
            return realm.heap().allocate<DOMMatrixReadOnly>(realm, realm, elements[0][0], elements[1][0], elements[0][1], elements[1][1], elements[0][3], elements[1][3]);
53
0
        }
54
55
        // Otherwise, return the result of invoking create a 3d matrix of type DOMMatrixReadOnly or DOMMatrix as appropriate, with a sequence of numbers, the values being the 16 elements of matrix.
56
0
        return realm.heap().allocate<DOMMatrixReadOnly>(realm, realm,
57
0
            elements[0][0], elements[1][0], elements[2][0], elements[3][0],
58
0
            elements[0][1], elements[1][1], elements[2][1], elements[3][1],
59
0
            elements[0][2], elements[1][2], elements[2][2], elements[3][2],
60
0
            elements[0][3], elements[1][3], elements[2][3], elements[3][3]);
61
0
    }
62
63
0
    auto const& double_sequence = init_value.get<Vector<double>>();
64
65
    // -> If init is a sequence with 6 elements
66
0
    if (double_sequence.size() == 6) {
67
        // Return the result of invoking create a 2d matrix of type DOMMatrixReadOnly or DOMMatrix as appropriate, with the sequence init.
68
0
        return realm.heap().allocate<DOMMatrixReadOnly>(realm, realm, double_sequence[0], double_sequence[1], double_sequence[2], double_sequence[3], double_sequence[4], double_sequence[5]);
69
0
    }
70
71
    // -> If init is a sequence with 16 elements
72
0
    if (double_sequence.size() == 16) {
73
        // Return the result of invoking create a 3d matrix of type DOMMatrixReadOnly or DOMMatrix as appropriate, with the sequence init.
74
0
        return realm.heap().allocate<DOMMatrixReadOnly>(realm, realm,
75
0
            double_sequence[0], double_sequence[1], double_sequence[2], double_sequence[3],
76
0
            double_sequence[4], double_sequence[5], double_sequence[6], double_sequence[7],
77
0
            double_sequence[8], double_sequence[9], double_sequence[10], double_sequence[11],
78
0
            double_sequence[12], double_sequence[13], double_sequence[14], double_sequence[15]);
79
0
    }
80
81
    // -> Otherwise, throw a TypeError exception.
82
0
    return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, TRY_OR_THROW_OOM(vm, String::formatted("Sequence must contain exactly 6 or 16 elements, got {} element(s)", double_sequence.size())) };
83
0
}
84
85
// https://drafts.fxtf.org/geometry/#create-a-dommatrixreadonly-from-the-2d-dictionary
86
WebIDL::ExceptionOr<JS::NonnullGCPtr<DOMMatrixReadOnly>> DOMMatrixReadOnly::create_from_dom_matrix_2d_init(JS::Realm& realm, DOMMatrix2DInit& init)
87
0
{
88
    // 1. Validate and fixup (2D) other.
89
0
    TRY(validate_and_fixup_dom_matrix_2d_init(init));
90
91
    // These should all have values after calling `validate_and_fixup_dom_matrix_2d_init`
92
0
    VERIFY(init.m11.has_value());
93
0
    VERIFY(init.m12.has_value());
94
0
    VERIFY(init.m21.has_value());
95
0
    VERIFY(init.m22.has_value());
96
0
    VERIFY(init.m41.has_value());
97
0
    VERIFY(init.m42.has_value());
98
99
    // 2. Return the result of invoking create a 2d matrix of type DOMMatrixReadOnly or DOMMatrix as appropriate, with a sequence of numbers,
100
    //    the values being the 6 elements m11, m12, m21, m22, m41 and m42 of other in the given order.
101
0
    return realm.heap().allocate<DOMMatrixReadOnly>(realm, realm, init.m11.value(), init.m12.value(), init.m21.value(), init.m22.value(), init.m41.value(), init.m42.value());
102
0
}
103
104
// https://drafts.fxtf.org/geometry/#create-a-dommatrixreadonly-from-the-dictionary
105
WebIDL::ExceptionOr<JS::NonnullGCPtr<DOMMatrixReadOnly>> DOMMatrixReadOnly::create_from_dom_matrix_init(JS::Realm& realm, DOMMatrixInit& init)
106
0
{
107
    // 1. Validate and fixup other.
108
0
    TRY(validate_and_fixup_dom_matrix_init(init));
109
110
    // 2. If the is2D dictionary member of other is true.
111
0
    if (init.is2d.has_value() && init.is2d.value()) {
112
        // Return the result of invoking create a 2d matrix of type DOMMatrixReadOnly or DOMMatrix as appropriate, with a sequence of numbers, the values being the 6 elements m11, m12, m21, m22, m41 and m42 of other in the given order.
113
0
        return realm.heap().allocate<DOMMatrixReadOnly>(realm, realm, init.m11.value(), init.m12.value(), init.m21.value(), init.m22.value(), init.m41.value(), init.m42.value());
114
0
    }
115
116
    // Otherwise, Return the result of invoking create a 3d matrix of type DOMMatrixReadOnly or DOMMatrix as appropriate, with a sequence of numbers, the values being the 16 elements m11, m12, m13, ..., m44 of other in the given order.
117
0
    return realm.heap().allocate<DOMMatrixReadOnly>(realm, realm, init.m11.value(), init.m12.value(), init.m13, init.m14,
118
0
        init.m21.value(), init.m22.value(), init.m23, init.m24,
119
0
        init.m31, init.m32, init.m33, init.m34,
120
0
        init.m41.value(), init.m42.value(), init.m43, init.m44);
121
0
}
122
123
JS::NonnullGCPtr<DOMMatrixReadOnly> DOMMatrixReadOnly::create(JS::Realm& realm)
124
0
{
125
0
    return realm.heap().allocate<DOMMatrixReadOnly>(realm, realm);
126
0
}
127
128
DOMMatrixReadOnly::DOMMatrixReadOnly(JS::Realm& realm, double m11, double m12, double m21, double m22, double m41, double m42)
129
0
    : Bindings::PlatformObject(realm)
130
0
{
131
0
    initialize_from_create_2d_matrix(m11, m12, m21, m22, m41, m42);
132
0
}
133
134
DOMMatrixReadOnly::DOMMatrixReadOnly(JS::Realm& realm, double m11, double m12, double m13, double m14, double m21, double m22, double m23, double m24, double m31, double m32, double m33, double m34, double m41, double m42, double m43, double m44)
135
0
    : Bindings::PlatformObject(realm)
136
0
{
137
0
    initialize_from_create_3d_matrix(m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44);
138
0
}
139
140
DOMMatrixReadOnly::DOMMatrixReadOnly(JS::Realm& realm, DOMMatrixReadOnly const& other)
141
0
    : Bindings::PlatformObject(realm)
142
0
    , m_matrix(other.m_matrix)
143
0
    , m_is_2d(other.m_is_2d)
144
0
{
145
0
}
146
147
DOMMatrixReadOnly::DOMMatrixReadOnly(JS::Realm& realm)
148
0
    : Bindings::PlatformObject(realm)
149
0
{
150
0
}
151
152
0
DOMMatrixReadOnly::~DOMMatrixReadOnly() = default;
153
154
void DOMMatrixReadOnly::initialize(JS::Realm& realm)
155
0
{
156
0
    Base::initialize(realm);
157
0
    WEB_SET_PROTOTYPE_FOR_INTERFACE(DOMMatrixReadOnly);
158
0
}
159
160
// https://drafts.fxtf.org/geometry/#create-a-2d-matrix
161
void DOMMatrixReadOnly::initialize_from_create_2d_matrix(double m11, double m12, double m21, double m22, double m41, double m42)
162
0
{
163
    // NOTE: The matrix used in the spec is column-major (https://drafts.fxtf.org/geometry/#4x4-abstract-matrix) but Gfx::Matrix4x4 is row-major so we need to transpose the values.
164
165
    // 1. Let matrix be a new instance of type.
166
    // 2. Set m11 element, m12 element, m21 element, m22 element, m41 element and m42 element to the values of init in order starting with the first value.
167
0
    auto* elements = m_matrix.elements();
168
0
    elements[0][0] = m11;
169
0
    elements[1][0] = m12;
170
0
    elements[0][1] = m21;
171
0
    elements[1][1] = m22;
172
0
    elements[0][3] = m41;
173
0
    elements[1][3] = m42;
174
175
    // 3. Set m13 element, m14 element, m23 element, m24 element, m31 element, m32 element, m34 element, and m43 element to 0.
176
0
    elements[2][0] = 0.0;
177
0
    elements[3][0] = 0.0;
178
0
    elements[2][1] = 0.0;
179
0
    elements[3][1] = 0.0;
180
0
    elements[0][2] = 0.0;
181
0
    elements[1][2] = 0.0;
182
0
    elements[3][2] = 0.0;
183
0
    elements[2][3] = 0.0;
184
185
    // 4. Set m33 element and m44 element to 1.
186
0
    elements[2][2] = 1.0;
187
0
    elements[3][3] = 1.0;
188
189
    // 5. Set is 2D to true.
190
0
    m_is_2d = true;
191
192
    // 6. Return matrix
193
0
}
194
195
// https://drafts.fxtf.org/geometry/#create-a-3d-matrix
196
void DOMMatrixReadOnly::initialize_from_create_3d_matrix(double m11, double m12, double m13, double m14, double m21, double m22, double m23, double m24, double m31, double m32, double m33, double m34, double m41, double m42, double m43, double m44)
197
0
{
198
    // NOTE: The matrix used in the spec is column-major (https://drafts.fxtf.org/geometry/#4x4-abstract-matrix) but Gfx::Matrix4x4 is row-major so we need to transpose the values.
199
200
    // 1. Let matrix be a new instance of type.
201
    // 2. Set m11 element to m44 element to the values of init in column-major order.
202
0
    auto* elements = m_matrix.elements();
203
0
    elements[0][0] = m11;
204
0
    elements[1][0] = m12;
205
0
    elements[2][0] = m13;
206
0
    elements[3][0] = m14;
207
0
    elements[0][1] = m21;
208
0
    elements[1][1] = m22;
209
0
    elements[2][1] = m23;
210
0
    elements[3][1] = m24;
211
0
    elements[0][2] = m31;
212
0
    elements[1][2] = m32;
213
0
    elements[2][2] = m33;
214
0
    elements[3][2] = m34;
215
0
    elements[0][3] = m41;
216
0
    elements[1][3] = m42;
217
0
    elements[2][3] = m43;
218
0
    elements[3][3] = m44;
219
220
    // 3. Set is 2D to false.
221
0
    m_is_2d = false;
222
223
    // 4. Return matrix
224
0
}
225
226
// https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-frommatrix
227
WebIDL::ExceptionOr<JS::NonnullGCPtr<DOMMatrixReadOnly>> DOMMatrixReadOnly::from_matrix(JS::VM& vm, DOMMatrixInit& other)
228
0
{
229
0
    return create_from_dom_matrix_init(*vm.current_realm(), other);
230
0
}
231
232
// https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-fromfloat32array
233
WebIDL::ExceptionOr<JS::NonnullGCPtr<DOMMatrixReadOnly>> DOMMatrixReadOnly::from_float32_array(JS::VM& vm, JS::Handle<WebIDL::BufferSource> const& array32)
234
0
{
235
0
    if (!is<JS::Float32Array>(*array32))
236
0
        return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "Float32Array");
237
238
0
    auto& realm = *vm.current_realm();
239
0
    auto& float32_array = static_cast<JS::Float32Array&>(*array32->raw_object());
240
0
    ReadonlySpan<float> elements = float32_array.data();
241
242
    // If array32 has 6 elements, return the result of invoking create a 2d matrix of type DOMMatrixReadOnly or DOMMatrix as appropriate, with a sequence of numbers taking the values from array32 in the provided order.
243
0
    if (elements.size() == 6)
244
0
        return realm.heap().allocate<DOMMatrixReadOnly>(realm, realm, elements.at(0), elements.at(1), elements.at(2), elements.at(3), elements.at(4), elements.at(5));
245
246
    // If array32 has 16 elements, return the result of invoking create a 3d matrix of type DOMMatrixReadOnly or DOMMatrix as appropriate, with a sequence of numbers taking the values from array32 in the provided order.
247
0
    if (elements.size() == 16)
248
0
        return realm.heap().allocate<DOMMatrixReadOnly>(realm, realm, elements.at(0), elements.at(1), elements.at(2), elements.at(3),
249
0
            elements.at(4), elements.at(5), elements.at(6), elements.at(7),
250
0
            elements.at(8), elements.at(9), elements.at(10), elements.at(11),
251
0
            elements.at(12), elements.at(13), elements.at(14), elements.at(15));
252
253
    // Otherwise, throw a TypeError exception.
254
0
    return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Expected a Float32Array argument with 6 or 16 elements"_string };
255
0
}
256
257
// https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-fromfloat64array
258
WebIDL::ExceptionOr<JS::NonnullGCPtr<DOMMatrixReadOnly>> DOMMatrixReadOnly::from_float64_array(JS::VM& vm, JS::Handle<WebIDL::BufferSource> const& array64)
259
0
{
260
0
    if (!is<JS::Float64Array>(*array64))
261
0
        return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "Float64Array");
262
263
0
    auto& realm = *vm.current_realm();
264
0
    auto& float64_array = static_cast<JS::Float64Array&>(*array64->raw_object());
265
0
    ReadonlySpan<double> elements = float64_array.data();
266
267
    // If array64 has 6 elements, return the result of invoking create a 2d matrix of type DOMMatrixReadOnly or DOMMatrix as appropriate, with a sequence of numbers taking the values from array64 in the provided order.
268
0
    if (elements.size() == 6)
269
0
        return realm.heap().allocate<DOMMatrixReadOnly>(realm, realm, elements.at(0), elements.at(1), elements.at(2), elements.at(3), elements.at(4), elements.at(5));
270
271
    // If array64 has 16 elements, return the result of invoking create a 3d matrix of type DOMMatrixReadOnly or DOMMatrix as appropriate, with a sequence of numbers taking the values from array64 in the provided order.
272
0
    if (elements.size() == 16)
273
0
        return realm.heap().allocate<DOMMatrixReadOnly>(realm, realm, elements.at(0), elements.at(1), elements.at(2), elements.at(3),
274
0
            elements.at(4), elements.at(5), elements.at(6), elements.at(7),
275
0
            elements.at(8), elements.at(9), elements.at(10), elements.at(11),
276
0
            elements.at(12), elements.at(13), elements.at(14), elements.at(15));
277
278
    // Otherwise, throw a TypeError exception.
279
0
    return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Expected a Float64Array argument with 6 or 16 elements"_string };
280
0
}
281
282
// https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-isidentity
283
bool DOMMatrixReadOnly::is_identity() const
284
0
{
285
    // The isIdentity attribute must return true if
286
    // m12 element, m13 element, m14 element,
287
    // m21 element, m23 element, m24 element,
288
    // m31 element, m32 element, m34 element
289
    // m41 element, m42 element, m43 element
290
    // are 0 or -0 and
291
    // m11 element, m22 element, m33 element, m44 element are 1.
292
    // Otherwise it must return false.
293
0
    if (m12() != 0.0 && m12() != -0.0)
294
0
        return false;
295
296
0
    if (m13() != 0.0 && m13() != -0.0)
297
0
        return false;
298
299
0
    if (m14() != 0.0 && m14() != -0.0)
300
0
        return false;
301
302
0
    if (m21() != 0.0 && m21() != -0.0)
303
0
        return false;
304
305
0
    if (m23() != 0.0 && m24() != -0.0)
306
0
        return false;
307
308
0
    if (m31() != 0.0 && m32() != -0.0)
309
0
        return false;
310
311
0
    if (m34() != 0.0 && m34() != -0.0)
312
0
        return false;
313
314
0
    if (m41() != 0.0 && m42() != -0.0)
315
0
        return false;
316
317
0
    if (m43() != 0.0 && m43() != -0.0)
318
0
        return false;
319
320
0
    if (m11() != 1.0)
321
0
        return false;
322
323
0
    if (m22() != 1.0)
324
0
        return false;
325
326
0
    if (m33() != 1.0)
327
0
        return false;
328
329
0
    if (m44() != 1.0)
330
0
        return false;
331
332
0
    return true;
333
0
}
334
335
// https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-translate
336
JS::NonnullGCPtr<DOMMatrix> DOMMatrixReadOnly::translate(Optional<double> const& tx, Optional<double> const& ty, Optional<double> const& tz) const
337
0
{
338
    // 1. Let result be the resulting matrix initialized to the values of the current matrix.
339
0
    auto result = DOMMatrix::create_from_dom_matrix_read_only(realm(), *this);
340
341
    // 2. Perform a translateSelf() transformation on result with the arguments tx, ty, tz.
342
    // 3. Return result.
343
0
    return result->translate_self(tx, ty, tz);
344
0
}
345
346
// https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-scale
347
JS::NonnullGCPtr<DOMMatrix> DOMMatrixReadOnly::scale(Optional<double> scale_x, Optional<double> scale_y, Optional<double> scale_z, Optional<double> origin_x, Optional<double> origin_y, Optional<double> origin_z)
348
0
{
349
    // 1. If scaleY is missing, set scaleY to the value of scaleX.
350
0
    if (!scale_y.has_value())
351
0
        scale_y = scale_x;
352
353
    // 2. Let result be the resulting matrix initialized to the values of the current matrix.
354
0
    auto result = DOMMatrix::create_from_dom_matrix_read_only(realm(), *this);
355
356
    // 3. Perform a scaleSelf() transformation on result with the arguments scaleX, scaleY, scaleZ, originX, originY, originZ.
357
    // 4. Return result.
358
0
    return result->scale_self(scale_x, scale_y, scale_z, origin_x, origin_y, origin_z);
359
0
}
360
361
// https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-scalenonuniform
362
JS::NonnullGCPtr<DOMMatrix> DOMMatrixReadOnly::scale_non_uniform(Optional<double> scale_x, Optional<double> scale_y)
363
0
{
364
    // 1. Let result be the resulting matrix initialized to the values of the current matrix.
365
0
    auto result = DOMMatrix::create_from_dom_matrix_read_only(realm(), *this);
366
367
    // 2. Perform a scaleSelf() transformation on result with the arguments scaleX, scaleY, 1, 0, 0, 0.
368
    // 3. Return result.
369
0
    return result->scale_self(scale_x, scale_y, 1, 0, 0, 0);
370
0
}
371
372
// https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-scale3d
373
JS::NonnullGCPtr<DOMMatrix> DOMMatrixReadOnly::scale3d(Optional<double> scale, Optional<double> origin_x, Optional<double> origin_y, Optional<double> origin_z)
374
0
{
375
    // 1. Let result be the resulting matrix initialized to the values of the current matrix.
376
0
    auto result = DOMMatrix::create_from_dom_matrix_read_only(realm(), *this);
377
378
    // 2. Perform a scale3dSelf() transformation on result with the arguments scale, originX, originY, originZ.
379
    // 3. Return result.
380
0
    return result->scale3d_self(scale, origin_x, origin_y, origin_z);
381
0
}
382
383
JS::NonnullGCPtr<DOMMatrix> DOMMatrixReadOnly::rotate(Optional<double> rot_x, Optional<double> rot_y, Optional<double> rot_z)
384
0
{
385
    // 1. Let result be the resulting matrix initialized to the values of the current matrix.
386
0
    auto result = DOMMatrix::create_from_dom_matrix_read_only(realm(), *this);
387
388
    // 2. Perform a rotateSelf() transformation on result with the arguments rotX, rotY, rotZ.
389
    // 3. Return result.
390
0
    return result->rotate_self(rot_x, rot_y, rot_z);
391
0
}
392
393
JS::NonnullGCPtr<DOMMatrix> DOMMatrixReadOnly::rotate_from_vector(Optional<double> x, Optional<double> y)
394
0
{
395
    // 1. Let result be the resulting matrix initialized to the values of the current matrix.
396
0
    auto result = DOMMatrix::create_from_dom_matrix_read_only(realm(), *this);
397
398
    // 2. Perform a rotateFromVectorSelf() transformation on result with the arguments x, y.
399
    // 3. Return result.
400
0
    return result->rotate_from_vector_self(x, y);
401
0
}
402
403
JS::NonnullGCPtr<DOMMatrix> DOMMatrixReadOnly::rotate_axis_angle(Optional<double> x, Optional<double> y, Optional<double> z, Optional<double> angle)
404
0
{
405
    // 1. Let result be the resulting matrix initialized to the values of the current matrix.
406
0
    auto result = DOMMatrix::create_from_dom_matrix_read_only(realm(), *this);
407
408
    // 2. Perform a rotateAxisAngleSelf() transformation on result with the arguments x, y, z, angle.
409
    // 3. Return result.
410
0
    return result->rotate_axis_angle_self(x, y, z, angle);
411
0
}
412
413
// https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-skewx
414
JS::NonnullGCPtr<DOMMatrix> DOMMatrixReadOnly::skew_x(double sx) const
415
0
{
416
    // 1. Let result be the resulting matrix initialized to the values of the current matrix.
417
0
    auto result = DOMMatrix::create_from_dom_matrix_read_only(realm(), *this);
418
419
    // 2. Perform a skewXSelf() transformation on result with the argument sx.
420
    // 3. Return result.
421
0
    return result->skew_x_self(sx);
422
0
}
423
424
// https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-skewy
425
JS::NonnullGCPtr<DOMMatrix> DOMMatrixReadOnly::skew_y(double sy) const
426
0
{
427
    // 1. Let result be the resulting matrix initialized to the values of the current matrix.
428
0
    auto result = DOMMatrix::create_from_dom_matrix_read_only(realm(), *this);
429
430
    // 2. Perform a skewYSelf() transformation on result with the argument sy.
431
    // 3. Return result.
432
0
    return result->skew_y_self(sy);
433
0
}
434
435
// https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-multiply
436
WebIDL::ExceptionOr<JS::NonnullGCPtr<DOMMatrix>> DOMMatrixReadOnly::multiply(DOMMatrixInit other)
437
0
{
438
    // 1. Let result be the resulting matrix initialized to the values of the current matrix.
439
0
    auto result = DOMMatrix::create_from_dom_matrix_read_only(realm(), *this);
440
441
    // 2. Perform a multiplySelf() transformation on result with the argument other.
442
    // 3. Return result.
443
0
    return result->multiply_self(other);
444
0
}
445
446
// https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-flipx
447
JS::NonnullGCPtr<DOMMatrix> DOMMatrixReadOnly::flip_x()
448
0
{
449
    // 1. Let result be the resulting matrix initialized to the values of the current matrix.
450
0
    auto result = DOMMatrix::create_from_dom_matrix_read_only(realm(), *this);
451
452
    // 2. Post-multiply result with new DOMMatrix([-1, 0, 0, 1, 0, 0]).
453
0
    Gfx::DoubleMatrix4x4 flip_matrix = {
454
0
        -1, 0, 0, 0,
455
0
        0, 1, 0, 0,
456
0
        0, 0, 1, 0,
457
0
        0, 0, 0, 1
458
0
    };
459
0
    result->m_matrix = result->m_matrix * flip_matrix;
460
461
    // 3. Return result.
462
0
    return result;
463
0
}
464
465
// https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-flipy
466
JS::NonnullGCPtr<DOMMatrix> DOMMatrixReadOnly::flip_y()
467
0
{
468
    // 1. Let result be the resulting matrix initialized to the values of the current matrix.
469
0
    auto result = DOMMatrix::create_from_dom_matrix_read_only(realm(), *this);
470
471
    // 2. Post-multiply result with new DOMMatrix([1, 0, 0, -1, 0, 0]).
472
0
    Gfx::DoubleMatrix4x4 flip_matrix = {
473
0
        1, 0, 0, 0,
474
0
        0, -1, 0, 0,
475
0
        0, 0, 1, 0,
476
0
        0, 0, 0, 1
477
0
    };
478
0
    result->m_matrix = result->m_matrix * flip_matrix;
479
480
    // 3. Return result.
481
0
    return result;
482
0
}
483
484
// https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-inverse
485
JS::NonnullGCPtr<DOMMatrix> DOMMatrixReadOnly::inverse() const
486
0
{
487
    // 1. Let result be the resulting matrix initialized to the values of the current matrix.
488
0
    auto result = DOMMatrix::create_from_dom_matrix_read_only(realm(), *this);
489
490
    // 2. Perform a invertSelf() transformation on result.
491
    // 3. Return result.
492
    // The current matrix is not modified.
493
0
    return result->invert_self();
494
0
}
495
496
// https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-transformpoint
497
JS::NonnullGCPtr<DOMPoint> DOMMatrixReadOnly::transform_point(DOMPointInit const& point) const
498
0
{
499
    // Let pointObject be the result of invoking create a DOMPoint from the dictionary point.
500
0
    auto point_object = DOMPoint::from_point(realm().vm(), point);
501
502
    // Return the result of invoking transform a point with a matrix, given pointObject and the current matrix. The passed argument does not get modified.
503
0
    return transform_point(point_object);
504
0
}
505
506
// https://drafts.fxtf.org/geometry/#transform-a-point-with-a-matrix
507
JS::NonnullGCPtr<DOMPoint> DOMMatrixReadOnly::transform_point(DOMPointReadOnly const& point) const
508
0
{
509
    // 1. Let x be point’s x coordinate.
510
    // 2. Let y be point’s y coordinate.
511
    // 3. Let z be point’s z coordinate.
512
    // 4. Let w be point’s w perspective.
513
    // 5. Let pointVector be a new column vector with the elements being x, y, z, and w, respectively.
514
0
    Vector4<double> point_vector { point.x(), point.y(), point.z(), point.w() };
515
516
    // 6. Set pointVector to pointVector pre-multiplied by matrix.
517
    // This is really a post multiply because of the transposed m_matrix.
518
0
    point_vector = m_matrix * point_vector;
519
520
    // 7. Let transformedPoint be a new DOMPoint object.
521
    // 8. Set transformedPoint’s x coordinate to pointVector’s first element.
522
    // 9. Set transformedPoint’s y coordinate to pointVector’s second element.
523
    // 10. Set transformedPoint’s z coordinate to pointVector’s third element.
524
    // 11. Set transformedPoint’s w perspective to pointVector’s fourth element.
525
    // 12. Return transformedPoint.
526
0
    return DOMPoint::construct_impl(realm(), point_vector.x(), point_vector.y(), point_vector.z(), point_vector.w());
527
0
}
528
529
// https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-tofloat32array
530
JS::NonnullGCPtr<JS::Float32Array> DOMMatrixReadOnly::to_float32_array() const
531
0
{
532
    // Returns the serialized 16 elements m11 to m44 of the current matrix in column-major order as Float32Array.
533
0
    float elements[16] = { static_cast<float>(m11()), static_cast<float>(m12()), static_cast<float>(m13()), static_cast<float>(m14()),
534
0
        static_cast<float>(m21()), static_cast<float>(m22()), static_cast<float>(m23()), static_cast<float>(m24()),
535
0
        static_cast<float>(m31()), static_cast<float>(m32()), static_cast<float>(m33()), static_cast<float>(m34()),
536
0
        static_cast<float>(m41()), static_cast<float>(m42()), static_cast<float>(m43()), static_cast<float>(m44()) };
537
0
    auto bytes = MUST(ByteBuffer::copy(elements, sizeof(elements)));
538
0
    auto array_buffer = JS::ArrayBuffer::create(realm(), move(bytes));
539
0
    return JS::Float32Array::create(realm(), sizeof(elements) / sizeof(float), array_buffer);
540
0
}
541
542
// https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-tofloat64array
543
JS::NonnullGCPtr<JS::Float64Array> DOMMatrixReadOnly::to_float64_array() const
544
0
{
545
    // Returns the serialized 16 elements m11 to m44 of the current matrix in column-major order as Float64Array.
546
0
    double elements[16] = { m11(), m12(), m13(), m14(),
547
0
        m21(), m22(), m23(), m24(),
548
0
        m31(), m32(), m33(), m34(),
549
0
        m41(), m42(), m43(), m44() };
550
0
    auto bytes = MUST(ByteBuffer::copy(elements, sizeof(elements)));
551
0
    auto array_buffer = JS::ArrayBuffer::create(realm(), move(bytes));
552
0
    return JS::Float64Array::create(realm(), sizeof(elements) / sizeof(double), array_buffer);
553
0
}
554
555
// https://drafts.fxtf.org/geometry/#dommatrixreadonly-stringification-behavior
556
WebIDL::ExceptionOr<String> DOMMatrixReadOnly::to_string() const
557
0
{
558
0
    auto& vm = this->vm();
559
560
    // 1. If one or more of m11 element through m44 element are a non-finite value, then throw an "InvalidStateError" DOMException.
561
    // Spec Note: The CSS syntax cannot represent NaN or Infinity values.
562
0
    if (!isfinite(m11()) || !isfinite(m12()) || !isfinite(m13()) || !isfinite(m14())
563
0
        || !isfinite(m21()) || !isfinite(m22()) || !isfinite(m23()) || !isfinite(m24())
564
0
        || !isfinite(m31()) || !isfinite(m32()) || !isfinite(m33()) || !isfinite(m34())
565
0
        || !isfinite(m41()) || !isfinite(m42()) || !isfinite(m43()) || !isfinite(m44())) {
566
0
        return WebIDL::InvalidStateError::create(realm(), "Cannot stringify non-finite matrix values"_string);
567
0
    }
568
569
    // 2. Let string be the empty string.
570
0
    StringBuilder builder;
571
572
    // 3. If is 2D is true, then:
573
0
    if (m_is_2d) {
574
        // 1. Append "matrix(" to string.
575
0
        TRY_OR_THROW_OOM(vm, builder.try_append("matrix("sv));
576
577
        // 2. Append ! ToString(m11 element) to string.
578
0
        TRY_OR_THROW_OOM(vm, builder.try_append(JS::Value(m11()).to_string_without_side_effects()));
579
580
        // 3. Append ", " to string.
581
0
        TRY_OR_THROW_OOM(vm, builder.try_append(", "sv));
582
583
        // 4. Append ! ToString(m12 element) to string.
584
0
        TRY_OR_THROW_OOM(vm, builder.try_append(JS::Value(m12()).to_string_without_side_effects()));
585
586
        // 5. Append ", " to string.
587
0
        TRY_OR_THROW_OOM(vm, builder.try_append(", "sv));
588
589
        // 6. Append ! ToString(m21 element) to string.
590
0
        TRY_OR_THROW_OOM(vm, builder.try_append(JS::Value(m21()).to_string_without_side_effects()));
591
592
        // 7. Append ", " to string.
593
0
        TRY_OR_THROW_OOM(vm, builder.try_append(", "sv));
594
595
        // 8. Append ! ToString(m22 element) to string.
596
0
        TRY_OR_THROW_OOM(vm, builder.try_append(JS::Value(m22()).to_string_without_side_effects()));
597
598
        // 9. Append ", " to string.
599
0
        TRY_OR_THROW_OOM(vm, builder.try_append(", "sv));
600
601
        // 10. Append ! ToString(m41 element) to string.
602
0
        TRY_OR_THROW_OOM(vm, builder.try_append(JS::Value(m41()).to_string_without_side_effects()));
603
604
        // 11. Append ", " to string.
605
0
        TRY_OR_THROW_OOM(vm, builder.try_append(", "sv));
606
607
        // 12. Append ! ToString(m42 element) to string.
608
0
        TRY_OR_THROW_OOM(vm, builder.try_append(JS::Value(m42()).to_string_without_side_effects()));
609
610
        // 13. Append ")" to string.
611
0
        TRY_OR_THROW_OOM(vm, builder.try_append(")"sv));
612
0
    } else {
613
        // 1. Append "matrix3d(" to string.
614
0
        TRY_OR_THROW_OOM(vm, builder.try_append("matrix3d("sv));
615
616
        // 2. Append ! ToString(m11 element) to string.
617
0
        TRY_OR_THROW_OOM(vm, builder.try_append(JS::number_to_string(m11())));
618
619
        // 3. Append ", " to string.
620
0
        TRY_OR_THROW_OOM(vm, builder.try_append(", "sv));
621
622
        // 4. Append ! ToString(m12 element) to string.
623
0
        TRY_OR_THROW_OOM(vm, builder.try_append(JS::number_to_string(m12())));
624
625
        // 5. Append ", " to string.
626
0
        TRY_OR_THROW_OOM(vm, builder.try_append(", "sv));
627
628
        // 6. Append ! ToString(m13 element) to string.
629
0
        TRY_OR_THROW_OOM(vm, builder.try_append(JS::number_to_string(m13())));
630
631
        // 7. Append ", " to string.
632
0
        TRY_OR_THROW_OOM(vm, builder.try_append(", "sv));
633
634
        // 8. Append ! ToString(m14 element) to string.
635
0
        TRY_OR_THROW_OOM(vm, builder.try_append(JS::number_to_string(m14())));
636
637
        // 9. Append ", " to string.
638
0
        TRY_OR_THROW_OOM(vm, builder.try_append(", "sv));
639
640
        // 10. Append ! ToString(m21 element) to string.
641
0
        TRY_OR_THROW_OOM(vm, builder.try_append(JS::number_to_string(m21())));
642
643
        // 11. Append ", " to string.
644
0
        TRY_OR_THROW_OOM(vm, builder.try_append(", "sv));
645
646
        // 12. Append ! ToString(m22 element) to string.
647
0
        TRY_OR_THROW_OOM(vm, builder.try_append(JS::number_to_string(m22())));
648
649
        // 13. Append ", " to string.
650
0
        TRY_OR_THROW_OOM(vm, builder.try_append(", "sv));
651
652
        // 14. Append ! ToString(m23 element) to string.
653
0
        TRY_OR_THROW_OOM(vm, builder.try_append(JS::number_to_string(m23())));
654
655
        // 15. Append ", " to string.
656
0
        TRY_OR_THROW_OOM(vm, builder.try_append(", "sv));
657
658
        // 16. Append ! ToString(m24 element) to string.
659
0
        TRY_OR_THROW_OOM(vm, builder.try_append(JS::number_to_string(m24())));
660
661
        // 17. Append ", " to string.
662
0
        TRY_OR_THROW_OOM(vm, builder.try_append(", "sv));
663
664
        // NOTE: The spec doesn't include the steps to append m31 to m34, but they are required as matrix3d requires 16 elements.
665
0
        TRY_OR_THROW_OOM(vm, builder.try_append(JS::number_to_string(m31())));
666
0
        TRY_OR_THROW_OOM(vm, builder.try_append(", "sv));
667
0
        TRY_OR_THROW_OOM(vm, builder.try_append(JS::number_to_string(m32())));
668
0
        TRY_OR_THROW_OOM(vm, builder.try_append(", "sv));
669
0
        TRY_OR_THROW_OOM(vm, builder.try_append(JS::number_to_string(m33())));
670
0
        TRY_OR_THROW_OOM(vm, builder.try_append(", "sv));
671
0
        TRY_OR_THROW_OOM(vm, builder.try_append(JS::number_to_string(m34())));
672
0
        TRY_OR_THROW_OOM(vm, builder.try_append(", "sv));
673
674
        // 18. Append ! ToString(m41 element) to string.
675
0
        TRY_OR_THROW_OOM(vm, builder.try_append(JS::number_to_string(m41())));
676
677
        // 19. Append ", " to string.
678
0
        TRY_OR_THROW_OOM(vm, builder.try_append(", "sv));
679
680
        // 20. Append ! ToString(m42 element) to string.
681
0
        TRY_OR_THROW_OOM(vm, builder.try_append(JS::number_to_string(m42())));
682
683
        // 21. Append ", " to string.
684
0
        TRY_OR_THROW_OOM(vm, builder.try_append(", "sv));
685
686
        // 22. Append ! ToString(m43 element) to string.
687
0
        TRY_OR_THROW_OOM(vm, builder.try_append(JS::number_to_string(m43())));
688
689
        // 23. Append ", " to string.
690
0
        TRY_OR_THROW_OOM(vm, builder.try_append(", "sv));
691
692
        // 24. Append ! ToString(m44 element) to string.
693
0
        TRY_OR_THROW_OOM(vm, builder.try_append(JS::number_to_string(m44())));
694
695
        // 25. Append ")" to string.
696
0
        TRY_OR_THROW_OOM(vm, builder.try_append(")"sv));
697
0
    }
698
699
    // 5. Return string.
700
0
    return TRY_OR_THROW_OOM(vm, builder.to_string());
701
0
}
702
703
// https://drafts.fxtf.org/geometry/#structured-serialization
704
WebIDL::ExceptionOr<void> DOMMatrixReadOnly::serialization_steps(HTML::SerializationRecord& serialized, bool, HTML::SerializationMemory&)
705
0
{
706
0
    HTML::serialize_primitive_type(serialized, m_is_2d);
707
    // 1. If value’s is 2D is true:
708
0
    if (m_is_2d) {
709
        // 1. Set serialized.[[M11]] to value’s m11 element.
710
0
        HTML::serialize_primitive_type(serialized, this->m11());
711
        // 2. Set serialized.[[M12]] to value’s m12 element.
712
0
        HTML::serialize_primitive_type(serialized, this->m12());
713
        // 3. Set serialized.[[M21]] to value’s m21 element.
714
0
        HTML::serialize_primitive_type(serialized, this->m21());
715
        // 4. Set serialized.[[M22]] to value’s m22 element.
716
0
        HTML::serialize_primitive_type(serialized, this->m22());
717
        // 5. Set serialized.[[M41]] to value’s m41 element.
718
0
        HTML::serialize_primitive_type(serialized, this->m41());
719
        // 6. Set serialized.[[M42]] to value’s m42 element.
720
0
        HTML::serialize_primitive_type(serialized, this->m42());
721
        // 7. Set serialized.[[Is2D]] to true.
722
        // NOTE: This is set in the beginning of the function.
723
0
    }
724
    // 2. Otherwise:
725
0
    else {
726
        // 1. Set serialized.[[M11]] to value’s m11 element.
727
0
        HTML::serialize_primitive_type(serialized, this->m11());
728
        // 2. Set serialized.[[M12]] to value’s m12 element.
729
0
        HTML::serialize_primitive_type(serialized, this->m12());
730
        // 3. Set serialized.[[M13]] to value’s m13 element.
731
0
        HTML::serialize_primitive_type(serialized, this->m13());
732
        // 4. Set serialized.[[M14]] to value’s m14 element.
733
0
        HTML::serialize_primitive_type(serialized, this->m14());
734
        // 5. Set serialized.[[M21]] to value’s m21 element.
735
0
        HTML::serialize_primitive_type(serialized, this->m21());
736
        // 6. Set serialized.[[M22]] to value’s m22 element.
737
0
        HTML::serialize_primitive_type(serialized, this->m22());
738
        // 7. Set serialized.[[M23]] to value’s m23 element.
739
0
        HTML::serialize_primitive_type(serialized, this->m23());
740
        // 8. Set serialized.[[M24]] to value’s m24 element.
741
0
        HTML::serialize_primitive_type(serialized, this->m24());
742
        // 9. Set serialized.[[M31]] to value’s m31 element.
743
0
        HTML::serialize_primitive_type(serialized, this->m31());
744
        // 10. Set serialized.[[M32]] to value’s m32 element.
745
0
        HTML::serialize_primitive_type(serialized, this->m32());
746
        // 11. Set serialized.[[M33]] to value’s m33 element.
747
0
        HTML::serialize_primitive_type(serialized, this->m33());
748
        // 12. Set serialized.[[M34]] to value’s m34 element.
749
0
        HTML::serialize_primitive_type(serialized, this->m34());
750
        // 13. Set serialized.[[M41]] to value’s m41 element.
751
0
        HTML::serialize_primitive_type(serialized, this->m41());
752
        // 14. Set serialized.[[M42]] to value’s m42 element.
753
0
        HTML::serialize_primitive_type(serialized, this->m42());
754
        // 15. Set serialized.[[M43]] to value’s m43 element.
755
0
        HTML::serialize_primitive_type(serialized, this->m43());
756
        // 16. Set serialized.[[M44]] to value’s m44 element.
757
0
        HTML::serialize_primitive_type(serialized, this->m44());
758
        // 17. Set serialized.[[Is2D]] to false.
759
        // NOTE: This is set in the beginning of the function.
760
0
    }
761
0
    return {};
762
0
}
763
764
// https://drafts.fxtf.org/geometry/#structured-serialization
765
WebIDL::ExceptionOr<void> DOMMatrixReadOnly::deserialization_steps(ReadonlySpan<u32> const& record, size_t& position, HTML::DeserializationMemory&)
766
0
{
767
0
    bool is_2d = HTML::deserialize_primitive_type<bool>(record, position);
768
    // 1. If serialized.[[Is2D]] is true:
769
0
    if (is_2d) {
770
        // 1. Set value’s m11 element to serialized.[[M11]].
771
0
        double m11 = HTML::deserialize_primitive_type<double>(record, position);
772
        // 2. Set value’s m12 element to serialized.[[M12]].
773
0
        double m12 = HTML::deserialize_primitive_type<double>(record, position);
774
        // 3. Set value’s m13 element to 0.
775
        // 4. Set value’s m14 element to 0.
776
        // 5. Set value’s m21 element to serialized.[[M21]].
777
0
        double m21 = HTML::deserialize_primitive_type<double>(record, position);
778
        // 6. Set value’s m22 element to serialized.[[M22]].
779
0
        double m22 = HTML::deserialize_primitive_type<double>(record, position);
780
        // 7. Set value’s m23 element to 0.
781
        // 8. Set value’s m24 element to 0.
782
        // 9. Set value’s m31 element to 0.
783
        // 10. Set value’s m32 element to 0.
784
        // 11. Set value’s m33 element to 1.
785
        // 12. Set value’s m34 element to 0.
786
        // 13. Set value’s m41 element to serialized.[[M41]].
787
0
        double m41 = HTML::deserialize_primitive_type<double>(record, position);
788
        // 14 Set value’s m42 element to serialized.[[M42]].
789
0
        double m42 = HTML::deserialize_primitive_type<double>(record, position);
790
        // 15. Set value’s m43 element to 0.
791
        // 16. Set value’s m44 element to 1.
792
        // 17. Set value’s is 2D to true.
793
794
0
        initialize_from_create_2d_matrix(m11, m12, m21, m22, m41, m42);
795
0
    }
796
    // 2. Otherwise:
797
0
    else {
798
        // 1. Set value’s m11 element to serialized.[[M11]].
799
0
        double m11 = HTML::deserialize_primitive_type<double>(record, position);
800
        // 2. Set value’s m12 element to serialized.[[M12]].
801
0
        double m12 = HTML::deserialize_primitive_type<double>(record, position);
802
        // 3. Set value’s m13 element to serialized.[[M13]].
803
0
        double m13 = HTML::deserialize_primitive_type<double>(record, position);
804
        // 4. Set value’s m14 element to serialized.[[M14]].
805
0
        double m14 = HTML::deserialize_primitive_type<double>(record, position);
806
        // 5. Set value’s m21 element to serialized.[[M21]].
807
0
        double m21 = HTML::deserialize_primitive_type<double>(record, position);
808
        // 6. Set value’s m22 element to serialized.[[M22]].
809
0
        double m22 = HTML::deserialize_primitive_type<double>(record, position);
810
        // 7. Set value’s m23 element to serialized.[[M23]].
811
0
        double m23 = HTML::deserialize_primitive_type<double>(record, position);
812
        // 8. Set value’s m24 element to serialized.[[M24]].
813
0
        double m24 = HTML::deserialize_primitive_type<double>(record, position);
814
        // 9. Set value’s m31 element to serialized.[[M31]].
815
0
        double m31 = HTML::deserialize_primitive_type<double>(record, position);
816
        // 10. Set value’s m32 element to serialized.[[M32]].
817
0
        double m32 = HTML::deserialize_primitive_type<double>(record, position);
818
        // 11. Set value’s m33 element to serialized.[[M33]].
819
0
        double m33 = HTML::deserialize_primitive_type<double>(record, position);
820
        // 12. Set value’s m34 element to serialized.[[M34]].
821
0
        double m34 = HTML::deserialize_primitive_type<double>(record, position);
822
        // 13. Set value’s m41 element to serialized.[[M41]].
823
0
        double m41 = HTML::deserialize_primitive_type<double>(record, position);
824
        // 14. Set value’s m42 element to serialized.[[M42]].
825
0
        double m42 = HTML::deserialize_primitive_type<double>(record, position);
826
        // 15. Set value’s m43 element to serialized.[[M43]].
827
0
        double m43 = HTML::deserialize_primitive_type<double>(record, position);
828
        // 16. Set value’s m44 element to serialized.[[M44]].
829
0
        double m44 = HTML::deserialize_primitive_type<double>(record, position);
830
        // 17. Set value’s is 2D to false.
831
832
0
        initialize_from_create_3d_matrix(m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44);
833
0
    }
834
0
    return {};
835
0
}
836
837
// https://drafts.fxtf.org/geometry/#matrix-validate-and-fixup-2d
838
WebIDL::ExceptionOr<void> validate_and_fixup_dom_matrix_2d_init(DOMMatrix2DInit& init)
839
0
{
840
    // 1. If at least one of the following conditions are true for dict, then throw a TypeError exception and abort these steps.
841
    // - a and m11 are both present and SameValueZero(a, m11) is false.
842
0
    if (init.a.has_value() && init.m11.has_value() && !JS::same_value_zero(JS::Value(init.a.value()), JS::Value(init.m11.value())))
843
0
        return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "DOMMatrix2DInit.a and DOMMatrix2DInit.m11 must have the same value if they are both present"sv };
844
845
    // - b and m12 are both present and SameValueZero(b, m12) is false.
846
0
    if (init.b.has_value() && init.m12.has_value() && !JS::same_value_zero(JS::Value(init.b.value()), JS::Value(init.m12.value())))
847
0
        return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "DOMMatrix2DInit.b and DOMMatrix2DInit.m12 must have the same value if they are both present"sv };
848
849
    // - c and m21 are both present and SameValueZero(c, m21) is false.
850
0
    if (init.c.has_value() && init.m21.has_value() && !JS::same_value_zero(JS::Value(init.c.value()), JS::Value(init.m21.value())))
851
0
        return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "DOMMatrix2DInit.c and DOMMatrix2DInit.m21 must have the same value if they are both present"sv };
852
853
    // - d and m22 are both present and SameValueZero(d, m22) is false.
854
0
    if (init.d.has_value() && init.m22.has_value() && !JS::same_value_zero(JS::Value(init.d.value()), JS::Value(init.m22.value())))
855
0
        return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "DOMMatrix2DInit.d and DOMMatrix2DInit.m22 must have the same value if they are both present"sv };
856
857
    // - e and m41 are both present and SameValueZero(e, m41) is false.
858
0
    if (init.e.has_value() && init.m41.has_value() && !JS::same_value_zero(JS::Value(init.e.value()), JS::Value(init.m41.value())))
859
0
        return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "DOMMatrix2DInit.e and DOMMatrix2DInit.m41 must have the same value if they are both present"sv };
860
861
    // - f and m42 are both present and SameValueZero(f, m42) is false.
862
0
    if (init.f.has_value() && init.m42.has_value() && !JS::same_value_zero(JS::Value(init.f.value()), JS::Value(init.m42.value())))
863
0
        return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "DOMMatrix2DInit.f and DOMMatrix2DInit.m42 must have the same value if they are both present"sv };
864
865
    // 2. If m11 is not present then set it to the value of member a, or value 1 if a is also not present.
866
0
    if (!init.m11.has_value())
867
0
        init.m11 = init.a.value_or(1.0);
868
869
    // 3. If m12 is not present then set it to the value of member b, or value 0 if b is also not present.
870
0
    if (!init.m12.has_value())
871
0
        init.m12 = init.b.value_or(0.0);
872
873
    // 4. If m21 is not present then set it to the value of member c, or value 0 if c is also not present.
874
0
    if (!init.m21.has_value())
875
0
        init.m21 = init.c.value_or(0.0);
876
877
    // 5. If m22 is not present then set it to the value of member d, or value 1 if d is also not present.
878
0
    if (!init.m22.has_value())
879
0
        init.m22 = init.d.value_or(1.0);
880
881
    // 6. If m41 is not present then set it to the value of member e, or value 0 if e is also not present.
882
0
    if (!init.m41.has_value())
883
0
        init.m41 = init.e.value_or(0.0);
884
885
    // 7. If m42 is not present then set it to the value of member f, or value 0 if f is also not present.
886
0
    if (!init.m42.has_value())
887
0
        init.m42 = init.f.value_or(0.0);
888
889
0
    return {};
890
0
}
891
892
// https://drafts.fxtf.org/geometry/#matrix-validate-and-fixup
893
WebIDL::ExceptionOr<void> validate_and_fixup_dom_matrix_init(DOMMatrixInit& init)
894
0
{
895
    // 1. Validate and fixup (2D) dict.
896
0
    TRY(validate_and_fixup_dom_matrix_2d_init(init));
897
898
    // 2. If is2D is true and: at least one of m13, m14, m23, m24, m31, m32, m34, m43 are present with a value other than 0 or -0,
899
    //    or at least one of m33, m44 are present with a value other than 1, then throw a TypeError exception and abort these steps.
900
0
    if (init.is2d.has_value() && init.is2d.value()) {
901
0
        if ((init.m13 != 0.0 && init.m13 != -0.0)
902
0
            || (init.m14 != 0.0 && init.m14 != -0.0)
903
0
            || (init.m23 != 0.0 && init.m23 != -0.0)
904
0
            || (init.m24 != 0.0 && init.m24 != -0.0)
905
0
            || (init.m31 != 0.0 && init.m31 != -0.0)
906
0
            || (init.m32 != 0.0 && init.m32 != -0.0)
907
0
            || (init.m34 != 0.0 && init.m34 != -0.0)
908
0
            || (init.m43 != 0.0 && init.m43 != -0.0)
909
0
            || init.m33 != 1.0
910
0
            || init.m44 != 1.0) {
911
0
            return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "DOMMatrixInit.is2D is true, but the given matrix is not a 2D matrix"sv };
912
0
        }
913
0
    }
914
915
    // If is2D is not present and at least one of m13, m14, m23, m24, m31, m32, m34, m43 are present with a value other than 0 or -0,
916
    // or at least one of m33, m44 are present with a value other than 1, set is2D to false.
917
0
    if (!init.is2d.has_value()) {
918
0
        if ((init.m13 != 0.0 && init.m13 != -0.0)
919
0
            || (init.m14 != 0.0 && init.m14 != -0.0)
920
0
            || (init.m23 != 0.0 && init.m23 != -0.0)
921
0
            || (init.m24 != 0.0 && init.m24 != -0.0)
922
0
            || (init.m31 != 0.0 && init.m31 != -0.0)
923
0
            || (init.m32 != 0.0 && init.m32 != -0.0)
924
0
            || (init.m34 != 0.0 && init.m34 != -0.0)
925
0
            || (init.m43 != 0.0 && init.m43 != -0.0)
926
0
            || init.m33 != 1.0
927
0
            || init.m44 != 1.0) {
928
0
            init.is2d = false;
929
0
        }
930
0
    }
931
932
    // 4. If is2D is still not present, set it to true.
933
0
    if (!init.is2d.has_value())
934
0
        init.is2d = true;
935
936
0
    return {};
937
0
}
938
939
// https://drafts.fxtf.org/geometry/#parse-a-string-into-an-abstract-matrix
940
WebIDL::ExceptionOr<ParsedMatrix> parse_dom_matrix_init_string(JS::Realm& realm, StringView transform_list)
941
0
{
942
    // 1. If transformList is the empty string, set it to the string "matrix(1, 0, 0, 1, 0, 0)".
943
0
    if (transform_list.is_empty())
944
0
        transform_list = "matrix(1, 0, 0, 1, 0, 0)"sv;
945
946
    // 2. Parse transformList into parsedValue given the grammar for the CSS transform property.
947
    // The result will be a <transform-list>, the keyword none, or failure.
948
    // If parsedValue is failure, or any <transform-function> has <length> values without absolute length units, or any keyword other than none is used, then return failure. [CSS3-SYNTAX] [CSS3-TRANSFORMS]
949
0
    auto parsing_context = CSS::Parser::ParsingContext { realm };
950
0
    auto transform_style_value = parse_css_value(parsing_context, transform_list, CSS::PropertyID::Transform);
951
0
    if (!transform_style_value)
952
0
        return WebIDL::SyntaxError::create(realm, "Failed to parse CSS transform string."_string);
953
0
    auto parsed_value = CSS::StyleProperties::transformations_for_style_value(*transform_style_value);
954
955
    // 3. If parsedValue is none, set parsedValue to a <transform-list> containing a single identity matrix.
956
    // NOTE: parsed_value is empty on none so for loop in 6 won't modify matrix
957
0
    auto matrix = Gfx::FloatMatrix4x4::identity();
958
959
    // 4. Let 2dTransform track the 2D/3D dimension status of parsedValue.
960
    // -> If parsedValue consists of any three-dimensional transform functions, set 2dTransform to false.
961
    // -> Otherwise, set 2dTransform to true.
962
0
    bool is_2d_transform = true;
963
0
    for (auto const& transform : parsed_value) {
964
        // https://www.w3.org/TR/css-transforms-1/#two-d-transform-functions
965
0
        if (transform.function() != CSS::TransformFunction::Matrix
966
0
            && transform.function() != CSS::TransformFunction::Translate
967
0
            && transform.function() != CSS::TransformFunction::TranslateX
968
0
            && transform.function() != CSS::TransformFunction::TranslateY
969
0
            && transform.function() != CSS::TransformFunction::Scale
970
0
            && transform.function() != CSS::TransformFunction::ScaleX
971
0
            && transform.function() != CSS::TransformFunction::ScaleY
972
0
            && transform.function() != CSS::TransformFunction::Rotate
973
0
            && transform.function() != CSS::TransformFunction::Skew
974
0
            && transform.function() != CSS::TransformFunction::SkewX
975
0
            && transform.function() != CSS::TransformFunction::SkewY)
976
0
            is_2d_transform = false;
977
0
    }
978
979
    // 5. Transform all <transform-function>s to 4x4 abstract matrices by following the “Mathematical Description of Transform Functions”. [CSS3-TRANSFORMS]
980
    // 6. Let matrix be a 4x4 abstract matrix as shown in the initial figure of this section. Post-multiply all matrices from left to right and set matrix to this product.
981
0
    for (auto const& transform : parsed_value) {
982
0
        auto const& transform_matrix = transform.to_matrix({});
983
0
        if (transform_matrix.is_error())
984
0
            return WebIDL::SyntaxError::create(realm, "Failed to parse CSS transform string."_string);
985
0
        matrix = matrix * transform_matrix.value();
986
0
    }
987
988
    // 7. Return matrix and 2dTransform.
989
0
    auto* elements = matrix.elements();
990
0
    Gfx::DoubleMatrix4x4 double_matrix {
991
0
        static_cast<double>(elements[0][0]), static_cast<double>(elements[0][1]), static_cast<double>(elements[0][2]), static_cast<double>(elements[0][3]),
992
0
        static_cast<double>(elements[1][0]), static_cast<double>(elements[1][1]), static_cast<double>(elements[1][2]), static_cast<double>(elements[1][3]),
993
0
        static_cast<double>(elements[2][0]), static_cast<double>(elements[2][1]), static_cast<double>(elements[2][2]), static_cast<double>(elements[2][3]),
994
0
        static_cast<double>(elements[3][0]), static_cast<double>(elements[3][1]), static_cast<double>(elements[3][2]), static_cast<float>(elements[3][3])
995
0
    };
996
0
    return ParsedMatrix { double_matrix, is_2d_transform };
997
0
}
998
999
}