Skip to main content

script/dom/
dommatrix.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use dom_struct::dom_struct;
6use euclid::default::Transform3D;
7use js::context::JSContext;
8use js::rust::{CustomAutoRooterGuard, HandleObject};
9use js::typedarray::{Float32Array, Float64Array};
10use rustc_hash::FxHashMap;
11use script_bindings::reflector::reflect_dom_object_with_proto;
12use script_bindings::str::DOMString;
13use servo_base::id::{DomMatrixId, DomMatrixIndex};
14use servo_constellation_traits::DomMatrix;
15
16use crate::dom::bindings::codegen::Bindings::DOMMatrixBinding::{DOMMatrixInit, DOMMatrixMethods};
17use crate::dom::bindings::codegen::Bindings::DOMMatrixReadOnlyBinding::DOMMatrixReadOnlyMethods;
18use crate::dom::bindings::codegen::UnionTypes::StringOrUnrestrictedDoubleSequence;
19use crate::dom::bindings::error;
20use crate::dom::bindings::error::Fallible;
21use crate::dom::bindings::inheritance::Castable;
22use crate::dom::bindings::root::DomRoot;
23use crate::dom::bindings::serializable::Serializable;
24use crate::dom::bindings::structuredclone::StructuredData;
25use crate::dom::dommatrixreadonly::{
26    DOMMatrixReadOnly, dommatrixinit_to_matrix, entries_to_matrix, transform_to_matrix,
27};
28use crate::dom::globalscope::GlobalScope;
29use crate::dom::window::Window;
30use crate::script_runtime::CanGc;
31
32#[dom_struct]
33pub(crate) struct DOMMatrix {
34    parent: DOMMatrixReadOnly,
35}
36
37#[expect(non_snake_case)]
38impl DOMMatrix {
39    pub(crate) fn new(
40        global: &GlobalScope,
41        is2D: bool,
42        matrix: Transform3D<f64>,
43        can_gc: CanGc,
44    ) -> DomRoot<Self> {
45        Self::new_with_proto(global, None, is2D, matrix, can_gc)
46    }
47
48    #[cfg_attr(crown, expect(crown::unrooted_must_root))]
49    fn new_with_proto(
50        global: &GlobalScope,
51        proto: Option<HandleObject>,
52        is2D: bool,
53        matrix: Transform3D<f64>,
54        can_gc: CanGc,
55    ) -> DomRoot<Self> {
56        let dommatrix = Self::new_inherited(is2D, matrix);
57        reflect_dom_object_with_proto(Box::new(dommatrix), global, proto, can_gc)
58    }
59
60    pub(crate) fn new_inherited(is2D: bool, matrix: Transform3D<f64>) -> Self {
61        DOMMatrix {
62            parent: DOMMatrixReadOnly::new_inherited(is2D, matrix),
63        }
64    }
65
66    pub(crate) fn from_readonly(
67        global: &GlobalScope,
68        ro: &DOMMatrixReadOnly,
69        can_gc: CanGc,
70    ) -> DomRoot<Self> {
71        Self::new(global, ro.is2D(), *ro.matrix(), can_gc)
72    }
73}
74
75#[expect(non_snake_case)]
76impl DOMMatrixMethods<crate::DomTypeHolder> for DOMMatrix {
77    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-dommatrixreadonly>
78    fn Constructor(
79        global: &GlobalScope,
80        proto: Option<HandleObject>,
81        can_gc: CanGc,
82        init: Option<StringOrUnrestrictedDoubleSequence>,
83    ) -> Fallible<DomRoot<Self>> {
84        if init.is_none() {
85            return Ok(Self::new_with_proto(
86                global,
87                proto,
88                true,
89                Transform3D::identity(),
90                can_gc,
91            ));
92        }
93        match init.unwrap() {
94            StringOrUnrestrictedDoubleSequence::String(ref s) => {
95                if !global.is::<Window>() {
96                    return Err(error::Error::Type(
97                        c"String constructor is only supported in the main thread.".to_owned(),
98                    ));
99                }
100                if s.is_empty() {
101                    return Ok(Self::new(global, true, Transform3D::identity(), can_gc));
102                }
103                transform_to_matrix(&s.str())
104                    .map(|(is2D, matrix)| Self::new_with_proto(global, proto, is2D, matrix, can_gc))
105            },
106            StringOrUnrestrictedDoubleSequence::UnrestrictedDoubleSequence(ref entries) => {
107                entries_to_matrix(&entries[..])
108                    .map(|(is2D, matrix)| Self::new_with_proto(global, proto, is2D, matrix, can_gc))
109            },
110        }
111    }
112
113    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrix-frommatrix>
114    fn FromMatrix(
115        global: &GlobalScope,
116        other: &DOMMatrixInit,
117        can_gc: CanGc,
118    ) -> Fallible<DomRoot<Self>> {
119        dommatrixinit_to_matrix(other).map(|(is2D, matrix)| Self::new(global, is2D, matrix, can_gc))
120    }
121
122    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrix-fromfloat32array>
123    fn FromFloat32Array(
124        global: &GlobalScope,
125        array: CustomAutoRooterGuard<Float32Array>,
126        can_gc: CanGc,
127    ) -> Fallible<DomRoot<DOMMatrix>> {
128        let vec: Vec<f64> = array.to_vec().iter().map(|&x| x as f64).collect();
129        DOMMatrix::Constructor(
130            global,
131            None,
132            can_gc,
133            Some(StringOrUnrestrictedDoubleSequence::UnrestrictedDoubleSequence(vec)),
134        )
135    }
136
137    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrix-fromfloat64array>
138    fn FromFloat64Array(
139        global: &GlobalScope,
140        array: CustomAutoRooterGuard<Float64Array>,
141        can_gc: CanGc,
142    ) -> Fallible<DomRoot<DOMMatrix>> {
143        let vec: Vec<f64> = array.to_vec();
144        DOMMatrix::Constructor(
145            global,
146            None,
147            can_gc,
148            Some(StringOrUnrestrictedDoubleSequence::UnrestrictedDoubleSequence(vec)),
149        )
150    }
151
152    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m11>
153    fn M11(&self) -> f64 {
154        self.upcast::<DOMMatrixReadOnly>().M11()
155    }
156
157    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m11>
158    fn SetM11(&self, value: f64) {
159        self.upcast::<DOMMatrixReadOnly>().set_m11(value);
160    }
161
162    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m12>
163    fn M12(&self) -> f64 {
164        self.upcast::<DOMMatrixReadOnly>().M12()
165    }
166
167    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m12>
168    fn SetM12(&self, value: f64) {
169        self.upcast::<DOMMatrixReadOnly>().set_m12(value);
170    }
171
172    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m13>
173    fn M13(&self) -> f64 {
174        self.upcast::<DOMMatrixReadOnly>().M13()
175    }
176
177    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m13>
178    fn SetM13(&self, value: f64) {
179        self.upcast::<DOMMatrixReadOnly>().set_m13(value);
180    }
181
182    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m14>
183    fn M14(&self) -> f64 {
184        self.upcast::<DOMMatrixReadOnly>().M14()
185    }
186
187    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m14>
188    fn SetM14(&self, value: f64) {
189        self.upcast::<DOMMatrixReadOnly>().set_m14(value);
190    }
191
192    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m21>
193    fn M21(&self) -> f64 {
194        self.upcast::<DOMMatrixReadOnly>().M21()
195    }
196
197    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m21>
198    fn SetM21(&self, value: f64) {
199        self.upcast::<DOMMatrixReadOnly>().set_m21(value);
200    }
201
202    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m22>
203    fn M22(&self) -> f64 {
204        self.upcast::<DOMMatrixReadOnly>().M22()
205    }
206
207    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m22>
208    fn SetM22(&self, value: f64) {
209        self.upcast::<DOMMatrixReadOnly>().set_m22(value);
210    }
211
212    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m23>
213    fn M23(&self) -> f64 {
214        self.upcast::<DOMMatrixReadOnly>().M23()
215    }
216
217    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m23>
218    fn SetM23(&self, value: f64) {
219        self.upcast::<DOMMatrixReadOnly>().set_m23(value);
220    }
221
222    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m24>
223    fn M24(&self) -> f64 {
224        self.upcast::<DOMMatrixReadOnly>().M24()
225    }
226
227    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m24>
228    fn SetM24(&self, value: f64) {
229        self.upcast::<DOMMatrixReadOnly>().set_m24(value);
230    }
231
232    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m31>
233    fn M31(&self) -> f64 {
234        self.upcast::<DOMMatrixReadOnly>().M31()
235    }
236
237    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m31>
238    fn SetM31(&self, value: f64) {
239        self.upcast::<DOMMatrixReadOnly>().set_m31(value);
240    }
241
242    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m32>
243    fn M32(&self) -> f64 {
244        self.upcast::<DOMMatrixReadOnly>().M32()
245    }
246
247    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m32>
248    fn SetM32(&self, value: f64) {
249        self.upcast::<DOMMatrixReadOnly>().set_m32(value);
250    }
251
252    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m33>
253    fn M33(&self) -> f64 {
254        self.upcast::<DOMMatrixReadOnly>().M33()
255    }
256
257    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m33>
258    fn SetM33(&self, value: f64) {
259        self.upcast::<DOMMatrixReadOnly>().set_m33(value);
260    }
261
262    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m34>
263    fn M34(&self) -> f64 {
264        self.upcast::<DOMMatrixReadOnly>().M34()
265    }
266
267    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m34>
268    fn SetM34(&self, value: f64) {
269        self.upcast::<DOMMatrixReadOnly>().set_m34(value);
270    }
271
272    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m41>
273    fn M41(&self) -> f64 {
274        self.upcast::<DOMMatrixReadOnly>().M41()
275    }
276
277    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m41>
278    fn SetM41(&self, value: f64) {
279        self.upcast::<DOMMatrixReadOnly>().set_m41(value);
280    }
281
282    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m42>
283    fn M42(&self) -> f64 {
284        self.upcast::<DOMMatrixReadOnly>().M42()
285    }
286
287    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m42>
288    fn SetM42(&self, value: f64) {
289        self.upcast::<DOMMatrixReadOnly>().set_m42(value);
290    }
291
292    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m43>
293    fn M43(&self) -> f64 {
294        self.upcast::<DOMMatrixReadOnly>().M43()
295    }
296
297    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m43>
298    fn SetM43(&self, value: f64) {
299        self.upcast::<DOMMatrixReadOnly>().set_m43(value);
300    }
301
302    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m44>
303    fn M44(&self) -> f64 {
304        self.upcast::<DOMMatrixReadOnly>().M44()
305    }
306
307    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m44>
308    fn SetM44(&self, value: f64) {
309        self.upcast::<DOMMatrixReadOnly>().set_m44(value);
310    }
311
312    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-a>
313    fn A(&self) -> f64 {
314        self.upcast::<DOMMatrixReadOnly>().A()
315    }
316
317    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-a>
318    fn SetA(&self, value: f64) {
319        self.upcast::<DOMMatrixReadOnly>().set_m11(value);
320    }
321
322    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-b>
323    fn B(&self) -> f64 {
324        self.upcast::<DOMMatrixReadOnly>().B()
325    }
326
327    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-b>
328    fn SetB(&self, value: f64) {
329        self.upcast::<DOMMatrixReadOnly>().set_m12(value);
330    }
331
332    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-c>
333    fn C(&self) -> f64 {
334        self.upcast::<DOMMatrixReadOnly>().C()
335    }
336
337    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-c>
338    fn SetC(&self, value: f64) {
339        self.upcast::<DOMMatrixReadOnly>().set_m21(value);
340    }
341
342    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-d>
343    fn D(&self) -> f64 {
344        self.upcast::<DOMMatrixReadOnly>().D()
345    }
346
347    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-d>
348    fn SetD(&self, value: f64) {
349        self.upcast::<DOMMatrixReadOnly>().set_m22(value);
350    }
351
352    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-e>
353    fn E(&self) -> f64 {
354        self.upcast::<DOMMatrixReadOnly>().E()
355    }
356
357    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-e>
358    fn SetE(&self, value: f64) {
359        self.upcast::<DOMMatrixReadOnly>().set_m41(value);
360    }
361
362    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-f>
363    fn F(&self) -> f64 {
364        self.upcast::<DOMMatrixReadOnly>().F()
365    }
366
367    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-f>
368    fn SetF(&self, value: f64) {
369        self.upcast::<DOMMatrixReadOnly>().set_m42(value);
370    }
371
372    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrix-multiplyself>
373    fn MultiplySelf(&self, other: &DOMMatrixInit) -> Fallible<DomRoot<DOMMatrix>> {
374        // Steps 1-3.
375        self.upcast::<DOMMatrixReadOnly>()
376            .multiply_self(other)
377            // Step 4.
378            .and(Ok(DomRoot::from_ref(self)))
379    }
380
381    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrix-premultiplyself>
382    fn PreMultiplySelf(&self, other: &DOMMatrixInit) -> Fallible<DomRoot<DOMMatrix>> {
383        // Steps 1-3.
384        self.upcast::<DOMMatrixReadOnly>()
385            .pre_multiply_self(other)
386            // Step 4.
387            .and(Ok(DomRoot::from_ref(self)))
388    }
389
390    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrix-translateself>
391    fn TranslateSelf(&self, tx: f64, ty: f64, tz: f64) -> DomRoot<DOMMatrix> {
392        // Steps 1-2.
393        self.upcast::<DOMMatrixReadOnly>()
394            .translate_self(tx, ty, tz);
395        // Step 3.
396        DomRoot::from_ref(self)
397    }
398
399    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrix-scaleself>
400    fn ScaleSelf(
401        &self,
402        scaleX: f64,
403        scaleY: Option<f64>,
404        scaleZ: f64,
405        originX: f64,
406        originY: f64,
407        originZ: f64,
408    ) -> DomRoot<DOMMatrix> {
409        // Steps 1-6.
410        self.upcast::<DOMMatrixReadOnly>()
411            .scale_self(scaleX, scaleY, scaleZ, originX, originY, originZ);
412        // Step 7.
413        DomRoot::from_ref(self)
414    }
415
416    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrix-scale3dself>
417    fn Scale3dSelf(
418        &self,
419        scale: f64,
420        originX: f64,
421        originY: f64,
422        originZ: f64,
423    ) -> DomRoot<DOMMatrix> {
424        // Steps 1-4.
425        self.upcast::<DOMMatrixReadOnly>()
426            .scale_3d_self(scale, originX, originY, originZ);
427        // Step 5.
428        DomRoot::from_ref(self)
429    }
430
431    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrix-rotateself>
432    fn RotateSelf(&self, rotX: f64, rotY: Option<f64>, rotZ: Option<f64>) -> DomRoot<DOMMatrix> {
433        // Steps 1-7.
434        self.upcast::<DOMMatrixReadOnly>()
435            .rotate_self(rotX, rotY, rotZ);
436        // Step 8.
437        DomRoot::from_ref(self)
438    }
439
440    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrix-rotatefromvectorself>
441    fn RotateFromVectorSelf(&self, x: f64, y: f64) -> DomRoot<DOMMatrix> {
442        // Step 1.
443        self.upcast::<DOMMatrixReadOnly>()
444            .rotate_from_vector_self(x, y);
445        // Step 2.
446        DomRoot::from_ref(self)
447    }
448
449    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrix-rotateaxisangleself>
450    fn RotateAxisAngleSelf(&self, x: f64, y: f64, z: f64, angle: f64) -> DomRoot<DOMMatrix> {
451        // Steps 1-2.
452        self.upcast::<DOMMatrixReadOnly>()
453            .rotate_axis_angle_self(x, y, z, angle);
454        // Step 3.
455        DomRoot::from_ref(self)
456    }
457
458    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrix-skewxself>
459    fn SkewXSelf(&self, sx: f64) -> DomRoot<DOMMatrix> {
460        // Step 1.
461        self.upcast::<DOMMatrixReadOnly>().skew_x_self(sx);
462        // Step 2.
463        DomRoot::from_ref(self)
464    }
465
466    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrix-skewyself>
467    fn SkewYSelf(&self, sy: f64) -> DomRoot<DOMMatrix> {
468        // Step 1.
469        self.upcast::<DOMMatrixReadOnly>().skew_y_self(sy);
470        // Step 2.
471        DomRoot::from_ref(self)
472    }
473
474    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrix-invertself>
475    fn InvertSelf(&self) -> DomRoot<DOMMatrix> {
476        // Steps 1-2.
477        self.upcast::<DOMMatrixReadOnly>().invert_self();
478        // Step 3.
479        DomRoot::from_ref(self)
480    }
481
482    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrix-setmatrixvalue>
483    fn SetMatrixValue(&self, transformList: DOMString) -> Fallible<DomRoot<DOMMatrix>> {
484        // 1. Parse transformList into an abstract matrix, and let
485        // matrix and 2dTransform be the result. If the result is failure,
486        // then throw a "SyntaxError" DOMException.
487        match transform_to_matrix(&transformList.str()) {
488            Ok(tuple) => {
489                // 2. Set is 2D to the value of 2dTransform.
490                self.parent.set_is2D(tuple.0);
491                // 3. Set m11 element through m44 element to the element values of matrix in column-major order.
492                self.parent.set_matrix(tuple.1);
493            },
494            Err(error) => return Err(error),
495        }
496
497        // 4. Return the current matrix.
498        Ok(DomRoot::from_ref(self))
499    }
500}
501
502impl Serializable for DOMMatrix {
503    type Index = DomMatrixIndex;
504    type Data = DomMatrix;
505
506    fn serialize(&self) -> Result<(DomMatrixId, Self::Data), ()> {
507        let serialized = if self.parent.is2D() {
508            DomMatrix {
509                matrix: Transform3D::new(
510                    self.M11(),
511                    self.M12(),
512                    f64::NAN,
513                    f64::NAN,
514                    self.M21(),
515                    self.M22(),
516                    f64::NAN,
517                    f64::NAN,
518                    f64::NAN,
519                    f64::NAN,
520                    f64::NAN,
521                    f64::NAN,
522                    self.M41(),
523                    self.M42(),
524                    f64::NAN,
525                    f64::NAN,
526                ),
527                is_2d: true,
528            }
529        } else {
530            DomMatrix {
531                matrix: *self.parent.matrix(),
532                is_2d: false,
533            }
534        };
535        Ok((DomMatrixId::new(), serialized))
536    }
537
538    fn deserialize(
539        cx: &mut JSContext,
540        owner: &GlobalScope,
541        serialized: Self::Data,
542    ) -> Result<DomRoot<Self>, ()>
543    where
544        Self: Sized,
545    {
546        if serialized.is_2d {
547            Ok(Self::new(
548                owner,
549                true,
550                Transform3D::new(
551                    serialized.matrix.m11,
552                    serialized.matrix.m12,
553                    0.0,
554                    0.0,
555                    serialized.matrix.m21,
556                    serialized.matrix.m22,
557                    0.0,
558                    0.0,
559                    0.0,
560                    0.0,
561                    1.0,
562                    0.0,
563                    serialized.matrix.m41,
564                    serialized.matrix.m42,
565                    0.0,
566                    1.0,
567                ),
568                CanGc::from_cx(cx),
569            ))
570        } else {
571            Ok(Self::new(
572                owner,
573                false,
574                serialized.matrix,
575                CanGc::from_cx(cx),
576            ))
577        }
578    }
579
580    fn serialized_storage<'a>(
581        data: StructuredData<'a, '_>,
582    ) -> &'a mut Option<FxHashMap<DomMatrixId, Self::Data>> {
583        match data {
584            StructuredData::Reader(reader) => &mut reader.matrices,
585            StructuredData::Writer(writer) => &mut writer.matrices,
586        }
587    }
588}