script/dom/
dommatrixreadonly.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 std::cell::Cell;
6use std::{f64, ptr};
7
8use base::id::{DomMatrixId, DomMatrixIndex};
9use constellation_traits::DomMatrix;
10use cssparser::{Parser, ParserInput};
11use dom_struct::dom_struct;
12use euclid::Angle;
13use euclid::default::{Transform2D, Transform3D};
14use js::conversions::jsstr_to_string;
15use js::jsapi::JSObject;
16use js::jsval;
17use js::rust::{CustomAutoRooterGuard, HandleObject, ToString};
18use js::typedarray::{Float32Array, Float64Array, HeapFloat32Array, HeapFloat64Array};
19use rustc_hash::FxHashMap;
20use script_bindings::cformat;
21use script_bindings::trace::RootedTraceableBox;
22use style::stylesheets::CssRuleType;
23use style_traits::ParsingMode;
24use url::Url;
25
26use crate::css::parser_context_for_anonymous_content;
27use crate::dom::bindings::buffer_source::create_buffer_source;
28use crate::dom::bindings::cell::{DomRefCell, Ref};
29use crate::dom::bindings::codegen::Bindings::DOMMatrixBinding::{
30    DOMMatrix2DInit, DOMMatrixInit, DOMMatrixMethods,
31};
32use crate::dom::bindings::codegen::Bindings::DOMMatrixReadOnlyBinding::DOMMatrixReadOnlyMethods;
33use crate::dom::bindings::codegen::Bindings::DOMPointBinding::DOMPointInit;
34use crate::dom::bindings::codegen::UnionTypes::StringOrUnrestrictedDoubleSequence;
35use crate::dom::bindings::error;
36use crate::dom::bindings::error::Fallible;
37use crate::dom::bindings::inheritance::Castable;
38use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object_with_proto};
39use crate::dom::bindings::root::DomRoot;
40use crate::dom::bindings::serializable::Serializable;
41use crate::dom::bindings::str::DOMString;
42use crate::dom::bindings::structuredclone::StructuredData;
43use crate::dom::dommatrix::DOMMatrix;
44use crate::dom::dompoint::DOMPoint;
45use crate::dom::globalscope::GlobalScope;
46use crate::dom::window::Window;
47use crate::script_runtime::{CanGc, JSContext};
48
49#[dom_struct]
50#[expect(non_snake_case)]
51pub(crate) struct DOMMatrixReadOnly {
52    reflector_: Reflector,
53    #[no_trace]
54    matrix: DomRefCell<Transform3D<f64>>,
55    is2D: Cell<bool>,
56}
57
58#[expect(non_snake_case)]
59impl DOMMatrixReadOnly {
60    pub(crate) fn new(
61        global: &GlobalScope,
62        is2D: bool,
63        matrix: Transform3D<f64>,
64        can_gc: CanGc,
65    ) -> DomRoot<Self> {
66        Self::new_with_proto(global, None, is2D, matrix, can_gc)
67    }
68
69    #[cfg_attr(crown, expect(crown::unrooted_must_root))]
70    fn new_with_proto(
71        global: &GlobalScope,
72        proto: Option<HandleObject>,
73        is2D: bool,
74        matrix: Transform3D<f64>,
75        can_gc: CanGc,
76    ) -> DomRoot<Self> {
77        let dommatrix = Self::new_inherited(is2D, matrix);
78        reflect_dom_object_with_proto(Box::new(dommatrix), global, proto, can_gc)
79    }
80
81    pub(crate) fn new_inherited(is2D: bool, matrix: Transform3D<f64>) -> Self {
82        DOMMatrixReadOnly {
83            reflector_: Reflector::new(),
84            matrix: DomRefCell::new(matrix),
85            is2D: Cell::new(is2D),
86        }
87    }
88
89    pub(crate) fn matrix(&self) -> Ref<'_, Transform3D<f64>> {
90        self.matrix.borrow()
91    }
92
93    pub(crate) fn set_matrix(&self, value: Transform3D<f64>) {
94        self.set_m11(value.m11);
95        self.set_m12(value.m12);
96        self.set_m13(value.m13);
97        self.set_m14(value.m14);
98        self.set_m21(value.m21);
99        self.set_m22(value.m22);
100        self.set_m23(value.m23);
101        self.set_m24(value.m24);
102        self.set_m31(value.m31);
103        self.set_m32(value.m32);
104        self.set_m33(value.m33);
105        self.set_m34(value.m34);
106        self.set_m41(value.m41);
107        self.set_m42(value.m42);
108        self.set_m43(value.m43);
109        self.set_m44(value.m44);
110    }
111
112    pub(crate) fn is2D(&self) -> bool {
113        self.is2D.get()
114    }
115
116    pub(crate) fn set_is2D(&self, value: bool) {
117        self.is2D.set(value);
118    }
119
120    // https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m11
121    pub(crate) fn set_m11(&self, value: f64) {
122        self.matrix.borrow_mut().m11 = value;
123    }
124
125    // https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m12
126    pub(crate) fn set_m12(&self, value: f64) {
127        self.matrix.borrow_mut().m12 = value;
128    }
129
130    // https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m13
131    pub(crate) fn set_m13(&self, value: f64) {
132        // For the DOMMatrix interface, setting the m13 attribute must set the
133        // m13 element to the new value and, if the new value is not 0 or -0, set is 2D to false.
134
135        self.matrix.borrow_mut().m13 = value;
136        if value.abs() != 0. {
137            self.is2D.set(false);
138        }
139    }
140
141    // https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m14
142    pub(crate) fn set_m14(&self, value: f64) {
143        // For the DOMMatrix interface, setting the m14 attribute must set the
144        // m14 element to the new value and, if the new value is not 0 or -0, set is 2D to false.
145        self.matrix.borrow_mut().m14 = value;
146
147        if value.abs() != 0. {
148            self.is2D.set(false);
149        }
150    }
151
152    // https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m21
153    pub(crate) fn set_m21(&self, value: f64) {
154        self.matrix.borrow_mut().m21 = value;
155    }
156
157    // https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m22
158    pub(crate) fn set_m22(&self, value: f64) {
159        self.matrix.borrow_mut().m22 = value;
160    }
161
162    // https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m23
163    pub(crate) fn set_m23(&self, value: f64) {
164        // For the DOMMatrix interface, setting the m23 attribute must set the
165        // m23 element to the new value and, if the new value is not 0 or -0, set is 2D to false.
166        self.matrix.borrow_mut().m23 = value;
167
168        if value.abs() != 0. {
169            self.is2D.set(false);
170        }
171    }
172
173    // https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m24
174    pub(crate) fn set_m24(&self, value: f64) {
175        // For the DOMMatrix interface, setting the m24 attribute must set the
176        // m24 element to the new value and, if the new value is not 0 or -0, set is 2D to false.
177        self.matrix.borrow_mut().m24 = value;
178
179        if value.abs() != 0. {
180            self.is2D.set(false);
181        }
182    }
183
184    // https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m31
185    pub(crate) fn set_m31(&self, value: f64) {
186        // For the DOMMatrix interface, setting the m31 attribute must set the
187        // m31 element to the new value and, if the new value is not 0 or -0, set is 2D to false.
188        self.matrix.borrow_mut().m31 = value;
189
190        if value.abs() != 0. {
191            self.is2D.set(false);
192        }
193    }
194
195    // https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m32
196    pub(crate) fn set_m32(&self, value: f64) {
197        // For the DOMMatrix interface, setting the m32 attribute must set the
198        // m32 element to the new value and, if the new value is not 0 or -0, set is 2D to false.
199        self.matrix.borrow_mut().m32 = value;
200
201        if value.abs() != 0. {
202            self.is2D.set(false);
203        }
204    }
205
206    // https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m33
207    pub(crate) fn set_m33(&self, value: f64) {
208        // For the DOMMatrix interface, setting the m33 attribute must set the
209        // m33 element to the new value and, if the new value is not 1, set is 2D to false.
210        self.matrix.borrow_mut().m33 = value;
211
212        if value != 1. {
213            self.is2D.set(false);
214        }
215    }
216
217    // https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m34
218    pub(crate) fn set_m34(&self, value: f64) {
219        // For the DOMMatrix interface, setting the m34 attribute must set the
220        // m34 element to the new value and, if the new value is not 0 or -0, set is 2D to false.
221        self.matrix.borrow_mut().m34 = value;
222
223        if value.abs() != 0. {
224            self.is2D.set(false);
225        }
226    }
227
228    // https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m41
229    pub(crate) fn set_m41(&self, value: f64) {
230        self.matrix.borrow_mut().m41 = value;
231    }
232
233    // https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m42
234    pub(crate) fn set_m42(&self, value: f64) {
235        self.matrix.borrow_mut().m42 = value;
236    }
237
238    // https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m43
239    pub(crate) fn set_m43(&self, value: f64) {
240        // For the DOMMatrix interface, setting the m43 attribute must set the
241        // m43 element to the new value and, if the new value is not 0 or -0, set is 2D to false.
242        self.matrix.borrow_mut().m43 = value;
243
244        if value.abs() != 0. {
245            self.is2D.set(false);
246        }
247    }
248
249    // https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m44
250    pub(crate) fn set_m44(&self, value: f64) {
251        // For the DOMMatrix interface, setting the m44 attribute must set the
252        // m44 element to the new value and, if the new value is not 1, set is 2D to false.
253        self.matrix.borrow_mut().m44 = value;
254
255        if value != 1. {
256            self.is2D.set(false);
257        }
258    }
259
260    // https://drafts.fxtf.org/geometry-1/#dom-dommatrix-multiplyself
261    pub(crate) fn multiply_self(&self, other: &DOMMatrixInit) -> Fallible<()> {
262        // Step 1.
263        dommatrixinit_to_matrix(other).map(|(is2D, other_matrix)| {
264            // Step 2.
265            let mut matrix = self.matrix.borrow_mut();
266            *matrix = other_matrix.then(&matrix);
267            // Step 3.
268            if !is2D {
269                self.is2D.set(false);
270            }
271            // Step 4 in DOMMatrix.MultiplySelf
272        })
273    }
274
275    // https://drafts.fxtf.org/geometry-1/#dom-dommatrix-premultiplyself
276    pub(crate) fn pre_multiply_self(&self, other: &DOMMatrixInit) -> Fallible<()> {
277        // Step 1.
278        dommatrixinit_to_matrix(other).map(|(is2D, other_matrix)| {
279            // Step 2.
280            let mut matrix = self.matrix.borrow_mut();
281            *matrix = matrix.then(&other_matrix);
282            // Step 3.
283            if !is2D {
284                self.is2D.set(false);
285            }
286            // Step 4 in DOMMatrix.PreMultiplySelf
287        })
288    }
289
290    // https://drafts.fxtf.org/geometry-1/#dom-dommatrix-translateself
291    pub(crate) fn translate_self(&self, tx: f64, ty: f64, tz: f64) {
292        // Step 1.
293        let translation = Transform3D::translation(tx, ty, tz);
294        let mut matrix = self.matrix.borrow_mut();
295        *matrix = translation.then(&matrix);
296        // Step 2.
297        if tz != 0.0 {
298            self.is2D.set(false);
299        }
300        // Step 3 in DOMMatrix.TranslateSelf
301    }
302
303    // https://drafts.fxtf.org/geometry-1/#dom-dommatrix-scaleself
304    pub(crate) fn scale_self(
305        &self,
306        scaleX: f64,
307        scaleY: Option<f64>,
308        scaleZ: f64,
309        mut originX: f64,
310        mut originY: f64,
311        mut originZ: f64,
312    ) {
313        // Step 1.
314        self.translate_self(originX, originY, originZ);
315        // Step 2.
316        let scaleY = scaleY.unwrap_or(scaleX);
317        // Step 3.
318        {
319            let scale3D = Transform3D::scale(scaleX, scaleY, scaleZ);
320            let mut matrix = self.matrix.borrow_mut();
321            *matrix = scale3D.then(&matrix);
322        }
323        // Step 4.
324        originX = -originX;
325        originY = -originY;
326        originZ = -originZ;
327        // Step 5.
328        self.translate_self(originX, originY, originZ);
329        // Step 6.
330        if scaleZ != 1.0 || originZ != 0.0 {
331            self.is2D.set(false);
332        }
333        // Step 7 in DOMMatrix.ScaleSelf
334    }
335
336    // https://drafts.fxtf.org/geometry-1/#dom-dommatrix-scale3dself
337    pub(crate) fn scale_3d_self(&self, scale: f64, originX: f64, originY: f64, originZ: f64) {
338        // Step 1.
339        self.translate_self(originX, originY, originZ);
340        // Step 2.
341        {
342            let scale3D = Transform3D::scale(scale, scale, scale);
343            let mut matrix = self.matrix.borrow_mut();
344            *matrix = scale3D.then(&matrix);
345        }
346        // Step 3.
347        self.translate_self(-originX, -originY, -originZ);
348        // Step 4.
349        if scale != 1.0 {
350            self.is2D.set(false);
351        }
352        // Step 5 in DOMMatrix.Scale3dSelf
353    }
354
355    // https://drafts.fxtf.org/geometry-1/#dom-dommatrix-rotateself
356    pub(crate) fn rotate_self(&self, mut rotX: f64, mut rotY: Option<f64>, mut rotZ: Option<f64>) {
357        // Step 1.
358        if rotY.is_none() && rotZ.is_none() {
359            rotZ = Some(rotX);
360            rotX = 0.0;
361            rotY = Some(0.0);
362        }
363        // Step 2.
364        let rotY = rotY.unwrap_or(0.0);
365        // Step 3.
366        let rotZ = rotZ.unwrap_or(0.0);
367        // Step 4.
368        if rotX != 0.0 || rotY != 0.0 {
369            self.is2D.set(false);
370        }
371        if rotZ != 0.0 {
372            // Step 5.
373            let rotation = Transform3D::rotation(0.0, 0.0, 1.0, Angle::radians(rotZ.to_radians()));
374            let mut matrix = self.matrix.borrow_mut();
375            *matrix = rotation.then(&matrix);
376        }
377        if rotY != 0.0 {
378            // Step 6.
379            let rotation = Transform3D::rotation(0.0, 1.0, 0.0, Angle::radians(rotY.to_radians()));
380            let mut matrix = self.matrix.borrow_mut();
381            *matrix = rotation.then(&matrix);
382        }
383        if rotX != 0.0 {
384            // Step 7.
385            let rotation = Transform3D::rotation(1.0, 0.0, 0.0, Angle::radians(rotX.to_radians()));
386            let mut matrix = self.matrix.borrow_mut();
387            *matrix = rotation.then(&matrix);
388        }
389        // Step 8 in DOMMatrix.RotateSelf
390    }
391
392    // https://drafts.fxtf.org/geometry-1/#dom-dommatrix-rotatefromvectorself
393    pub(crate) fn rotate_from_vector_self(&self, x: f64, y: f64) {
394        // don't do anything when the rotation angle is zero or undefined
395        if y != 0.0 || x < 0.0 {
396            // Step 1.
397            let rotZ = Angle::radians(f64::atan2(y, x));
398            let rotation = Transform3D::rotation(0.0, 0.0, 1.0, rotZ);
399            let mut matrix = self.matrix.borrow_mut();
400            *matrix = rotation.then(&matrix);
401        }
402        // Step 2 in DOMMatrix.RotateFromVectorSelf
403    }
404
405    // https://drafts.fxtf.org/geometry-1/#dom-dommatrix-rotateaxisangleself
406    pub(crate) fn rotate_axis_angle_self(&self, x: f64, y: f64, z: f64, angle: f64) {
407        // Step 1.
408        let (norm_x, norm_y, norm_z) = normalize_point(x, y, z);
409        // Beware: pass negated value until https://github.com/servo/euclid/issues/354
410        let rotation =
411            Transform3D::rotation(norm_x, norm_y, norm_z, Angle::radians(angle.to_radians()));
412        let mut matrix = self.matrix.borrow_mut();
413        *matrix = rotation.then(&matrix);
414        // Step 2.
415        if x != 0.0 || y != 0.0 {
416            self.is2D.set(false);
417        }
418        // Step 3 in DOMMatrix.RotateAxisAngleSelf
419    }
420
421    // https://drafts.fxtf.org/geometry-1/#dom-dommatrix-skewxself
422    pub(crate) fn skew_x_self(&self, sx: f64) {
423        // Step 1.
424        let skew = Transform3D::skew(Angle::radians(sx.to_radians()), Angle::radians(0.0));
425        let mut matrix = self.matrix.borrow_mut();
426        *matrix = skew.then(&matrix);
427        // Step 2 in DOMMatrix.SkewXSelf
428    }
429
430    // https://drafts.fxtf.org/geometry-1/#dom-dommatrix-skewyself
431    pub(crate) fn skew_y_self(&self, sy: f64) {
432        // Step 1.
433        let skew = Transform3D::skew(Angle::radians(0.0), Angle::radians(sy.to_radians()));
434        let mut matrix = self.matrix.borrow_mut();
435        *matrix = skew.then(&matrix);
436        // Step 2 in DOMMatrix.SkewYSelf
437    }
438
439    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrix-invertself>
440    pub(crate) fn invert_self(&self) {
441        let mut matrix = self.matrix.borrow_mut();
442        // Step 1. Invert the current matrix.
443        let inverted = match self.is2D() {
444            true => matrix.to_2d().inverse().map(|m| m.to_3d()),
445            false => matrix.inverse(),
446        };
447
448        // Step 2. If the current matrix is not invertible set all attributes to NaN
449        // and set is 2D to false.
450        *matrix = inverted.unwrap_or_else(|| -> Transform3D<f64> {
451            self.is2D.set(false);
452            Transform3D::new(
453                f64::NAN,
454                f64::NAN,
455                f64::NAN,
456                f64::NAN,
457                f64::NAN,
458                f64::NAN,
459                f64::NAN,
460                f64::NAN,
461                f64::NAN,
462                f64::NAN,
463                f64::NAN,
464                f64::NAN,
465                f64::NAN,
466                f64::NAN,
467                f64::NAN,
468                f64::NAN,
469            )
470        });
471        // Step 3 in DOMMatrix.InvertSelf
472    }
473}
474
475#[expect(non_snake_case)]
476impl DOMMatrixReadOnlyMethods<crate::DomTypeHolder> for DOMMatrixReadOnly {
477    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-dommatrixreadonly>
478    fn Constructor(
479        global: &GlobalScope,
480        proto: Option<HandleObject>,
481        can_gc: CanGc,
482        init: Option<StringOrUnrestrictedDoubleSequence>,
483    ) -> Fallible<DomRoot<Self>> {
484        if init.is_none() {
485            return Ok(Self::new_with_proto(
486                global,
487                proto,
488                true,
489                Transform3D::identity(),
490                can_gc,
491            ));
492        }
493        match init.unwrap() {
494            StringOrUnrestrictedDoubleSequence::String(ref s) => {
495                if !global.is::<Window>() {
496                    return Err(error::Error::Type(
497                        c"String constructor is only supported in the main thread.".to_owned(),
498                    ));
499                }
500                if s.is_empty() {
501                    return Ok(Self::new(global, true, Transform3D::identity(), can_gc));
502                }
503                transform_to_matrix(s.to_string())
504                    .map(|(is2D, matrix)| Self::new_with_proto(global, proto, is2D, matrix, can_gc))
505            },
506            StringOrUnrestrictedDoubleSequence::UnrestrictedDoubleSequence(ref entries) => {
507                entries_to_matrix(&entries[..])
508                    .map(|(is2D, matrix)| Self::new_with_proto(global, proto, is2D, matrix, can_gc))
509            },
510        }
511    }
512
513    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-frommatrix>
514    fn FromMatrix(
515        global: &GlobalScope,
516        other: &DOMMatrixInit,
517        can_gc: CanGc,
518    ) -> Fallible<DomRoot<Self>> {
519        dommatrixinit_to_matrix(other).map(|(is2D, matrix)| Self::new(global, is2D, matrix, can_gc))
520    }
521
522    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-fromfloat32array>
523    fn FromFloat32Array(
524        global: &GlobalScope,
525        array: CustomAutoRooterGuard<Float32Array>,
526        can_gc: CanGc,
527    ) -> Fallible<DomRoot<DOMMatrixReadOnly>> {
528        let vec: Vec<f64> = array.to_vec().iter().map(|&x| x as f64).collect();
529        DOMMatrixReadOnly::Constructor(
530            global,
531            None,
532            can_gc,
533            Some(StringOrUnrestrictedDoubleSequence::UnrestrictedDoubleSequence(vec)),
534        )
535    }
536
537    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-fromfloat64array>
538    fn FromFloat64Array(
539        global: &GlobalScope,
540        array: CustomAutoRooterGuard<Float64Array>,
541        can_gc: CanGc,
542    ) -> Fallible<DomRoot<DOMMatrixReadOnly>> {
543        let vec: Vec<f64> = array.to_vec();
544        DOMMatrixReadOnly::Constructor(
545            global,
546            None,
547            can_gc,
548            Some(StringOrUnrestrictedDoubleSequence::UnrestrictedDoubleSequence(vec)),
549        )
550    }
551
552    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m11>
553    fn M11(&self) -> f64 {
554        self.matrix.borrow().m11
555    }
556
557    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m12>
558    fn M12(&self) -> f64 {
559        self.matrix.borrow().m12
560    }
561
562    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m13>
563    fn M13(&self) -> f64 {
564        self.matrix.borrow().m13
565    }
566
567    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m14>
568    fn M14(&self) -> f64 {
569        self.matrix.borrow().m14
570    }
571
572    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m21>
573    fn M21(&self) -> f64 {
574        self.matrix.borrow().m21
575    }
576
577    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m22>
578    fn M22(&self) -> f64 {
579        self.matrix.borrow().m22
580    }
581
582    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m23>
583    fn M23(&self) -> f64 {
584        self.matrix.borrow().m23
585    }
586
587    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m24>
588    fn M24(&self) -> f64 {
589        self.matrix.borrow().m24
590    }
591
592    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m31>
593    fn M31(&self) -> f64 {
594        self.matrix.borrow().m31
595    }
596
597    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m32>
598    fn M32(&self) -> f64 {
599        self.matrix.borrow().m32
600    }
601
602    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m33>
603    fn M33(&self) -> f64 {
604        self.matrix.borrow().m33
605    }
606
607    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m34>
608    fn M34(&self) -> f64 {
609        self.matrix.borrow().m34
610    }
611
612    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m41>
613    fn M41(&self) -> f64 {
614        self.matrix.borrow().m41
615    }
616
617    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m42>
618    fn M42(&self) -> f64 {
619        self.matrix.borrow().m42
620    }
621
622    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m43>
623    fn M43(&self) -> f64 {
624        self.matrix.borrow().m43
625    }
626
627    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-m44>
628    fn M44(&self) -> f64 {
629        self.matrix.borrow().m44
630    }
631
632    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-a>
633    fn A(&self) -> f64 {
634        self.M11()
635    }
636
637    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-b>
638    fn B(&self) -> f64 {
639        self.M12()
640    }
641
642    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-c>
643    fn C(&self) -> f64 {
644        self.M21()
645    }
646
647    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-d>
648    fn D(&self) -> f64 {
649        self.M22()
650    }
651
652    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-e>
653    fn E(&self) -> f64 {
654        self.M41()
655    }
656
657    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-f>
658    fn F(&self) -> f64 {
659        self.M42()
660    }
661
662    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-is2d>
663    fn Is2D(&self) -> bool {
664        self.is2D.get()
665    }
666
667    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-isidentity>
668    fn IsIdentity(&self) -> bool {
669        let matrix = self.matrix.borrow();
670        matrix.m12 == 0.0 &&
671            matrix.m13 == 0.0 &&
672            matrix.m14 == 0.0 &&
673            matrix.m21 == 0.0 &&
674            matrix.m23 == 0.0 &&
675            matrix.m24 == 0.0 &&
676            matrix.m31 == 0.0 &&
677            matrix.m32 == 0.0 &&
678            matrix.m34 == 0.0 &&
679            matrix.m41 == 0.0 &&
680            matrix.m42 == 0.0 &&
681            matrix.m43 == 0.0 &&
682            matrix.m11 == 1.0 &&
683            matrix.m22 == 1.0 &&
684            matrix.m33 == 1.0 &&
685            matrix.m44 == 1.0
686    }
687
688    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-translate>
689    fn Translate(&self, tx: f64, ty: f64, tz: f64, can_gc: CanGc) -> DomRoot<DOMMatrix> {
690        DOMMatrix::from_readonly(&self.global(), self, can_gc).TranslateSelf(tx, ty, tz)
691    }
692
693    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-scale>
694    fn Scale(
695        &self,
696        scaleX: f64,
697        scaleY: Option<f64>,
698        scaleZ: f64,
699        originX: f64,
700        originY: f64,
701        originZ: f64,
702        can_gc: CanGc,
703    ) -> DomRoot<DOMMatrix> {
704        DOMMatrix::from_readonly(&self.global(), self, can_gc)
705            .ScaleSelf(scaleX, scaleY, scaleZ, originX, originY, originZ)
706    }
707
708    /// <https://drafts.fxtf.org/geometry/#dom-dommatrixreadonly-scalenonuniform>
709    fn ScaleNonUniform(&self, scaleX: f64, scaleY: f64, can_gc: CanGc) -> DomRoot<DOMMatrix> {
710        DOMMatrix::from_readonly(&self.global(), self, can_gc).ScaleSelf(
711            scaleX,
712            Some(scaleY),
713            1.0,
714            0.0,
715            0.0,
716            0.0,
717        )
718    }
719
720    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-scale3d>
721    fn Scale3d(
722        &self,
723        scale: f64,
724        originX: f64,
725        originY: f64,
726        originZ: f64,
727        can_gc: CanGc,
728    ) -> DomRoot<DOMMatrix> {
729        DOMMatrix::from_readonly(&self.global(), self, can_gc)
730            .Scale3dSelf(scale, originX, originY, originZ)
731    }
732
733    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-rotate>
734    fn Rotate(
735        &self,
736        rotX: f64,
737        rotY: Option<f64>,
738        rotZ: Option<f64>,
739        can_gc: CanGc,
740    ) -> DomRoot<DOMMatrix> {
741        DOMMatrix::from_readonly(&self.global(), self, can_gc).RotateSelf(rotX, rotY, rotZ)
742    }
743
744    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-rotatefromvector>
745    fn RotateFromVector(&self, x: f64, y: f64, can_gc: CanGc) -> DomRoot<DOMMatrix> {
746        DOMMatrix::from_readonly(&self.global(), self, can_gc).RotateFromVectorSelf(x, y)
747    }
748
749    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-rotateaxisangle>
750    fn RotateAxisAngle(
751        &self,
752        x: f64,
753        y: f64,
754        z: f64,
755        angle: f64,
756        can_gc: CanGc,
757    ) -> DomRoot<DOMMatrix> {
758        DOMMatrix::from_readonly(&self.global(), self, can_gc).RotateAxisAngleSelf(x, y, z, angle)
759    }
760
761    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-skewx>
762    fn SkewX(&self, sx: f64, can_gc: CanGc) -> DomRoot<DOMMatrix> {
763        DOMMatrix::from_readonly(&self.global(), self, can_gc).SkewXSelf(sx)
764    }
765
766    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-skewy>
767    fn SkewY(&self, sy: f64, can_gc: CanGc) -> DomRoot<DOMMatrix> {
768        DOMMatrix::from_readonly(&self.global(), self, can_gc).SkewYSelf(sy)
769    }
770
771    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-multiply>
772    fn Multiply(&self, other: &DOMMatrixInit, can_gc: CanGc) -> Fallible<DomRoot<DOMMatrix>> {
773        DOMMatrix::from_readonly(&self.global(), self, can_gc).MultiplySelf(other)
774    }
775
776    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-flipx>
777    fn FlipX(&self, can_gc: CanGc) -> DomRoot<DOMMatrix> {
778        let is2D = self.is2D.get();
779        let flip = Transform3D::new(
780            -1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0,
781        );
782        let matrix = flip.then(&self.matrix.borrow());
783        DOMMatrix::new(&self.global(), is2D, matrix, can_gc)
784    }
785
786    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-flipy>
787    fn FlipY(&self, can_gc: CanGc) -> DomRoot<DOMMatrix> {
788        let is2D = self.is2D.get();
789        let flip = Transform3D::new(
790            1.0, 0.0, 0.0, 0.0, 0.0, -1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0,
791        );
792        let matrix = flip.then(&self.matrix.borrow());
793        DOMMatrix::new(&self.global(), is2D, matrix, can_gc)
794    }
795
796    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-inverse>
797    fn Inverse(&self, can_gc: CanGc) -> DomRoot<DOMMatrix> {
798        DOMMatrix::from_readonly(&self.global(), self, can_gc).InvertSelf()
799    }
800
801    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-transformpoint>
802    fn TransformPoint(&self, point: &DOMPointInit, can_gc: CanGc) -> DomRoot<DOMPoint> {
803        // Euclid always normalizes the homogeneous coordinate which is usually the right
804        // thing but may (?) not be compliant with the CSS matrix spec (or at least is
805        // probably not the behavior web authors will expect even if it is mathematically
806        // correct in the context of geometry computations).
807        // Since this is the only place where this is needed, better implement it here
808        // than in euclid (which does not have a notion of 4d points).
809        let mat = self.matrix.borrow();
810        let x = point.x * mat.m11 + point.y * mat.m21 + point.z * mat.m31 + point.w * mat.m41;
811        let y = point.x * mat.m12 + point.y * mat.m22 + point.z * mat.m32 + point.w * mat.m42;
812        let z = point.x * mat.m13 + point.y * mat.m23 + point.z * mat.m33 + point.w * mat.m43;
813        let w = point.x * mat.m14 + point.y * mat.m24 + point.z * mat.m34 + point.w * mat.m44;
814
815        DOMPoint::new(&self.global(), x, y, z, w, can_gc)
816    }
817
818    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-tofloat32array>
819    fn ToFloat32Array(&self, cx: JSContext, can_gc: CanGc) -> RootedTraceableBox<HeapFloat32Array> {
820        let vec: Vec<f32> = self
821            .matrix
822            .borrow()
823            .to_array()
824            .iter()
825            .map(|&x| x as f32)
826            .collect();
827        rooted!(in (*cx) let mut array = ptr::null_mut::<JSObject>());
828        create_buffer_source(cx, &vec, array.handle_mut(), can_gc)
829            .expect("Converting matrix to float32 array should never fail")
830    }
831
832    /// <https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-tofloat64array>
833    fn ToFloat64Array(&self, cx: JSContext, can_gc: CanGc) -> RootedTraceableBox<HeapFloat64Array> {
834        rooted!(in (*cx) let mut array = ptr::null_mut::<JSObject>());
835        create_buffer_source(
836            cx,
837            &self.matrix.borrow().to_array(),
838            array.handle_mut(),
839            can_gc,
840        )
841        .expect("Converting matrix to float64 array should never fail")
842    }
843
844    // https://drafts.fxtf.org/geometry/#dommatrixreadonly-stringification-behavior
845    #[expect(unsafe_code)]
846    fn Stringifier(&self) -> Fallible<DOMString> {
847        // Step 1. If one or more of m11 element through m44 element are a non-finite value,
848        // then throw an "InvalidStateError" DOMException.
849        let mat = self.matrix.borrow();
850        if !mat.m11.is_finite() ||
851            !mat.m12.is_finite() ||
852            !mat.m13.is_finite() ||
853            !mat.m14.is_finite() ||
854            !mat.m21.is_finite() ||
855            !mat.m22.is_finite() ||
856            !mat.m23.is_finite() ||
857            !mat.m24.is_finite() ||
858            !mat.m31.is_finite() ||
859            !mat.m32.is_finite() ||
860            !mat.m33.is_finite() ||
861            !mat.m34.is_finite() ||
862            !mat.m41.is_finite() ||
863            !mat.m42.is_finite() ||
864            !mat.m43.is_finite() ||
865            !mat.m44.is_finite()
866        {
867            return Err(error::Error::InvalidState(None));
868        }
869
870        let cx = GlobalScope::get_cx();
871        let to_string = |f: f64| {
872            let value = jsval::DoubleValue(f);
873
874            unsafe {
875                rooted!(in(*cx) let mut rooted_value = value);
876                let serialization = std::ptr::NonNull::new(ToString(*cx, rooted_value.handle()))
877                    .expect("Pointer cannot be null");
878                jsstr_to_string(*cx, serialization)
879            }
880        };
881
882        // Step 2. Let string be the empty string.
883        // Step 3. If is 2D is true, then:
884        let string = if self.is2D() {
885            // Step 3.1 Append "matrix(" to string.
886            // Step 3.2 Append ! ToString(m11 element) to string.
887            // Step 3.3 Append ", " to string.
888            // Step 3.4 Append ! ToString(m12 element) to string.
889            // Step 3.5 Append ", " to string.
890            // Step 3.6 Append ! ToString(m21 element) to string.
891            // Step 3.7 Append ", " to string.
892            // Step 3.8 Append ! ToString(m22 element) to string.
893            // Step 3.9 Append ", " to string.
894            // Step 3.10 Append ! ToString(m41 element) to string.
895            // Step 3.11 Append ", " to string.
896            // Step 3.12 Append ! ToString(m42 element) to string.
897            // Step 3.13 Append ")" to string.
898            format!(
899                "matrix({}, {}, {}, {}, {}, {})",
900                to_string(mat.m11),
901                to_string(mat.m12),
902                to_string(mat.m21),
903                to_string(mat.m22),
904                to_string(mat.m41),
905                to_string(mat.m42)
906            )
907            .into()
908        }
909        // Step 4. Otherwise:
910        else {
911            // Step 4.1 Append "matrix3d(" to string.
912            // Step 4.2 Append ! ToString(m11 element) to string.
913            // Step 4.3 Append ", " to string.
914            // Step 4.4 Append ! ToString(m12 element) to string.
915            // Step 4.5 Append ", " to string.
916            // Step 4.6 Append ! ToString(m13 element) to string.
917            // Step 4.7 Append ", " to string.
918            // Step 4.8 Append ! ToString(m14 element) to string.
919            // Step 4.9 Append ", " to string.
920            // Step 4.10 Append ! ToString(m21 element) to string.
921            // Step 4.11 Append ", " to string.
922            // Step 4.12 Append ! ToString(m22 element) to string.
923            // Step 4.13 Append ", " to string.
924            // Step 4.14 Append ! ToString(m23 element) to string.
925            // Step 4.15 Append ", " to string.
926            // Step 4.16 Append ! ToString(m24 element) to string.
927            // Step 4.17 Append ", " to string.
928            // Step 4.18 Append ! ToString(m41 element) to string.
929            // Step 4.19 Append ", " to string.
930            // Step 4.20 Append ! ToString(m42 element) to string.
931            // Step 4.21 Append ", " to string.
932            // Step 4.22 Append ! ToString(m43 element) to string.
933            // Step 4.23 Append ", " to string.
934            // Step 4.24 Append ! ToString(m44 element) to string.
935            // Step 4.25 Append ")" to string.
936
937            // NOTE: The spec is wrong and missing the m3* elements.
938            // (https://github.com/w3c/fxtf-drafts/issues/574)
939            format!(
940                "matrix3d({}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {})",
941                to_string(mat.m11),
942                to_string(mat.m12),
943                to_string(mat.m13),
944                to_string(mat.m14),
945                to_string(mat.m21),
946                to_string(mat.m22),
947                to_string(mat.m23),
948                to_string(mat.m24),
949                to_string(mat.m31),
950                to_string(mat.m32),
951                to_string(mat.m33),
952                to_string(mat.m34),
953                to_string(mat.m41),
954                to_string(mat.m42),
955                to_string(mat.m43),
956                to_string(mat.m44)
957            )
958            .into()
959        };
960
961        Ok(string)
962    }
963}
964
965impl Serializable for DOMMatrixReadOnly {
966    type Index = DomMatrixIndex;
967    type Data = DomMatrix;
968
969    fn serialize(&self) -> Result<(DomMatrixId, Self::Data), ()> {
970        let serialized = if self.is2D() {
971            DomMatrix {
972                matrix: Transform3D::new(
973                    self.M11(),
974                    self.M12(),
975                    f64::NAN,
976                    f64::NAN,
977                    self.M21(),
978                    self.M22(),
979                    f64::NAN,
980                    f64::NAN,
981                    f64::NAN,
982                    f64::NAN,
983                    f64::NAN,
984                    f64::NAN,
985                    self.M41(),
986                    self.M42(),
987                    f64::NAN,
988                    f64::NAN,
989                ),
990                is_2d: true,
991            }
992        } else {
993            DomMatrix {
994                matrix: *self.matrix(),
995                is_2d: false,
996            }
997        };
998        Ok((DomMatrixId::new(), serialized))
999    }
1000
1001    fn deserialize(
1002        owner: &GlobalScope,
1003        serialized: Self::Data,
1004        can_gc: CanGc,
1005    ) -> Result<DomRoot<Self>, ()>
1006    where
1007        Self: Sized,
1008    {
1009        if serialized.is_2d {
1010            Ok(Self::new(
1011                owner,
1012                true,
1013                Transform3D::new(
1014                    serialized.matrix.m11,
1015                    serialized.matrix.m12,
1016                    0.0,
1017                    0.0,
1018                    serialized.matrix.m21,
1019                    serialized.matrix.m22,
1020                    0.0,
1021                    0.0,
1022                    0.0,
1023                    0.0,
1024                    1.0,
1025                    0.0,
1026                    serialized.matrix.m41,
1027                    serialized.matrix.m42,
1028                    0.0,
1029                    1.0,
1030                ),
1031                can_gc,
1032            ))
1033        } else {
1034            Ok(Self::new(owner, false, serialized.matrix, can_gc))
1035        }
1036    }
1037
1038    fn serialized_storage<'a>(
1039        data: StructuredData<'a, '_>,
1040    ) -> &'a mut Option<FxHashMap<DomMatrixId, Self::Data>> {
1041        match data {
1042            StructuredData::Reader(reader) => &mut reader.matrices,
1043            StructuredData::Writer(writer) => &mut writer.matrices,
1044        }
1045    }
1046}
1047
1048// https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-dommatrixreadonly-numbersequence
1049pub(crate) fn entries_to_matrix(entries: &[f64]) -> Fallible<(bool, Transform3D<f64>)> {
1050    if let Ok(array) = entries.try_into() {
1051        Ok((true, Transform2D::from_array(array).to_3d()))
1052    } else if let Ok(array) = entries.try_into() {
1053        Ok((false, Transform3D::from_array(array)))
1054    } else {
1055        let err_msg = cformat!("Expected 6 or 16 entries, but found {}.", entries.len());
1056        Err(error::Error::Type(err_msg))
1057    }
1058}
1059
1060/// <https://drafts.fxtf.org/geometry-1/#matrix-validate-and-fixup-2d>
1061fn validate_and_fixup_2d(dict: &DOMMatrix2DInit) -> Fallible<Transform2D<f64>> {
1062    // <https://tc39.es/ecma262/#sec-numeric-types-number-sameValueZero>
1063    let same_value_zero = |x: f64, y: f64| -> bool { x.is_nan() && y.is_nan() || x == y };
1064
1065    // Step 1. If if at least one of the following conditions are true for dict,
1066    // then throw a TypeError exception and abort these steps.
1067    if dict.a.is_some() &&
1068        dict.m11.is_some() &&
1069        !same_value_zero(dict.a.unwrap(), dict.m11.unwrap()) ||
1070        dict.b.is_some() &&
1071            dict.m12.is_some() &&
1072            !same_value_zero(dict.b.unwrap(), dict.m12.unwrap()) ||
1073        dict.c.is_some() &&
1074            dict.m21.is_some() &&
1075            !same_value_zero(dict.c.unwrap(), dict.m21.unwrap()) ||
1076        dict.d.is_some() &&
1077            dict.m22.is_some() &&
1078            !same_value_zero(dict.d.unwrap(), dict.m22.unwrap()) ||
1079        dict.e.is_some() &&
1080            dict.m41.is_some() &&
1081            !same_value_zero(dict.e.unwrap(), dict.m41.unwrap()) ||
1082        dict.f.is_some() &&
1083            dict.m42.is_some() &&
1084            !same_value_zero(dict.f.unwrap(), dict.m42.unwrap())
1085    {
1086        return Err(error::Error::Type(
1087            c"Property mismatch on matrix initialization.".to_owned(),
1088        ));
1089    }
1090
1091    // Step 2. If m11 is not present then set it to the value of member a,
1092    // or value 1 if a is also not present.
1093    let m11 = dict.m11.unwrap_or(dict.a.unwrap_or(1.0));
1094
1095    // Step 3. If m12 is not present then set it to the value of member b,
1096    // or value 0 if b is also not present.
1097    let m12 = dict.m12.unwrap_or(dict.b.unwrap_or(0.0));
1098
1099    // Step 4. If m21 is not present then set it to the value of member c,
1100    // or value 0 if c is also not present.
1101    let m21 = dict.m21.unwrap_or(dict.c.unwrap_or(0.0));
1102
1103    // Step 5. If m22 is not present then set it to the value of member d,
1104    // or value 1 if d is also not present.
1105    let m22 = dict.m22.unwrap_or(dict.d.unwrap_or(1.0));
1106
1107    // Step 6. If m41 is not present then set it to the value of member e,
1108    // or value 0 if e is also not present.
1109    let m41 = dict.m41.unwrap_or(dict.e.unwrap_or(0.0));
1110
1111    // Step 7. If m42 is not present then set it to the value of member f,
1112    // or value 0 if f is also not present.
1113    let m42 = dict.m42.unwrap_or(dict.f.unwrap_or(0.0));
1114
1115    Ok(Transform2D::new(m11, m12, m21, m22, m41, m42))
1116}
1117
1118/// <https://drafts.fxtf.org/geometry-1/#matrix-validate-and-fixup>
1119fn validate_and_fixup(dict: &DOMMatrixInit) -> Fallible<(bool, Transform3D<f64>)> {
1120    // Step 1. Validate and fixup (2D) dict.
1121    let transform2d = validate_and_fixup_2d(&dict.parent)?;
1122
1123    // Step 2. If is2D is true and: at least one of m13, m14, m23, m24, m31,
1124    // m32, m34, m43 are present with a value other than 0 or -0, or at least
1125    // one of m33, m44 are present with a value other than 1, then throw
1126    // a TypeError exception and abort these steps.
1127    if dict.is2D == Some(true) &&
1128        (dict.m13 != 0.0 ||
1129            dict.m14 != 0.0 ||
1130            dict.m23 != 0.0 ||
1131            dict.m24 != 0.0 ||
1132            dict.m31 != 0.0 ||
1133            dict.m32 != 0.0 ||
1134            dict.m34 != 0.0 ||
1135            dict.m43 != 0.0 ||
1136            dict.m33 != 1.0 ||
1137            dict.m44 != 1.0)
1138    {
1139        return Err(error::Error::Type(
1140            c"The is2D member is set to true but the input matrix is a 3d matrix.".to_owned(),
1141        ));
1142    }
1143
1144    let mut is_2d = dict.is2D;
1145
1146    // Step 3. If is2D is not present and at least one of m13, m14, m23, m24,
1147    // m31, m32, m34, m43 are present with a value other than 0 or -0, or at
1148    // least one of m33, m44 are present with a value other than 1, set is2D
1149    // to false.
1150    if is_2d.is_none() &&
1151        (dict.m13 != 0.0 ||
1152            dict.m14 != 0.0 ||
1153            dict.m23 != 0.0 ||
1154            dict.m24 != 0.0 ||
1155            dict.m31 != 0.0 ||
1156            dict.m32 != 0.0 ||
1157            dict.m34 != 0.0 ||
1158            dict.m43 != 0.0 ||
1159            dict.m33 != 1.0 ||
1160            dict.m44 != 1.0)
1161    {
1162        is_2d = Some(false);
1163    }
1164
1165    // Step 4. If is2D is still not present, set it to true.
1166    let is_2d = is_2d.unwrap_or(true);
1167
1168    let mut transform = transform2d.to_3d();
1169    transform.m13 = dict.m13;
1170    transform.m14 = dict.m14;
1171    transform.m23 = dict.m23;
1172    transform.m24 = dict.m24;
1173    transform.m31 = dict.m31;
1174    transform.m32 = dict.m32;
1175    transform.m33 = dict.m33;
1176    transform.m34 = dict.m34;
1177    transform.m43 = dict.m43;
1178    transform.m44 = dict.m44;
1179
1180    Ok((is_2d, transform))
1181}
1182
1183/// <https://drafts.fxtf.org/geometry-1/#create-a-dommatrixreadonly-from-the-2d-dictionary>
1184pub(crate) fn dommatrix2dinit_to_matrix(dict: &DOMMatrix2DInit) -> Fallible<Transform2D<f64>> {
1185    // Step 1. Validate and fixup (2D) other.
1186    // Step 2. Return the result of invoking create a 2d matrix of type
1187    // DOMMatrixReadOnly or DOMMatrix as appropriate, with a sequence of
1188    // numbers, the values being the 6 elements m11, m12, m21, m22, m41 and m42
1189    // of other in the given order.
1190    validate_and_fixup_2d(dict)
1191}
1192
1193/// <https://drafts.fxtf.org/geometry-1/#create-a-dommatrix-from-the-dictionary>
1194pub(crate) fn dommatrixinit_to_matrix(dict: &DOMMatrixInit) -> Fallible<(bool, Transform3D<f64>)> {
1195    // Step 1. Validate and fixup other.
1196    // Step 2. Return the result of invoking create a 3d matrix of type
1197    // DOMMatrixReadOnly or DOMMatrix as appropriate, with a sequence of
1198    // numbers, the values being the 16 elements m11, m12, m13, ..., m44
1199    // of other in the given order.
1200    validate_and_fixup(dict)
1201}
1202
1203#[inline]
1204fn normalize_point(x: f64, y: f64, z: f64) -> (f64, f64, f64) {
1205    let len = (x * x + y * y + z * z).sqrt();
1206    if len == 0.0 {
1207        (0.0, 0.0, 0.0)
1208    } else {
1209        (x / len, y / len, z / len)
1210    }
1211}
1212
1213pub(crate) fn transform_to_matrix(value: String) -> Fallible<(bool, Transform3D<f64>)> {
1214    use style::properties::longhands::transform;
1215
1216    let mut input = ParserInput::new(&value);
1217    let mut parser = Parser::new(&mut input);
1218    let url_data = Url::parse("about:blank").unwrap().into();
1219    let context =
1220        parser_context_for_anonymous_content(CssRuleType::Style, ParsingMode::DEFAULT, &url_data);
1221
1222    let transform = match parser.parse_entirely(|t| transform::parse(&context, t)) {
1223        Ok(result) => result,
1224        Err(..) => return Err(error::Error::Syntax(None)),
1225    };
1226
1227    let (m, is_3d) = match transform.to_transform_3d_matrix_f64(None) {
1228        Ok(result) => result,
1229        Err(..) => return Err(error::Error::Syntax(None)),
1230    };
1231
1232    Ok((!is_3d, m))
1233}