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