script/dom/
dommatrixreadonly.rs

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