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