Skip to main content

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