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/Geometry/DOMMatrix.cpp
Line
Count
Source
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
 *
5
 * SPDX-License-Identifier: BSD-2-Clause
6
 */
7
8
#include <LibJS/Runtime/TypedArray.h>
9
#include <LibWeb/Bindings/DOMMatrixPrototype.h>
10
#include <LibWeb/Bindings/Intrinsics.h>
11
#include <LibWeb/Geometry/DOMMatrix.h>
12
#include <LibWeb/HTML/Window.h>
13
#include <LibWeb/WebIDL/Buffers.h>
14
#include <LibWeb/WebIDL/ExceptionOr.h>
15
16
namespace Web::Geometry {
17
18
JS_DEFINE_ALLOCATOR(DOMMatrix);
19
20
// https://drafts.fxtf.org/geometry/#dom-dommatrix-dommatrix
21
WebIDL::ExceptionOr<JS::NonnullGCPtr<DOMMatrix>> DOMMatrix::construct_impl(JS::Realm& realm, Optional<Variant<String, Vector<double>>> const& init)
22
0
{
23
0
    auto& vm = realm.vm();
24
25
    // -> If init is omitted
26
0
    if (!init.has_value()) {
27
        // 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].
28
0
        return realm.heap().allocate<DOMMatrix>(realm, realm, 1, 0, 0, 1, 0, 0);
29
0
    }
30
31
0
    auto const& init_value = init.value();
32
33
    // -> If init is a DOMString
34
0
    if (init_value.has<String>()) {
35
        // 1. If current global object is not a Window object, then throw a TypeError exception.
36
0
        if (!is<HTML::Window>(realm.global_object()))
37
0
            return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "This can only be used in a Window context"_string };
38
39
        // 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.
40
0
        auto result = TRY(parse_dom_matrix_init_string(realm, init_value.get<String>()));
41
0
        auto* elements = result.matrix.elements();
42
43
        // If 2dTransform is true
44
0
        if (result.is_2d_transform) {
45
            // 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.
46
0
            return realm.heap().allocate<DOMMatrix>(realm, realm, elements[0][0], elements[1][0], elements[0][1], elements[1][1], elements[0][3], elements[1][3]);
47
0
        }
48
49
        // 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.
50
0
        return realm.heap().allocate<DOMMatrix>(realm, realm,
51
0
            elements[0][0], elements[1][0], elements[2][0], elements[3][0],
52
0
            elements[0][1], elements[1][1], elements[2][1], elements[3][1],
53
0
            elements[0][2], elements[1][2], elements[2][2], elements[3][2],
54
0
            elements[0][3], elements[1][3], elements[2][3], elements[3][3]);
55
0
    }
56
57
0
    auto const& double_sequence = init_value.get<Vector<double>>();
58
59
    // -> If init is a sequence with 6 elements
60
0
    if (double_sequence.size() == 6) {
61
        // Return the result of invoking create a 2d matrix of type DOMMatrixReadOnly or DOMMatrix as appropriate, with the sequence init.
62
0
        return realm.heap().allocate<DOMMatrix>(realm, realm, double_sequence[0], double_sequence[1], double_sequence[2], double_sequence[3], double_sequence[4], double_sequence[5]);
63
0
    }
64
65
    // -> If init is a sequence with 16 elements
66
0
    if (double_sequence.size() == 16) {
67
        // Return the result of invoking create a 3d matrix of type DOMMatrixReadOnly or DOMMatrix as appropriate, with the sequence init.
68
0
        return realm.heap().allocate<DOMMatrix>(realm, realm,
69
0
            double_sequence[0], double_sequence[1], double_sequence[2], double_sequence[3],
70
0
            double_sequence[4], double_sequence[5], double_sequence[6], double_sequence[7],
71
0
            double_sequence[8], double_sequence[9], double_sequence[10], double_sequence[11],
72
0
            double_sequence[12], double_sequence[13], double_sequence[14], double_sequence[15]);
73
0
    }
74
75
    // -> Otherwise, throw a TypeError exception.
76
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())) };
77
0
}
78
79
// https://drafts.fxtf.org/geometry/#create-a-dommatrix-from-the-2d-dictionary
80
WebIDL::ExceptionOr<JS::NonnullGCPtr<DOMMatrix>> DOMMatrix::create_from_dom_matrix_2d_init(JS::Realm& realm, DOMMatrix2DInit& init)
81
0
{
82
    // 1. Validate and fixup (2D) other.
83
0
    TRY(validate_and_fixup_dom_matrix_2d_init(init));
84
85
    // These should all have values after calling `validate_and_fixup_dom_matrix_2d_init`
86
0
    VERIFY(init.m11.has_value());
87
0
    VERIFY(init.m12.has_value());
88
0
    VERIFY(init.m21.has_value());
89
0
    VERIFY(init.m22.has_value());
90
0
    VERIFY(init.m41.has_value());
91
0
    VERIFY(init.m42.has_value());
92
93
    // 2. Return the result of invoking create a 2d matrix of type DOMMatrixReadOnly or DOMMatrix as appropriate, with a sequence of numbers,
94
    //    the values being the 6 elements m11, m12, m21, m22, m41 and m42 of other in the given order.
95
0
    return realm.heap().allocate<DOMMatrix>(realm, realm, init.m11.value(), init.m12.value(), init.m21.value(), init.m22.value(), init.m41.value(), init.m42.value());
96
0
}
97
98
// https://drafts.fxtf.org/geometry/#create-a-dommatrix-from-the-dictionary
99
WebIDL::ExceptionOr<JS::NonnullGCPtr<DOMMatrix>> DOMMatrix::create_from_dom_matrix_init(JS::Realm& realm, DOMMatrixInit& init)
100
0
{
101
    // 1. Validate and fixup other.
102
0
    TRY(validate_and_fixup_dom_matrix_init(init));
103
104
    // 2. If the is2D dictionary member of other is true.
105
0
    if (init.is2d.has_value() && init.is2d.value()) {
106
        // 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.
107
0
        return realm.heap().allocate<DOMMatrix>(realm, realm, init.m11.value(), init.m12.value(), init.m21.value(), init.m22.value(), init.m41.value(), init.m42.value());
108
0
    }
109
110
    // 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.
111
0
    return realm.heap().allocate<DOMMatrix>(realm, realm, init.m11.value(), init.m12.value(), init.m13, init.m14,
112
0
        init.m21.value(), init.m22.value(), init.m23, init.m24,
113
0
        init.m31, init.m32, init.m33, init.m34,
114
0
        init.m41.value(), init.m42.value(), init.m43, init.m44);
115
0
}
116
117
JS::NonnullGCPtr<DOMMatrix> DOMMatrix::create_from_dom_matrix_read_only(JS::Realm& realm, DOMMatrixReadOnly const& read_only_matrix)
118
0
{
119
0
    return realm.heap().allocate<DOMMatrix>(realm, realm, read_only_matrix);
120
0
}
121
122
JS::NonnullGCPtr<DOMMatrix> DOMMatrix::create(JS::Realm& realm)
123
0
{
124
0
    return realm.heap().allocate<DOMMatrix>(realm, realm);
125
0
}
126
127
DOMMatrix::DOMMatrix(JS::Realm& realm, double m11, double m12, double m21, double m22, double m41, double m42)
128
0
    : DOMMatrixReadOnly(realm, m11, m12, m21, m22, m41, m42)
