1use 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 pub(crate) fn set_m11(&self, value: f64) {
121 self.matrix.borrow_mut().m11 = value;
122 }
123
124 pub(crate) fn set_m12(&self, value: f64) {
126 self.matrix.borrow_mut().m12 = value;
127 }
128
129 pub(crate) fn set_m13(&self, value: f64) {
131 self.matrix.borrow_mut().m13 = value;
135 if value.abs() != 0. {
136 self.is2D.set(false);
137 }
138 }
139
140 pub(crate) fn set_m14(&self, value: f64) {
142 self.matrix.borrow_mut().m14 = value;
145
146 if value.abs() != 0. {
147 self.is2D.set(false);
148 }
149 }
150
151 pub(crate) fn set_m21(&self, value: f64) {
153 self.matrix.borrow_mut().m21 = value;
154 }
155
156 pub(crate) fn set_m22(&self, value: f64) {
158 self.matrix.borrow_mut().m22 = value;
159 }
160
161 pub(crate) fn set_m23(&self, value: f64) {
163 self.matrix.borrow_mut().m23 = value;
166
167 if value.abs() != 0. {
168 self.is2D.set(false);
169 }
170 }
171
172 pub(crate) fn set_m24(&self, value: f64) {
174 self.matrix.borrow_mut().m24 = value;
177
178 if value.abs() != 0. {
179 self.is2D.set(false);
180 }
181 }
182
183 pub(crate) fn set_m31(&self, value: f64) {
185 self.matrix.borrow_mut().m31 = value;
188
189 if value.abs() != 0. {
190 self.is2D.set(false);
191 }
192 }
193
194 pub(crate) fn set_m32(&self, value: f64) {
196 self.matrix.borrow_mut().m32 = value;
199
200 if value.abs() != 0. {
201 self.is2D.set(false);
202 }
203 }
204
205 pub(crate) fn set_m33(&self, value: f64) {
207 self.matrix.borrow_mut().m33 = value;
210
211 if value != 1. {
212 self.is2D.set(false);
213 }
214 }
215
216 pub(crate) fn set_m34(&self, value: f64) {
218 self.matrix.borrow_mut().m34 = value;
221
222 if value.abs() != 0. {
223 self.is2D.set(false);
224 }
225 }
226
227 pub(crate) fn set_m41(&self, value: f64) {
229 self.matrix.borrow_mut().m41 = value;
230 }
231
232 pub(crate) fn set_m42(&self, value: f64) {
234 self.matrix.borrow_mut().m42 = value;
235 }
236
237 pub(crate) fn set_m43(&self, value: f64) {
239 self.matrix.borrow_mut().m43 = value;
242
243 if value.abs() != 0. {
244 self.is2D.set(false);
245 }
246 }
247
248 pub(crate) fn set_m44(&self, value: f64) {
250 self.matrix.borrow_mut().m44 = value;
253
254 if value != 1. {
255 self.is2D.set(false);
256 }
257 }
258
259 pub(crate) fn multiply_self(&self, other: &DOMMatrixInit) -> Fallible<()> {
261 dommatrixinit_to_matrix(other).map(|(is2D, other_matrix)| {
263 let mut matrix = self.matrix.borrow_mut();
265 *matrix = other_matrix.then(&matrix);
266 if !is2D {
268 self.is2D.set(false);
269 }
270 })
272 }
273
274 pub(crate) fn pre_multiply_self(&self, other: &DOMMatrixInit) -> Fallible<()> {
276 dommatrixinit_to_matrix(other).map(|(is2D, other_matrix)| {
278 let mut matrix = self.matrix.borrow_mut();
280 *matrix = matrix.then(&other_matrix);
281 if !is2D {
283 self.is2D.set(false);
284 }
285 })
287 }
288
289 pub(crate) fn translate_self(&self, tx: f64, ty: f64, tz: f64) {
291 let translation = Transform3D::translation(tx, ty, tz);
293 let mut matrix = self.matrix.borrow_mut();
294 *matrix = translation.then(&matrix);
295 if tz != 0.0 {
297 self.is2D.set(false);
298 }
299 }
301
302 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 self.translate_self(originX, originY, originZ);
314 let scaleY = scaleY.unwrap_or(scaleX);
316 {
318 let scale3D = Transform3D::scale(scaleX, scaleY, scaleZ);
319 let mut matrix = self.matrix.borrow_mut();
320 *matrix = scale3D.then(&matrix);
321 }
322 originX = -originX;
324 originY = -originY;
325 originZ = -originZ;
326 self.translate_self(originX, originY, originZ);
328 if scaleZ != 1.0 || originZ != 0.0 {
330 self.is2D.set(false);
331 }
332 }
334
335 pub(crate) fn scale_3d_self(&self, scale: f64, originX: f64, originY: f64, originZ: f64) {
337 self.translate_self(originX, originY, originZ);
339 {
341 let scale3D = Transform3D::scale(scale, scale, scale);
342 let mut matrix = self.matrix.borrow_mut();
343 *matrix = scale3D.then(&matrix);
344 }
345 self.translate_self(-originX, -originY, -originZ);
347 if scale != 1.0 {
349 self.is2D.set(false);
350 }
351 }
353
354 pub(crate) fn rotate_self(&self, mut rotX: f64, mut rotY: Option<f64>, mut rotZ: Option<f64>) {
356 if rotY.is_none() && rotZ.is_none() {
358 rotZ = Some(rotX);
359 rotX = 0.0;
360 rotY = Some(0.0);
361 }
362 let rotY = rotY.unwrap_or(0.0);
364 let rotZ = rotZ.unwrap_or(0.0);
366 if rotX != 0.0 || rotY != 0.0 {
368 self.is2D.set(false);
369 }
370 if rotZ != 0.0 {
371 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 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 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 }
390
391 pub(crate) fn rotate_from_vector_self(&self, x: f64, y: f64) {
393 if y != 0.0 || x < 0.0 {
395 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 }
403
404 pub(crate) fn rotate_axis_angle_self(&self, x: f64, y: f64, z: f64, angle: f64) {
406 let (norm_x, norm_y, norm_z) = normalize_point(x, y, z);
408 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 if x != 0.0 || y != 0.0 {
415 self.is2D.set(false);
416 }
417 }
419
420 pub(crate) fn skew_x_self(&self, sx: f64) {
422 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 }
428
429 pub(crate) fn skew_y_self(&self, sy: f64) {
431 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 }
437
438 pub(crate) fn invert_self(&self) {
440 let mut matrix = self.matrix.borrow_mut();
441 let inverted = match self.is2D() {
443 true => matrix.to_2d().inverse().map(|m| m.to_3d()),
444 false => matrix.inverse(),
445 };
446
447 *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 }
472}
473
474#[expect(non_snake_case)]
475impl DOMMatrixReadOnlyMethods<crate::DomTypeHolder> for DOMMatrixReadOnly {
476 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 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 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 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 fn M11(&self) -> f64 {
553 self.matrix.borrow().m11
554 }
555
556 fn M12(&self) -> f64 {
558 self.matrix.borrow().m12
559 }
560
561 fn M13(&self) -> f64 {
563 self.matrix.borrow().m13
564 }
565
566 fn M14(&self) -> f64 {
568 self.matrix.borrow().m14
569 }
570
571 fn M21(&self) -> f64 {
573 self.matrix.borrow().m21
574 }
575
576 fn M22(&self) -> f64 {
578 self.matrix.borrow().m22
579 }
580
581 fn M23(&self) -> f64 {
583 self.matrix.borrow().m23
584 }
585
586 fn M24(&self) -> f64 {
588 self.matrix.borrow().m24
589 }
590
591 fn M31(&self) -> f64 {
593 self.matrix.borrow().m31
594 }
595
596 fn M32(&self) -> f64 {
598 self.matrix.borrow().m32
599 }
600
601 fn M33(&self) -> f64 {
603 self.matrix.borrow().m33
604 }
605
606 fn M34(&self) -> f64 {
608 self.matrix.borrow().m34
609 }
610
611 fn M41(&self) -> f64 {
613 self.matrix.borrow().m41
614 }
615
616 fn M42(&self) -> f64 {
618 self.matrix.borrow().m42
619 }
620
621 fn M43(&self) -> f64 {
623 self.matrix.borrow().m43
624 }
625
626 fn M44(&self) -> f64 {
628 self.matrix.borrow().m44
629 }
630
631 fn A(&self) -> f64 {
633 self.M11()
634 }
635
636 fn B(&self) -> f64 {
638 self.M12()
639 }
640
641 fn C(&self) -> f64 {
643 self.M21()
644 }
645
646 fn D(&self) -> f64 {
648 self.M22()
649 }
650
651 fn E(&self) -> f64 {
653 self.M41()
654 }
655
656 fn F(&self) -> f64 {
658 self.M42()
659 }
660
661 fn Is2D(&self) -> bool {
663 self.is2D.get()
664 }
665
666 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 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 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 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 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 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 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 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 fn SkewX(&self, sx: f64, can_gc: CanGc) -> DomRoot<DOMMatrix> {
762 DOMMatrix::from_readonly(&self.global(), self, can_gc).SkewXSelf(sx)
763 }
764
765 fn SkewY(&self, sy: f64, can_gc: CanGc) -> DomRoot<DOMMatrix> {
767 DOMMatrix::from_readonly(&self.global(), self, can_gc).SkewYSelf(sy)
768 }
769
770 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 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 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 fn Inverse(&self, can_gc: CanGc) -> DomRoot<DOMMatrix> {
797 DOMMatrix::from_readonly(&self.global(), self, can_gc).InvertSelf()
798 }
799
800 fn TransformPoint(&self, point: &DOMPointInit, can_gc: CanGc) -> DomRoot<DOMPoint> {
802 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 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 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 #[expect(unsafe_code)]
845 fn Stringifier(&self) -> Fallible<DOMString> {
846 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 let string = if self.is2D() {
884 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 else {
910 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
1047pub(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
1059fn validate_and_fixup_2d(dict: &DOMMatrix2DInit) -> Fallible<Transform2D<f64>> {
1061 let same_value_zero = |x: f64, y: f64| -> bool { x.is_nan() && y.is_nan() || x == y };
1063
1064 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 let m11 = dict.m11.unwrap_or(dict.a.unwrap_or(1.0));
1093
1094 let m12 = dict.m12.unwrap_or(dict.b.unwrap_or(0.0));
1097
1098 let m21 = dict.m21.unwrap_or(dict.c.unwrap_or(0.0));
1101
1102 let m22 = dict.m22.unwrap_or(dict.d.unwrap_or(1.0));
1105
1106 let m41 = dict.m41.unwrap_or(dict.e.unwrap_or(0.0));
1109
1110 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
1117fn validate_and_fixup(dict: &DOMMatrixInit) -> Fallible<(bool, Transform3D<f64>)> {
1119 let transform2d = validate_and_fixup_2d(&dict.parent)?;
1121
1122 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 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 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
1182pub(crate) fn dommatrix2dinit_to_matrix(dict: &DOMMatrix2DInit) -> Fallible<Transform2D<f64>> {
1184 validate_and_fixup_2d(dict)
1190}
1191
1192pub(crate) fn dommatrixinit_to_matrix(dict: &DOMMatrixInit) -> Fallible<(bool, Transform3D<f64>)> {
1194 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}