Skip to main content

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