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