129
0
{
130
0
}
131
132
DOMMatrix::DOMMatrix(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)
133
0
    : DOMMatrixReadOnly(realm, m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44)
134
0
{
135
0
}
136
137
DOMMatrix::DOMMatrix(JS::Realm& realm, DOMMatrixReadOnly const& read_only_matrix)
138
0
    : DOMMatrixReadOnly(realm, read_only_matrix)
139
0
{
140
0
}
141
142
DOMMatrix::DOMMatrix(JS::Realm& realm)
143
0
    : DOMMatrixReadOnly(realm)
144
0
{
145
0
}
146
147
0
DOMMatrix::~DOMMatrix() = default;
148
149
void DOMMatrix::initialize(JS::Realm& realm)
150
0
{
151
0
    Base::initialize(realm);
152
0
    WEB_SET_PROTOTYPE_FOR_INTERFACE(DOMMatrix);
153
0
}
154
155
// https://drafts.fxtf.org/geometry/#dom-dommatrix-frommatrix
156
WebIDL::ExceptionOr<JS::NonnullGCPtr<DOMMatrix>> DOMMatrix::from_matrix(JS::VM& vm, DOMMatrixInit other)
157
0
{
158
0
    return create_from_dom_matrix_init(*vm.current_realm(), other);
159
0
}
160
161
// https://drafts.fxtf.org/geometry/#dom-dommatrix-fromfloat32array
162
WebIDL::ExceptionOr<JS::NonnullGCPtr<DOMMatrix>> DOMMatrix::from_float32_array(JS::VM& vm, JS::Handle<WebIDL::BufferSource> const& array32)
163
0
{
164
0
    if (!is<JS::Float32Array>(*array32->raw_object()))
165
0
        return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "Float32Array");
