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.to_string())
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) -> 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 cx = GlobalScope::get_cx();
876        let to_string = |f: f64| {
877            let value = jsval::DoubleValue(f);
878
879            unsafe {
880                rooted!(in(*cx) let mut rooted_value = value);
881                let serialization = std::ptr::NonNull::new(ToString(*cx, rooted_value.handle()))
882                    .expect("Pointer cannot be null");
883                jsstr_to_string(*cx, serialization)
884            }
885        };
886
887        // Step 2. Let string be the empty string.
888        // Step 3. If is 2D is true, then:
889        let string = if self.is2D() {
890            // Step 3.1 Append "matrix(" to string.
891            // Step 3.2 Append ! ToString(m11 element) to string.
892            // Step 3.3 Append ", " to string.
893            // Step 3.4 Append ! ToString(m12 element) to string.
894            // Step 3.5 Append ", " to string.
895            // Step 3.6 Append ! ToString(m21 element) to string.
896            // Step 3.7 Append ", " to string.
897            // Step 3.8 Append ! ToString(m22 element) to string.
898            // Step 3.9 Append ", " to string.
899            // Step 3.10 Append ! ToString(m41 element) to string.
900            // Step 3.11 Append ", " to string.
901            // Step 3.12 Append ! ToString(m42 element) to string.
902            // Step 3.13 Append ")" to string.
903            format!(
904                "matrix({}, {}, {}, {}, {}, {})",
905                to_string(mat.m11),
906                to_string(mat.m12),
907                to_string(mat.m21),
908                to_string(mat.m22),
909                to_string(mat.m41),
910                to_string(mat.m42)
911            )
912            .into()
913        }
914        // Step 4. Otherwise:
915        else {
916            // Step 4.1 Append "matrix3d(" to string.
917            // Step 4.2 Append ! ToString(m11 element) to string.
918            // Step 4.3 Append ", " to string.
919            // Step 4.4 Append ! ToString(m12 element) to string.
920            // Step 4.5 Append ", " to string.
921            // Step 4.6 Append ! ToString(m13 element) to string.
922            // Step 4.7 Append ", " to string.
923            // Step 4.8 Append ! ToString(m14 element) to string.
924            // Step 4.9 Append ", " to string.
925            // Step 4.10 Append ! ToString(m21 element) to string.
926            // Step 4.11 Append ", " to string.
927            // Step 4.12 Append ! ToString(m22 element) to string.
928            // Step 4.13 Append ", " to string.
929            // Step 4.14 Append ! ToString(m23 element) to string.
930            // Step 4.15 Append ", " to string.
931            // Step 4.16 Append ! ToString(m24 element) to string.
932            // Step 4.17 Append ", " to string.
933            // Step 4.18 Append ! ToString(m41 element) to string.
934            // Step 4.19 Append ", " to string.
935            // Step 4.20 Append ! ToString(m42 element) to string.
936            // Step 4.21 Append ", " to string.
937            // Step 4.22 Append ! ToString(m43 element) to string.
938            // Step 4.23 Append ", " to string.
939            // Step 4.24 Append ! ToString(m44 element) to string.
940            // Step 4.25 Append ")" to string.
941
942            // NOTE: The spec is wrong and missing the m3* elements.
943            // (https://github.com/w3c/fxtf-drafts/issues/574)
944            format!(
945                "matrix3d({}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {})",
946                to_string(mat.m11),
947                to_string(mat.m12),
948                to_string(mat.m13),
949                to_string(mat.m14),
950                to_string(mat.m21),
951                to_string(mat.m22),
952                to_string(mat.m23),
953                to_string(mat.m24),
954                to_string(mat.m31),
955                to_string(mat.m32),
956                to_string(mat.m33),
957                to_string(mat.m34),
958                to_string(mat.m41),
959                to_string(mat.m42),
960                to_string(mat.m43),
961                to_string(mat.m44)
962            )
963            .into()
964        };
965
966        Ok(string)
967    }
968}
969
970impl Serializable for DOMMatrixReadOnly {
971    type Index = DomMatrixIndex;
972    type Data = DomMatrix;
973
974    fn serialize(&self) -> Result<(DomMatrixId, Self::Data), ()> {
975        let serialized = if self.is2D() {
976            DomMatrix {
977                matrix: Transform3D::new(
978                    self.M11(),
979                    self.M12(),
980                    f64::NAN,
981                    f64::NAN,
982                    self.M21(),
983                    self.M22(),
984                    f64::NAN,
985                    f64::NAN,
986                    f64::NAN,
987                    f64::NAN,
988                    f64::NAN,
989                    f64::NAN,
990                    self.M41(),
991                    self.M42(),
992                    f64::NAN,
993                    f64::NAN,
994                ),
995                is_2d: true,
996            }
997        } else {
998            DomMatrix {
999                matrix: *self.matrix(),
1000                is_2d: false,
1001            }
1002        };
1003        Ok((DomMatrixId::new(), serialized))
1004    }
1005
1006    fn deserialize(
1007        owner: &GlobalScope,
1008        serialized: Self::Data,
1009        can_gc: CanGc,
1010    ) -> Result<DomRoot<Self>, ()>
1011    where
1012        Self: Sized,
1013    {
1014        if serialized.is_2d {
1015            Ok(Self::new(
1016                owner,
1017                true,
1018                Transform3D::new(
1019                    serialized.matrix.m11,
1020                    serialized.matrix.m12,
1021                    0.0,
1022                    0.0,
1023                    serialized.matrix.m21,
1024                    serialized.matrix.m22,
1025                    0.0,
1026                    0.0,
1027                    0.0,
1028                    0.0,
1029                    1.0,
1030                    0.0,
1031                    serialized.matrix.m41,
1032                    serialized.matrix.m42,
1033                    0.0,
1034                    1.0,
1035                ),
1036                can_gc,
1037            ))
1038        } else {
1039            Ok(Self::new(owner, false, serialized.matrix, can_gc))
1040        }
1041    }
1042
1043    fn serialized_storage<'a>(
1044        data: StructuredData<'a, '_>,
1045    ) -> &'a mut Option<FxHashMap<DomMatrixId, Self::Data>> {
1046        match data {
1047            StructuredData::Reader(reader) => &mut reader.matrices,
1048            StructuredData::Writer(writer) => &mut writer.matrices,
1049        }
1050    }
1051}
1052
1053// https://drafts.fxtf.org/geometry-1/#dom-dommatrixreadonly-dommatrixreadonly-numbersequence
1054pub(crate) fn entries_to_matrix(entries: &[f64]) -> Fallible<(bool, Transform3D<f64>)> {
1055    if let Ok(array) = entries.try_into() {
1056        Ok((true, Transform2D::from_array(array).to_3d()))
1057    } else if let Ok(array) = entries.try_into() {
1058        Ok((false, Transform3D::from_array(array)))
1059    } else {
1060        let err_msg = cformat!("Expected 6 or 16 entries, but found {}.", entries.len());
1061        Err(error::Error::Type(err_msg))
1062    }
1063}
1064
1065/// <https://drafts.fxtf.org/geometry-1/#matrix-validate-and-fixup-2d>
1066fn validate_and_fixup_2d(dict: &DOMMatrix2DInit) -> Fallible<Transform2D<f64>> {
1067    // <https://tc39.es/ecma262/#sec-numeric-types-number-sameValueZero>
1068    let same_value_zero = |x: f64, y: f64| -> bool { x.is_nan() && y.is_nan() || x == y };
1069
1070    // Step 1. If if at least one of the following conditions are true for dict,
1071    // then throw a TypeError exception and abort these steps.
1072    if dict.a.is_some() &&
1073        dict.m11.is_some() &&
1074        !same_value_zero(dict.a.unwrap(), dict.m11.unwrap()) ||
1075        dict.b.is_some() &&
1076            dict.m12.is_some() &&
1077            !same_value_zero(dict.b.unwrap(), dict.m12.unwrap()) ||
1078        dict.c.is_some() &&
1079            dict.m21.is_some() &&
1080            !same_value_zero(dict.c.unwrap(), dict.m21.unwrap()) ||
1081        dict.d.is_some() &&
1082            dict.m22.is_some() &&
1083            !same_value_zero(dict.d.unwrap(), dict.m22.unwrap()) ||
1084        dict.e.is_some() &&
1085            dict.m41.is_some() &&
1086            !same_value_zero(dict.e.unwrap(), dict.m41.unwrap()) ||
1087        dict.f.is_some() &&
1088            dict.m42.is_some() &&
1089            !same_value_zero(dict.f.unwrap(), dict.m42.unwrap())
1090    {
1091        return Err(error::Error::Type(
1092            c"Property mismatch on matrix initialization.".to_owned(),
1093        ));
1094    }
1095
1096    // Step 2. If m11 is not present then set it to the value of member a,
1097    // or value 1 if a is also not present.
1098    let m11 = dict.m11.unwrap_or(dict.a.unwrap_or(1.0));
1099
1100    // Step 3. If m12 is not present then set it to the value of member b,
1101    // or value 0 if b is also not present.
1102    let m12 = dict.m12.unwrap_or(dict.b.unwrap_or(0.0));
1103
1104    // Step 4. If m21 is not present then set it to the value of member c,
1105    // or value 0 if c is also not present.
1106    let m21 = dict.m21.unwrap_or(dict.c.unwrap_or(0.0));
1107
1108    // Step 5. If m22 is not present then set it to the value of member d,
1109    // or value 1 if d is also not present.
1110    let m22 = dict.m22.unwrap_or(dict.d.unwrap_or(1.0));
1111
1112    // Step 6. If m41 is not present then set it to the value of member e,
1113    // or value 0 if e is also not present.
1114    let m41 = dict.m41.unwrap_or(dict.e.unwrap_or(0.0));
1115
1116    // Step 7. If m42 is not present then set it to the value of member f,
1117    // or value 0 if f is also not present.
1118    let m42 = dict.m42.unwrap_or(dict.f.unwrap_or(0.0));
1119
1120    Ok(Transform2D::new(m11, m12, m21, m22, m41, m42))
1121}
1122
1123/// <https://drafts.fxtf.org/geometry-1/#matrix-validate-and-fixup>
1124fn validate_and_fixup(dict: &DOMMatrixInit) -> Fallible<(bool, Transform3D<f64>)> {
1125    // Step 1. Validate and fixup (2D) dict.
1126    let transform2d = validate_and_fixup_2d(&dict.parent)?;
1127
1128    // Step 2. If is2D is true and: at least one of m13, m14, m23, m24, m31,
1129    // m32, m34, m43 are present with a value other than 0 or -0, or at least
1130    // one of m33, m44 are present with a value other than 1, then throw
1131    // a TypeError exception and abort these steps.
1132    if dict.is2D == Some(true) &&
1133        (dict.m13 != 0.0 ||
1134            dict.m14 != 0.0 ||
1135            dict.m23 != 0.0 ||
1136            dict.m24 != 0.0 ||
1137            dict.m31 != 0.0 ||
1138            dict.m32 != 0.0 ||
1139            dict.m34 != 0.0 ||
1140            dict.m43 != 0.0 ||
1141            dict.m33 != 1.0 ||
1142            dict.m44 != 1.0)
1143    {
1144        return Err(error::Error::Type(
1145            c"The is2D member is set to true but the input matrix is a 3d matrix.".to_owned(),
1146        ));
1147    }
1148
1149    let mut is_2d = dict.is2D;
1150
1151    // Step 3. If is2D is not present and at least one of m13, m14, m23, m24,
1152    // m31, m32, m34, m43 are present with a value other than 0 or -0, or at
1153    // least one of m33, m44 are present with a value other than 1, set is2D
1154    // to false.
1155    if is_2d.is_none() &&
1156        (dict.m13 != 0.0 ||
1157            dict.m14 != 0.0 ||
1158            dict.m23 != 0.0 ||
1159            dict.m24 != 0.0 ||
1160            dict.m31 != 0.0 ||
1161            dict.m32 != 0.0 ||
1162            dict.m34 != 0.0 ||
1163            dict.m43 != 0.0 ||
1164            dict.m33 != 1.0 ||
1165            dict.m44 != 1.0)
1166    {
1167        is_2d = Some(false);
1168    }
1169
1170    // Step 4. If is2D is still not present, set it to true.
1171    let is_2d = is_2d.unwrap_or(true);
1172
1173    let mut transform = transform2d.to_3d();
1174    transform.m13 = dict.m13;
1175    transform.m14 = dict.m14;
1176    transform.m23 = dict.m23;
1177    transform.m24 = dict.m24;
1178    transform.m31 = dict.m31;
1179    transform.m32 = dict.m32;
1180    transform.m33 = dict.m33;
1181    transform.m34 = dict.m34;
1182    transform.m43 = dict.m43;
1183    transform.m44 = dict.m44;
1184
1185    Ok((is_2d, transform))
1186}
1187
1188/// <https://drafts.fxtf.org/geometry-1/#create-a-dommatrixreadonly-from-the-2d-dictionary>
1189pub(crate) fn dommatrix2dinit_to_matrix(dict: &DOMMatrix2DInit) -> Fallible<Transform2D<f64>> {
1190    // Step 1. Validate and fixup (2D) other.
1191    // Step 2. Return the result of invoking create a 2d matrix of type
1192    // DOMMatrixReadOnly or DOMMatrix as appropriate, with a sequence of
1193    // numbers, the values being the 6 elements m11, m12, m21, m22, m41 and m42
1194    // of other in the given order.
1195    validate_and_fixup_2d(dict)
1196}
1197
1198/// <https://drafts.fxtf.org/geometry-1/#create-a-dommatrix-from-the-dictionary>
1199pub(crate) fn dommatrixinit_to_matrix(dict: &DOMMatrixInit) -> Fallible<(bool, Transform3D<f64>)> {
1200    // Step 1. Validate and fixup other.
1201    // Step 2. Return the result of invoking create a 3d matrix of type
1202    // DOMMatrixReadOnly or DOMMatrix as appropriate, with a sequence of
1203    // numbers, the values being the 16 elements m11, m12, m13, ..., m44
1204    // of other in the given order.
1205    validate_and_fixup(dict)
1206}
1207
1208#[inline]
1209fn normalize_point(x: f64, y: f64, z: f64) -> (f64, f64, f64) {
1210    let len = (x * x + y * y + z * z).sqrt();
1211    if len == 0.0 {
1212        (0.0, 0.0, 0.0)
1213    } else {
1214        (x / len, y / len, z / len)
1215    }
1216}
1217
1218pub(crate) fn transform_to_matrix(value: String) -> Fallible<(bool, Transform3D<f64>)> {
1219    use style::properties::longhands::transform;
1220
1221    let mut input = ParserInput::new(&value);
1222    let mut parser = Parser::new(&mut input);
1223    let url_data = Url::parse("about:blank").unwrap().into();
1224    let context =
1225        parser_context_for_anonymous_content(CssRuleType::Style, ParsingMode::DEFAULT, &url_data);
1226
1227    let transform = match parser.parse_entirely(|t| transform::parse(&context, t)) {
1228        Ok(result) => result,
1229        Err(..) => return Err(error::Error::Syntax(None)),
1230    };
1231
1232    let (m, is_3d) = match transform.to_transform_3d_matrix_f64(None) {
1233        Ok(result) => result,
1234        Err(..) => return Err(error::Error::Syntax(None)),
1235    };
1236
1237    Ok((!is_3d, m))
1238}