Coverage Report

Created: 2026-10-28 07:05

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