166
167
0
    auto& realm = *vm.current_realm();
168
0
    auto& float32_array = static_cast<JS::Float32Array&>(*array32->raw_object());
169
0
    ReadonlySpan<float> elements = float32_array.data();
170
171
    // 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.
172
0
    if (elements.size() == 6)
173
0
        return realm.heap().allocate<DOMMatrix>(realm, realm, elements.at(0), elements.at(1), elements.at(2), elements.at(3), elements.at(4), elements.at(5));
174
175
    // 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.
176
0
    if (elements.size() == 16)
177
0
        return realm.heap().allocate<DOMMatrix>(realm, realm, elements.at(0), elements.at(1), elements.at(2), elements.at(3),
178
0
            elements.at(4), elements.at(5), elements.at(6), elements.at(7),
179
0
            elements.at(8), elements.at(9), elements.at(10), elements.at(11),
180
0
            elements.at(12), elements.at(13), elements.at(14), elements.at(15));
181
182
    // Otherwise, throw a TypeError exception.
183
0
    return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Expected a Float32Array argument with 6 or 16 elements"_string };
184
0
}
185
186
// https://drafts.fxtf.org/geometry/#dom-dommatrix-fromfloat64array
187
WebIDL::ExceptionOr<JS::NonnullGCPtr<DOMMatrix>> DOMMatrix::from_float64_array(JS::VM& vm, JS::Handle<WebIDL::BufferSource> const& array64)
188
0
{
189
0
    if (!is<JS::Float64Array>(*array64->raw_object()))
190
0
        return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "Float64Array");
191
192
0
    auto& realm = *vm.current_realm();
193
0
    auto& float64_array = static_cast<JS::Float64Array&>(*array64->raw_object());
194
0
    ReadonlySpan<double> elements = float64_array.data();
195
196
    // 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.
197
0
    if (elements.size() == 6)
198
0
        return realm.heap().allocate<DOMMatrix>(realm, realm, elements.at(0), elements.at(1), elements.at(2), elements.at(3), elements.at(4), elements.at(5));
199
200
    // 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.
201
0
    if (elements.size() == 16)
202
0
        return realm.heap().allocate<DOMMatrix>(realm, realm, elements.at(0), elements.at(1), elements.at(2), elements.at(3),
203
0
            elements.at(4), elements.at(5), elements.at(6), elements.at(7),
204
0
            elements.at(8), elements.at(9), elements.at(10), elements.at(11),
205
0
            elements.at(12), elements.at(13), elements.at(14), elements.at(15));
206
207
    // Otherwise, throw a TypeError exception.
208
0
    return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "Expected a Float64Array argument with 6 or 16 elements"_string };
