Skip to main content

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