209
0
}
210
211
// https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-m11
212
void DOMMatrix::set_m11(double value)
213
0
{
214
    // For the DOMMatrix interface, setting the m11 or the a attribute must set the m11 element to the new value.
215
0
    m_matrix.elements()[0][0] = value;
216
0
}
217
218
// https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-m12
219
void DOMMatrix::set_m12(double value)
220
0
{
221
    // For the DOMMatrix interface, setting the m12 or the b attribute must set the m12 element to the new value.
222
0
    m_matrix.elements()[1][0] = value;
223
0
}
224
225
// https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-m13
226
void DOMMatrix::set_m13(double value)
227
0
{
228
    // For the DOMMatrix interface, setting the m13 attribute must set the m13 element to the new value and, if the new value is not 0 or -0, set is 2D to false.
229
0
    m_matrix.elements()[2][0] = value;
230
0
    if (value != 0.0 && value != -0.0)
231
0
        m_is_2d = false;
232
0
}
233
234
// https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-m14
235
void DOMMatrix::set_m14(double value)
236
0
{
237
    // For the DOMMatrix interface, setting the m14 attribute must set the m14 element to the new value and, if the new value is not 0 or -0, set is 2D to false.
238
0
    m_matrix.elements()[3][0] = value;
239
0
    if (value != 0.0 && value != -0.0)
240
0
        m_is_2d = false;
241
0
}
242
243
// https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-m21
244
void DOMMatrix::set_m21(double value)
245
0
{
246
    // For the DOMMatrix interface, setting the m21 or the c attribute must set the m21 element to the new value.
247
0
    m_matrix.elements()[0][1] = value;
248
0
}
249
250
// https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-m22
251
void DOMMatrix::set_m22(double value)
252
0
{
253
    // For the DOMMatrix interface, setting the m22 or the d attribute must set the m22 element to the new value.
254
0
    m_matrix.elements()[1][1] = value;
255
0
}
256
257
// https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-m23
258
void DOMMatrix::set_m23(double value)
259
0
{
260
    // For the DOMMatrix interface, setting the m23 attribute must set the m23 element to the new value and, if the new value is not 0 or -0, set is 2D to false.
261
0
    m_matrix.elements()[2][1] = value;
262
0
    if (value != 0.0 && value != -0.0)
263
0
        m_is_2d = false;
264
0
}
265
266
// https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-m24
267
void DOMMatrix::set_m24(double value)
268
0
{
269
    // For the DOMMatrix interface, setting the m24 attribute must set the m24 element to the new value and, if the new value is not 0 or -0, set is 2D to false.
270
0
    m_matrix.elements()[3][1] = value;
271
0
    if (value != 0.0 && value != -0.0)
272
0
        m_is_2d = false;
273
0
}
274
275
// https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-m31
276
void DOMMatrix::set_m31(double value)
277
0
{
278
    // For the DOMMatrix interface, setting the m31 attribute must set the m31 element to the new value and, if the new value is not 0 or -0, set is 2D to false.
279
0
    m_matrix.elements()[0][2] = value;
280
0
    if (value != 0.0 && value != -0.0)
281
0
        m_is_2d = false;
282
0
}
283
284
// https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-m32
285
void DOMMatrix::set_m32(double value)
286
0
{
287
    // For the DOMMatrix interface, setting the m32 attribute must set the m32 element to the new value and, if the new value is not 0 or -0, set is 2D to false.
288
0
    m_matrix.elements()[1][2] = value;
289
0
    if (value != 0.0 && value != -0.0)
290
0
        m_is_2d = false;
291
0
}
292
293
// https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-m33
294
void DOMMatrix::set_m33(double value)
295
0
{
296
    // For the DOMMatrix interface, setting the m33 attribute must set the m33 element to the new value and, if the new value is not 1, set is 2D to false.
297
0
    m_matrix.elements()[2][2] = value;
298
0
    if (value != 1.0)
299
0
        m_is_2d = false;
300
0
}
301
302
// https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-m34
303
void DOMMatrix::set_m34(double value)
304
0
{
305
    // For the DOMMatrix interface, setting the m34 attribute must set the m34 element to the new value and, if the new value is not 0 or -0, set is 2D to false.
306
0
    m_matrix.elements()[3][2] = value;
307
0
    if (value != 0.0 && value != -0.0)
308
0
        m_is_2d = false;
309
0
}
310
311
// https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-m41
312
void DOMMatrix::set_m41(double value)
313
0
{
314
    // For the DOMMatrix interface, setting the m41 or the e attribute must set the m41 element to the new value.
315
0
    m_matrix.elements()[0][3] = value;
316
0
}
317
318
// https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-m42
319
void DOMMatrix::set_m42(double value)
320
0
{
321
    // For the DOMMatrix interface, setting the m42 or the f attribute must set the m42 element to the new value.
322
0
    m_matrix.elements()[1][3] = value;
323
0
}
324
325
// https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-m43
326
void DOMMatrix::set_m43(double value)
327
0
{
328
    // For the DOMMatrix interface, setting the m43 attribute must set the m43 element to the new value and, if the new value is not 0 or -0, set is 2D to false.
329
0
    m_matrix.elements()[2][3] = value;
330
0
    if (value != 0.0 && value != -0.0)
331
0
        m_is_2d = false;
332
0
}
333
334
// https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-m44
335
void DOMMatrix::set_m44(double value)
336
0
{
337
    // For the DOMMatrix interface, setting the m44 attribute must set the m44 element to the new value and, if the new value is not 1, set is 2D to false.
338
0
    m_matrix.elements()[3][3] = value;
339
0
    if (value != 1.0)
340
0
        m_is_2d = false;
341
0
}
342
343
// https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-a
344
void DOMMatrix::set_a(double value)
345
0
{
346
    // For the DOMMatrix interface, setting the m11 or the a attribute must set the m11 element to the new value.
347
0
    set_m11(value);
348
0
}
349
350
// https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-b
351
void DOMMatrix::set_b(double value)
352
0
{
353
    // For the DOMMatrix interface, setting the m12 or the b attribute must set the m12 element to the new value.
354
0
    set_m12(value);
355
0
}
356
357
// https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-c
358
void DOMMatrix::set_c(double value)
359
0
{
360
    // For the DOMMatrix interface, setting the m21 or the c attribute must set the m21 element to the new value.
361
0
    set_m21(value);
362
0
}
363
364
// https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-d
365
void DOMMatrix::set_d(double value)
366
0
{
367
    // For the DOMMatrix interface, setting the m22 or the d attribute must set the m22 element to the new value.
368
0
    set_m22(value);
369
0
}
370
371
// https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-e
372
void DOMMatrix::set_e(double value)
373
0
{
374
    // For the DOMMatrix interface, setting the m41 or the e attribute must set the m41 element to the new value.
375
0
    set_m41(value);
376
0
}
377
378
// https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-f
379
void DOMMatrix::set_f(double value)
380
0
{
381
    // For the DOMMatrix interface, setting the m42 or the f attribute must set the m42 element to the new value.
382
0
    set_m42(value);
383
0
}
384
385
// https://drafts.fxtf.org/geometry/#dom-dommatrix-multiplyself
386
WebIDL::ExceptionOr<JS::NonnullGCPtr<DOMMatrix>> DOMMatrix::multiply_self(DOMMatrixInit other)
387
0
{
388
    // 1. Let otherObject be the result of invoking create a DOMMatrix from the dictionary other.
389
0
    auto other_object = TRY(DOMMatrix::create_from_dom_matrix_init(realm(), other));
390
391
    // 2. The otherObject matrix gets post-multiplied to the current matrix.
392
0
    m_matrix = m_matrix * other_object->m_matrix;
393
394
    // 3. If is 2D of otherObject is false, set is 2D of the current matrix to false.
395
0
    if (!other_object->m_is_2d)
396
0
        m_is_2d = false;
397
398
    // 4. Return the current matrix.
399
0
    return JS::NonnullGCPtr<DOMMatrix>(*this);
400
0
}
401
402
// https://drafts.fxtf.org/geometry/#dom-dommatrix-premultiplyself
403
WebIDL::ExceptionOr<JS::NonnullGCPtr<DOMMatrix>> DOMMatrix::pre_multiply_self(DOMMatrixInit other)
404
0
{
405
    // 1. Let otherObject be the result of invoking create a DOMMatrix from the dictionary other.
406
0
    auto other_object = TRY(DOMMatrix::create_from_dom_matrix_init(realm(), other));
407
408
    // 2. The otherObject matrix gets pre-multiplied to the current matrix.
409
0
    m_matrix = other_object->m_matrix * m_matrix;
410
411
    // 3. If is 2D of otherObject is false, set is 2D of the current matrix to false.
412
0
    if (!other_object->m_is_2d)
413
0
        m_is_2d = false;
414
415
    // 4. Return the current matrix.
416
0
    return JS::NonnullGCPtr<DOMMatrix>(*this);
417
0
}
418
419
// https://drafts.fxtf.org/geometry/#dom-dommatrix-translateself
420
JS::NonnullGCPtr<DOMMatrix> DOMMatrix::translate_self(Optional<double> tx, Optional<double> ty, Optional<double> tz)
421
0
{
422
    // 1. Post-multiply a translation transformation on the current matrix. The 3D translation matrix is described in CSS Transforms.
423
0
    m_matrix = m_matrix * Gfx::translation_matrix(Vector3<double> { tx.value_or(0), ty.value_or(0), tz.value_or(0) });
424
425
    // 2. If tz is specified and not 0 or -0, set is 2D of the current matrix to false.
426
0
    if (tz.has_value() && (tz != 0 || tz != -0))
427
0
        m_is_2d = false;
428
429
    // 3. Return the current matrix.
430
0
    return *this;
431
0
}
432
433
// https://drafts.fxtf.org/geometry/#dom-dommatrix-scaleself
434
JS::NonnullGCPtr<DOMMatrix> DOMMatrix::scale_self(Optional<double> scale_x, Optional<double> scale_y, Optional<double> scale_z, Optional<double> origin_x, Optional<double> origin_y, Optional<double> origin_z)
435
0
{
436
    // 1. Perform a translateSelf() transformation on the current matrix with the arguments originX, originY, originZ.
437
0
    translate_self(origin_x, origin_y, origin_z);
438
439
    // 2. If scaleY is missing, set scaleY to the value of scaleX.
440
0
    if (!scale_y.has_value())
441
0
        scale_y = scale_x.value_or(1);
442
443
    // 3. Post-multiply a non-uniform scale transformation on the current matrix. The 3D scale matrix is described in CSS Transforms with sx = scaleX, sy = scaleY and sz = scaleZ. [CSS3-TRANSFORMS]
444
0
    m_matrix = m_matrix * Gfx::scale_matrix(Vector3<double> { scale_x.value_or(1), scale_y.value(), scale_z.value_or(1) });
445
446
    // 4. Negate originX, originY and originZ.
447
    // 5. Perform a translateSelf() transformation on the current matrix with the arguments originX, originY, originZ.
448
0
    translate_self(-origin_x.value_or(0), -origin_y.value_or(0), -origin_z.value_or(0));
449
450
    // 6. If scaleZ is not 1, set is 2D of the current matrix to false.
451
0
    if (scale_z != 1)
452
0
        m_is_2d = false;
453
454
    // 7. Return the current matrix.
455
0
    return *this;
456
0
}
457
458
// https://drafts.fxtf.org/geometry/#dom-dommatrix-scale3dself
459
JS::NonnullGCPtr<DOMMatrix> DOMMatrix::scale3d_self(Optional<double> scale, Optional<double> origin_x, Optional<double> origin_y, Optional<double> origin_z)
460
0
{
461
    // 1. Apply a translateSelf() transformation to the current matrix with the arguments originX, originY, originZ.
462
0
    translate_self(origin_x, origin_y, origin_z);
463
464
    // 2. Post-multiply a uniform 3D scale transformation (m11 = m22 = m33 = scale) on the current matrix. The 3D scale matrix is described in CSS Transforms with sx = sy = sz = scale. [CSS3-TRANSFORMS]
465
0
    m_matrix = m_matrix * Gfx::scale_matrix(Vector3<double> { scale.value_or(1), scale.value_or(1), scale.value_or(1) });
466
467
    // 3. Apply a translateSelf() transformation to the current matrix with the arguments -originX, -originY, -originZ.
468
0
    translate_self(-origin_x.value_or(0), -origin_y.value_or(0), -origin_z.value_or(0));
469
470
    // 4. If scale is not 1, set is 2D of the current matrix to false.
471
0
    if (scale != 1)
472
0
        m_is_2d = false;
473
474
    // 5. Return the current matrix.
475
0
    return *this;
476
0
}
477
478
// https://drafts.fxtf.org/geometry/#dom-dommatrix-rotateself
479
JS::NonnullGCPtr<DOMMatrix> DOMMatrix::rotate_self(Optional<double> rot_x, Optional<double> rot_y, Optional<double> rot_z)
480
0
{
481
    // 1. If rotY and rotZ are both missing, set rotZ to the value of rotX and set rotX and rotY to 0.
482
0
    if (!rot_y.has_value() && !rot_z.has_value()) {
483
0
        rot_z = rot_x;
484
0
        rot_x = 0;
485
0
        rot_y = 0;
486
0
    }
487
488
    // 2. If rotY is still missing, set rotY to 0.
489
0
    if (!rot_y.has_value())
490
0
        rot_y = 0;
491
492
    // 3. If rotZ is still missing, set rotZ to 0.
493
0
    if (!rot_z.has_value())
494
0
        rot_z = 0;
495
496
    // 4. If rotX or rotY are not 0 or -0, set is 2D of the current matrix to false.
497
0
    if (rot_x != 0 || rot_x != -0 || rot_y != 0 || rot_y != -0)
498
0
        m_is_2d = false;
499
500
    // 5. Post-multiply a rotation transformation on the current matrix around the vector 0, 0, 1 by the specified rotation rotZ in degrees. The 3D rotation matrix is described in CSS Transforms with alpha = rotZ in degrees. [CSS3-TRANSFORMS]
501
0
    m_matrix = m_matrix * Gfx::rotation_matrix<double>(Vector3<double> { 0.0, 0.0, 1.0 }, AK::to_radians(rot_z.value()));
502
503
    // 6. Post-multiply a rotation transformation on the current matrix around the vector 0, 1, 0 by the specified rotation rotY in degrees. The 3D rotation matrix is described in CSS Transforms with alpha = rotY in degrees. [CSS3-TRANSFORMS]
504
0
    m_matrix = m_matrix * Gfx::rotation_matrix<double>(Vector3<double> { 0.0, 1.0, 0.0 }, AK::to_radians(rot_y.value()));
505
506
    // 7. Post-multiply a rotation transformation on the current matrix around the vector 1, 0, 0 by the specified rotation rotX in degrees. The 3D rotation matrix is described in CSS Transforms with alpha = rotX in degrees. [CSS3-TRANSFORMS]
507
0
    m_matrix = m_matrix * Gfx::rotation_matrix<double>(Vector3<double> { 1.0, 0.0, 0.0 }, AK::to_radians(rot_x.value()));
508
509
    // 8. Return the current matrix.
510
0
    return *this;
511
0
}
512
513
JS::NonnullGCPtr<DOMMatrix> DOMMatrix::rotate_from_vector_self(Optional<double> x, Optional<double> y)
514
0
{
515
    // 1. Post-multiply a rotation transformation on the current matrix.
516
    //    The rotation angle is determined by the angle between the vector (1,0)T and (x,y)T in the clockwise direction. If x and y should both be 0 or -0, the angle is specified as 0.
517
0
    double angle = (x == 0 || x == -0) && (y == 0 || y == -0) ? 0.0 : atan2(y.value_or(0), x.value_or(0));
518
519
    // The 2D rotation matrix is described in CSS Transforms where alpha is the angle between the vector (1,0)T and (x,y)T in degrees. [CSS3-TRANSFORMS]
520
0
    m_matrix = m_matrix * Gfx::rotation_matrix<double>(Vector3<double> { 0.0, 0.0, 1.0 }, angle);
521
522
    // 2. Return the current matrix.
523
0
    return *this;
524
0
}
525
526
JS::NonnullGCPtr<DOMMatrix> DOMMatrix::rotate_axis_angle_self(Optional<double> x, Optional<double> y, Optional<double> z, Optional<double> angle)
527
0
{
528
    // 1. Post-multiply a rotation transformation on the current matrix around the specified vector x, y, z by the specified rotation angle in degrees. The 3D rotation matrix is described in CSS Transforms with alpha = angle in degrees. [CSS3-TRANSFORMS]
529
0
    m_matrix = m_matrix * Gfx::rotation_matrix<double>(Vector3<double> { x.value_or(0), y.value_or(0), z.value_or(0) }.normalized(), AK::to_radians(angle.value()));
530
531
    // 2. If x or y are not 0 or -0, set is 2D of the current matrix to false.
532
0
    if (x != 0 || x != -0 || y != 0 || y != -0)
533
0
        m_is_2d = false;
534
535
    // 3. Return the current matrix.
536
0
    return *this;
537
0
}
538
539
// https://drafts.fxtf.org/geometry/#dom-dommatrix-skewxself
540
JS::NonnullGCPtr<DOMMatrix> DOMMatrix::skew_x_self(double sx)
541
0
{
542
    // 1. Post-multiply a skewX transformation on the current matrix by the specified angle sx in degrees. The 2D skewX matrix is described in CSS Transforms with alpha = sx in degrees. [CSS3-TRANSFORMS]
543
0
    Gfx::DoubleMatrix4x4 skew_matrix = {
544
0
        1, tan(AK::to_radians(sx)), 0, 0,
545
0
        0, 1, 0, 0,
546
0
        0, 0, 1, 0,
547
0
        0, 0, 0, 1
548
0
    };
549
0
    m_matrix = m_matrix * skew_matrix;
550
551
    // 3. Return the current matrix.
552
0
    return *this;
553
0
}
554
555
// https://drafts.fxtf.org/geometry/#dom-dommatrix-skewyself
556
JS::NonnullGCPtr<DOMMatrix> DOMMatrix::skew_y_self(double sy)
557
0
{
558
    // 1. Post-multiply a skewX transformation on the current matrix by the specified angle sy in degrees. The 2D skewY matrix is described in CSS Transforms with beta = sy in degrees. [CSS3-TRANSFORMS]
559
0
    Gfx::DoubleMatrix4x4 skew_matrix = {
560
0
        1, 0, 0, 0,
561
0
        tan(AK::to_radians(sy)), 1, 0, 0,
562
0
        0, 0, 1, 0,
563
0
        0, 0, 0, 1
564
0
    };
565
0
    m_matrix = m_matrix * skew_matrix;
566
567
    // 3. Return the current matrix.
568
0
    return *this;
569
0
}
570
571
// https://drafts.fxtf.org/geometry/#dom-dommatrix-invertself
572
JS::NonnullGCPtr<DOMMatrix> DOMMatrix::invert_self()
573
0
{
574
0
    bool is_invertible = m_matrix.is_invertible();
575
576
    // 1. Invert the current matrix.
577
0
    if (is_invertible)
578
0
        m_matrix = m_matrix.inverse();
579
580
    // 2. If the current matrix is not invertible set all attributes to NaN and set is 2D to false.
581
0
    if (!is_invertible) {
582
0
        for (u8 i = 0; i < 4; i++) {
583
0
            for (u8 j = 0; j < 4; j++)
584
0
                m_matrix.elements()[j][i] = NAN;
585
0
        }
586
0
        m_is_2d = false;
587
0
    }
588
589
    // 3. Return the current matrix.
590
0
    return *this;
591
0
}
592
593
// https://drafts.fxtf.org/geometry/#dom-dommatrix-setmatrixvalue
594
WebIDL::ExceptionOr<JS::NonnullGCPtr<DOMMatrix>> DOMMatrix::set_matrix_value(String const& transform_list)
595
0
{
596
    // 1. Parse transformList into an abstract matrix, and let matrix and 2dTransform be the result. If the result is failure, then throw a "SyntaxError" DOMException.
597
0
    auto result = TRY(parse_dom_matrix_init_string(realm(), transform_list));
598
599
    // 2. Set is 2D to the value of 2dTransform.
600
0
    m_is_2d = result.is_2d_transform;
601
602
    // 3. Set m11 element through m44 element to the element values of matrix in column-major order.
603
0
    m_matrix = result.matrix;
604
605
    // 4. Return the current matrix.
606
0
    return JS::NonnullGCPtr<DOMMatrix>(*this);
607
0
}
608
609
}