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::cformat;
21use script_bindings::trace::RootedTraceableBox;
22use style::stylesheets::CssRuleType;
23use style_traits::ParsingMode;
24use url::Url;
25
26use crate::css::parser_context_for_anonymous_content;
27use crate::dom::bindings::buffer_source::create_buffer_source;
28use crate::dom::bindings::cell::{DomRefCell, Ref};
29use crate::dom::bindings::codegen::Bindings::DOMMatrixBinding::{
30 DOMMatrix2DInit, DOMMatrixInit, DOMMatrixMethods,
31};
32use crate::dom::bindings::codegen::Bindings::DOMMatrixReadOnlyBinding::DOMMatrixReadOnlyMethods;
33use crate::dom::bindings::codegen::Bindings::DOMPointBinding::DOMPointInit;
34use crate::dom::bindings::codegen::UnionTypes::StringOrUnrestrictedDoubleSequence;
35use crate::dom::bindings::error;
36use crate::dom::bindings::error::Fallible;
37use crate::dom::bindings::inheritance::Castable;
38use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object_with_proto};
39use crate::dom::bindings::root::DomRoot;
40use crate::dom::bindings::serializable::Serializable;
41use crate::dom::bindings::str::DOMString;
42use crate::dom::bindings::structuredclone::StructuredData;
43use crate::dom::dommatrix::DOMMatrix;
44use crate::dom::dompoint::DOMPoint;
45use crate::dom::globalscope::GlobalScope;
46use crate::dom::window::Window;
47use crate::script_runtime::{CanGc, JSContext};
48
49#[dom_struct]
50#[expect(non_snake_case)]
51pub(crate) struct DOMMatrixReadOnly {
52 reflector_: Reflector,
53 #[no_trace]
54 matrix: DomRefCell<Transform3D<f64>>,
55 is2D: Cell<bool>,
56}
57
58#[expect(non_snake_case)]
59impl DOMMatrixReadOnly {
60 pub(crate) fn new(
61 global: &GlobalScope,
62 is2D: bool,
63 matrix: Transform3D<f64>,
64 can_gc: CanGc,
65 ) -> DomRoot<Self> {
66 Self::new_with_proto(global, None, is2D, matrix, can_gc)
67 }
68
69 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
70 fn new_with_proto(
71 global: &GlobalScope,
72 proto: Option<HandleObject>,
73 is2D: bool,
74 matrix: Transform3D<f64>,
75 can_gc: CanGc,
76 ) -> DomRoot<Self> {
77 let dommatrix = Self::new_inherited(is2D, matrix);
78 reflect_dom_object_with_proto(Box::new(dommatrix), global, proto, can_gc)
79 }
80
81 pub(crate) fn new_inherited(is2D: bool, matrix: Transform3D<f64>) -> Self {
82 DOMMatrixReadOnly {
83 reflector_: Reflector::new(),
84 matrix: DomRefCell::new(matrix),
85 is2D: Cell::new(is2D),
86 }
87 }
88
89 pub(crate) fn matrix(&self) -> Ref<'_, Transform3D<f64>> {
90 self.matrix.borrow()
91 }
92
93 pub(crate) fn set_matrix(&self, value: Transform3D<f64>) {
94 self.set_m11(value.m11);
95 self.set_m12(value.m12);
96 self.set_m13(value.m13);
97 self.set_m14(value.m14);
98 self.set_m21(value.m21);
99 self.set_m22(value.m22);
100 self.set_m23(value.m23);
101 self.set_m24(value.m24);
102 self.set_m31(value.m31);
103 self.set_m32(value.m32);
104 self.set_m33(value.m33);
105 self.set_m34(value.m34);
106 self.set_m41(value.m41);
107 self.set_m42(value.m42);
108 self.set_m43(value.m43);
109 self.set_m44(value.m44);
110 }
111
112 pub(crate) fn is2D(&self) -> bool {
113 self.is2D.get()
114 }
115
116 pub(crate) fn set_is2D(&self, value: bool) {
117 self.is2D.set(value);
118 }
119
120 pub(crate) fn set_m11(&self, value: f64) {
122 self.matrix.borrow_mut().m11 = value;
123 }
124
125 pub(crate) fn set_m12(&self, value: f64) {
127 self.matrix.borrow_mut().m12 = value;
128 }
129
130 pub(crate) fn set_m13(&self, value: f64) {
132 self.matrix.borrow_mut().m13 = value;
136 if value.abs() != 0. {
137 self.is2D.set(false);
138 }
139 }
140
141 pub(crate) fn set_m14(&self, value: f64) {
143 self.matrix.borrow_mut().m14 = value;
146
147 if value.abs() != 0. {
148 self.is2D.set(false);
149 }
150 }
151
152 pub(crate) fn set_m21(&self, value: f64) {
154 self.matrix.borrow_mut().m21 = value;
155 }
156
157 pub(crate) fn set_m22(&self, value: f64) {
159 self.matrix.borrow_mut().m22 = value;
160 }
161
162 pub(crate) fn set_m23(&self, value: f64) {
164 self.matrix.borrow_mut().m23 = value;
167
168 if value.abs() != 0. {
169 self.is2D.set(false);
170 }
171 }
172
173 pub(crate) fn set_m24(&self, value: f64) {
175 self.matrix.borrow_mut().m24 = value;
178
179 if value.abs() != 0. {
180 self.is2D.set(false);
181 }
182 }
183
184 pub(crate) fn set_m31(&self, value: f64) {
186 self.matrix.borrow_mut().m31 = value;
189
190 if value.abs() != 0. {
191 self.is2D.set(false);
192 }
193 }
194
195 pub(crate) fn set_m32(&self, value: f64) {
197 self.matrix.borrow_mut().m32 = value;
200
201 if value.abs() != 0. {
202 self.is2D.set(false);
203 }
204 }
205
206 pub(crate) fn set_m33(&self, value: f64) {
208 self.matrix.borrow_mut().m33 = value;
211
212 if value != 1. {
213 self.is2D.set(false);
214 }
215 }
216
217 pub(crate) fn set_m34(&self, value: f64) {
219 self.matrix.borrow_mut().m34 = value;
222
223 if value.abs() != 0. {
224 self.is2D.set(false);
225 }
226 }
227
228 pub(crate) fn set_m41(&self, value: f64) {
230 self.matrix.borrow_mut().m41 = value;
231 }
232
233 pub(crate) fn set_m42(&self, value: f64) {
235 self.matrix.borrow_mut().m42 = value;
236 }
237
238 pub(crate) fn set_m43(&self, value: f64) {
240 self.matrix.borrow_mut().m43 = value;
243
244 if value.abs() != 0. {
245 self.is2D.set(false);
246 }
247 }
248
249 pub(crate) fn set_m44(&self, value: f64) {
251 self.matrix.borrow_mut().m44 = value;
254
255 if value != 1. {
256 self.is2D.set(false);
257 }
258 }
259
260 pub(crate) fn multiply_self(&self, other: &DOMMatrixInit) -> Fallible<()> {
262 dommatrixinit_to_matrix(other).map(|(is2D, other_matrix)| {
264 let mut matrix = self.matrix.borrow_mut();
266 *matrix = other_matrix.then(&matrix);
267 if !is2D {
269 self.is2D.set(false);
270 }
271 })
273 }
274
275 pub(crate) fn pre_multiply_self(&self, other: &DOMMatrixInit) -> Fallible<()> {
277 dommatrixinit_to_matrix(other).map(|(is2D, other_matrix)| {
279 let mut matrix = self.matrix.borrow_mut();
281 *matrix = matrix.then(&other_matrix);
282 if !is2D {
284 self.is2D.set(false);
285 }
286 })
288 }
289
290 pub(crate) fn translate_self(&self, tx: f64, ty: f64, tz: f64) {
292 let translation = Transform3D::translation(tx, ty, tz);
294 let mut matrix = self.matrix.borrow_mut();
295 *matrix = translation.then(&matrix);
296 if tz != 0.0 {
298 self.is2D.set(false);
299 }
300 }
302
303 pub(crate) fn scale_self(
305 &self,
306 scaleX: f64,
307 scaleY: Option<f64>,
308 scaleZ: f64,
309 mut originX: f64,
310 mut originY: f64,
311 mut originZ: f64,
312 ) {
313 self.translate_self(originX, originY, originZ);
315 let scaleY = scaleY.unwrap_or(scaleX);
317 {
319 let scale3D = Transform3D::scale(scaleX, scaleY, scaleZ);
320 let mut matrix = self.matrix.borrow_mut();
321 *matrix = scale3D.then(&matrix);
322 }
323 originX = -originX;
325 originY = -originY;
326 originZ = -originZ;
327 self.translate_self(originX, originY, originZ);
329 if scaleZ != 1.0 || originZ != 0.0 {
331 self.is2D.set(false);
332 }
333 }
335
336 pub(crate) fn scale_3d_self(&self, scale: f64, originX: f64, originY: f64, originZ: f64) {
338 self.translate_self(originX, originY, originZ);
340 {
342 let scale3D = Transform3D::scale(scale, scale, scale);
343 let mut matrix = self.matrix.borrow_mut();
344 *matrix = scale3D.then(&matrix);
345 }
346 self.translate_self(-originX, -originY, -originZ);
348 if scale != 1.0 {
350 self.is2D.set(false);
351 }
352 }
354
355 pub(crate) fn rotate_self(&self, mut rotX: f64, mut rotY: Option<f64>, mut rotZ: Option<f64>) {
357 if rotY.is_none() && rotZ.is_none() {
359 rotZ = Some(rotX);
360 rotX = 0.0;
361 rotY = Some(0.0);
362 }
363 let rotY = rotY.unwrap_or(0.0);
365 let rotZ = rotZ.unwrap_or(0.0);
367 if rotX != 0.0 || rotY != 0.0 {
369 self.is2D.set(false);
370 }
371 if rotZ != 0.0 {
372 let rotation = Transform3D::rotation(0.0, 0.0, 1.0, Angle::radians(rotZ.to_radians()));
374 let mut matrix = self.matrix.borrow_mut();
375 *matrix = rotation.then(&matrix);
376 }
377 if rotY != 0.0 {
378 let rotation = Transform3D::rotation(0.0, 1.0, 0.0, Angle::radians(rotY.to_radians()));
380 let mut matrix = self.matrix.borrow_mut();
381 *matrix = rotation.then(&matrix);
382 }
383 if rotX != 0.0 {
384 let rotation = Transform3D::rotation(1.0, 0.0, 0.0, Angle::radians(rotX.to_radians()));
386 let mut matrix = self.matrix.borrow_mut();
387 *matrix = rotation.then(&matrix);
388 }
389 }
391
392 pub(crate) fn rotate_from_vector_self(&self, x: f64, y: f64) {
394 if y != 0.0 || x < 0.0 {
396 let rotZ = Angle::radians(f64::atan2(y, x));
398 let rotation = Transform3D::rotation(0.0, 0.0, 1.0, rotZ);
399 let mut matrix = self.matrix.borrow_mut();
400 *matrix = rotation.then(&matrix);
401 }
402 }
404
405 pub(crate) fn rotate_axis_angle_self(&self, x: f64, y: f64, z: f64, angle: f64) {
407 let (norm_x, norm_y, norm_z) = normalize_point(x, y, z);
409 let rotation =
411 Transform3D::rotation(norm_x, norm_y, norm_z, Angle::radians(angle.to_radians()));
412 let mut matrix = self.matrix.borrow_mut();
413 *matrix = rotation.then(&matrix);
414 if x != 0.0 || y != 0.0 {
416 self.is2D.set(false);
417 }
418 }
420
421 pub(crate) fn skew_x_self(&self, sx: f64) {
423 let skew = Transform3D::skew(Angle::radians(sx.to_radians()), Angle::radians(0.0));
425 let mut matrix = self.matrix.borrow_mut();
426 *matrix = skew.then(&matrix);
427 }
429
430 pub(crate) fn skew_y_self(&self, sy: f64) {
432 let skew = Transform3D::skew(Angle::radians(0.0), Angle::radians(sy.to_radians()));
434 let mut matrix = self.matrix.borrow_mut();
435 *matrix = skew.then(&matrix);
436 }
438
439 pub(crate) fn invert_self(&self) {
441 let mut matrix = self.matrix.borrow_mut();
442 let inverted = match self.is2D() {
444 true => matrix.to_2d().inverse().map(|m| m.to_3d()),
445 false => matrix.inverse(),
446 };
447
448 *matrix = inverted.unwrap_or_else(|| -> Transform3D<f64> {
451 self.is2D.set(false);
452 Transform3D::new(
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 f64::NAN,
469 )
470 });
471 }
473}
474
475#[expect(non_snake_case)]
476impl DOMMatrixReadOnlyMethods<crate::DomTypeHolder> for DOMMatrixReadOnly {
477 fn Constructor(
479 global: &GlobalScope,
480 proto: Option<HandleObject>,
481 can_gc: CanGc,
482 init: Option<StringOrUnrestrictedDoubleSequence>,
483 ) -> Fallible<DomRoot<Self>> {
484 if init.is_none() {
485 return Ok(Self::new_with_proto(
486 global,
487 proto,
488 true,
489 Transform3D::identity(),
490 can_gc,
491 ));
492 }
493 match init.unwrap() {
494 StringOrUnrestrictedDoubleSequence::String(ref s) => {
495 if !global.is::<Window>() {
496 return Err(error::Error::Type(
497 c"String constructor is only supported in the main thread.".to_owned(),
498 ));
499 }
500 if s.is_empty() {
501 return Ok(Self::new(global, true, Transform3D::identity(), can_gc));
502 }
503 transform_to_matrix(s.to_string())
504 .map(|(is2D, matrix)| Self::new_with_proto(global, proto, is2D, matrix, can_gc))
505 },
506 StringOrUnrestrictedDoubleSequence::UnrestrictedDoubleSequence(ref entries) => {
507 entries_to_matrix(&entries[..])
508 .map(|(is2D, matrix)| Self::new_with_proto(global, proto, is2D, matrix, can_gc))
509 },
510 }
511 }
512
513 fn FromMatrix(
515 global: &GlobalScope,
516 other: &DOMMatrixInit,
517 can_gc: CanGc,
518 ) -> Fallible<DomRoot<Self>> {
519 dommatrixinit_to_matrix(other).map(|(is2D, matrix)| Self::new(global, is2D, matrix, can_gc))
520 }
521
522 fn FromFloat32Array(
524 global: &GlobalScope,
525 array: CustomAutoRooterGuard<Float32Array>,
526 can_gc: CanGc,
527 ) -> Fallible<DomRoot<DOMMatrixReadOnly>> {
528 let vec: Vec<f64> = array.to_vec().iter().map(|&x| x as f64).collect();
529 DOMMatrixReadOnly::Constructor(
530 global,
531 None,
532 can_gc,
533 Some(StringOrUnrestrictedDoubleSequence::UnrestrictedDoubleSequence(vec)),
534 )
535 }
536
537 fn FromFloat64Array(
539 global: &GlobalScope,
540 array: CustomAutoRooterGuard<Float64Array>,
541 can_gc: CanGc,
542 ) -> Fallible<DomRoot<DOMMatrixReadOnly>> {
543 let vec: Vec<f64> = array.to_vec();
544 DOMMatrixReadOnly::Constructor(
545 global,
546 None,
547 can_gc,
548 Some(StringOrUnrestrictedDoubleSequence::UnrestrictedDoubleSequence(vec)),
549 )
550 }
551
552 fn M11(&self) -> f64 {
554 self.matrix.borrow().m11
555 }
556
557 fn M12(&self) -> f64 {
559 self.matrix.borrow().m12
560 }
561
562 fn M13(&self) -> f64 {
564 self.matrix.borrow().m13
565 }
566
567 fn M14(&self) -> f64 {
569 self.matrix.borrow().m14
570 }
571
572 fn M21(&self) -> f64 {
574 self.matrix.borrow().m21
575 }
576
577 fn M22(&self) -> f64 {
579 self.matrix.borrow().m22
580 }
581
582 fn M23(&self) -> f64 {
584 self.matrix.borrow().m23
585 }
586
587 fn M24(&self) -> f64 {
589 self.matrix.borrow().m24
590 }
591
592 fn M31(&self) -> f64 {
594 self.matrix.borrow().m31
595 }
596
597 fn M32(&self) -> f64 {
599 self.matrix.borrow().m32
600 }
601
602 fn M33(&self) -> f64 {
604 self.matrix.borrow().m33
605 }
606
607 fn M34(&self) -> f64 {
609 self.matrix.borrow().m34
610 }
611
612 fn M41(&self) -> f64 {
614 self.matrix.borrow().m41
615 }
616
617 fn M42(&self) -> f64 {
619 self.matrix.borrow().m42
620 }
621
622 fn M43(&self) -> f64 {
624 self.matrix.borrow().m43
625 }
626
627 fn M44(&self) -> f64 {
629 self.matrix.borrow().m44
630 }
631
632 fn A(&self) -> f64 {
634 self.M11()
635 }
636
637 fn B(&self) -> f64 {
639 self.M12()
640 }
641
642 fn C(&self) -> f64 {
644 self.M21()
645 }
646
647 fn D(&self) -> f64 {
649 self.M22()
650 }
651
652 fn E(&self) -> f64 {
654 self.M41()
655 }
656
657 fn F(&self) -> f64 {
659 self.M42()
660 }
661
662 fn Is2D(&self) -> bool {
664 self.is2D.get()
665 }
666
667 fn IsIdentity(&self) -> bool {
669 let matrix = self.matrix.borrow();
670 matrix.m12 == 0.0 &&
671 matrix.m13 == 0.0 &&
672 matrix.m14 == 0.0 &&
673 matrix.m21 == 0.0 &&
674 matrix.m23 == 0.0 &&
675 matrix.m24 == 0.0 &&
676 matrix.m31 == 0.0 &&
677 matrix.m32 == 0.0 &&
678 matrix.m34 == 0.0 &&
679 matrix.m41 == 0.0 &&
680 matrix.m42 == 0.0 &&
681 matrix.m43 == 0.0 &&
682 matrix.m11 == 1.0 &&
683 matrix.m22 == 1.0 &&
684 matrix.m33 == 1.0 &&
685 matrix.m44 == 1.0
686 }
687
688 fn Translate(&self, tx: f64, ty: f64, tz: f64, can_gc: CanGc) -> DomRoot<DOMMatrix> {
690 DOMMatrix::from_readonly(&self.global(), self, can_gc).TranslateSelf(tx, ty, tz)
691 }
692
693 fn Scale(
695 &self,
696 scaleX: f64,
697 scaleY: Option<f64>,
698 scaleZ: f64,
699 originX: f64,
700 originY: f64,
701 originZ: f64,
702 can_gc: CanGc,
703 ) -> DomRoot<DOMMatrix> {
704 DOMMatrix::from_readonly(&self.global(), self, can_gc)
705 .ScaleSelf(scaleX, scaleY, scaleZ, originX, originY, originZ)
706 }
707
708 fn ScaleNonUniform(&self, scaleX: f64, scaleY: f64, can_gc: CanGc) -> DomRoot<DOMMatrix> {
710 DOMMatrix::from_readonly(&self.global(), self, can_gc).ScaleSelf(
711 scaleX,
712 Some(scaleY),
713 1.0,
714 0.0,
715 0.0,
716 0.0,
717 )
718 }
719
720 fn Scale3d(
722 &self,
723 scale: f64,
724 originX: f64,
725 originY: f64,
726 originZ: f64,
727 can_gc: CanGc,
728 ) -> DomRoot<DOMMatrix> {
729 DOMMatrix::from_readonly(&self.global(), self, can_gc)
730 .Scale3dSelf(scale, originX, originY, originZ)
731 }
732
733 fn Rotate(
735 &self,
736 rotX: f64,
737 rotY: Option<f64>,
738 rotZ: Option<f64>,
739 can_gc: CanGc,
740 ) -> DomRoot<DOMMatrix> {
741 DOMMatrix::from_readonly(&self.global(), self, can_gc).RotateSelf(rotX, rotY, rotZ)
742 }
743
744 fn RotateFromVector(&self, x: f64, y: f64, can_gc: CanGc) -> DomRoot<DOMMatrix> {
746 DOMMatrix::from_readonly(&self.global(), self, can_gc).RotateFromVectorSelf(x, y)
747 }
748
749 fn RotateAxisAngle(
751 &self,
752 x: f64,
753 y: f64,
754 z: f64,
755 angle: f64,
756 can_gc: CanGc,
757 ) -> DomRoot<DOMMatrix> {
758 DOMMatrix::from_readonly(&self.global(), self, can_gc).RotateAxisAngleSelf(x, y, z, angle)
759 }
760
761 fn SkewX(&self, sx: f64, can_gc: CanGc) -> DomRoot<DOMMatrix> {
763 DOMMatrix::from_readonly(&self.global(), self, can_gc).SkewXSelf(sx)
764 }
765
766 fn SkewY(&self, sy: f64, can_gc: CanGc) -> DomRoot<DOMMatrix> {
768 DOMMatrix::from_readonly(&self.global(), self, can_gc).SkewYSelf(sy)
769 }
770
771 fn Multiply(&self, other: &DOMMatrixInit, can_gc: CanGc) -> Fallible<DomRoot<DOMMatrix>> {
773 DOMMatrix::from_readonly(&self.global(), self, can_gc).MultiplySelf(other)
774 }
775
776 fn FlipX(&self, can_gc: CanGc) -> DomRoot<DOMMatrix> {
778 let is2D = self.is2D.get();
779 let flip = Transform3D::new(
780 -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,
781 );
782 let matrix = flip.then(&self.matrix.borrow());
783 DOMMatrix::new(&self.global(), is2D, matrix, can_gc)
784 }
785
786 fn FlipY(&self, can_gc: CanGc) -> DomRoot<DOMMatrix> {
788 let is2D = self.is2D.get();
789 let flip = Transform3D::new(
790 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,
791 );
792 let matrix = flip.then(&self.matrix.borrow());
793 DOMMatrix::new(&self.global(), is2D, matrix, can_gc)
794 }
795
796 fn Inverse(&self, can_gc: CanGc) -> DomRoot<DOMMatrix> {
798 DOMMatrix::from_readonly(&self.global(), self, can_gc).InvertSelf()
799 }
800
801 fn TransformPoint(&self, point: &DOMPointInit, can_gc: CanGc) -> DomRoot<DOMPoint> {
803 let mat = self.matrix.borrow();
810 let x = point.x * mat.m11 + point.y * mat.m21 + point.z * mat.m31 + point.w * mat.m41;
811 let y = point.x * mat.m12 + point.y * mat.m22 + point.z * mat.m32 + point.w * mat.m42;
812 let z = point.x * mat.m13 + point.y * mat.m23 + point.z * mat.m33 + point.w * mat.m43;
813 let w = point.x * mat.m14 + point.y * mat.m24 + point.z * mat.m34 + point.w * mat.m44;
814
815 DOMPoint::new(&self.global(), x, y, z, w, can_gc)
816 }
817
818 fn ToFloat32Array(&self, cx: JSContext, can_gc: CanGc) -> RootedTraceableBox<HeapFloat32Array> {
820 let vec: Vec<f32> = self
821 .matrix
822 .borrow()
823 .to_array()
824 .iter()
825 .map(|&x| x as f32)
826 .collect();
827 rooted!(in (*cx) let mut array = ptr::null_mut::<JSObject>());
828 create_buffer_source(cx, &vec, array.handle_mut(), can_gc)
829 .expect("Converting matrix to float32 array should never fail")
830 }
831
832 fn ToFloat64Array(&self, cx: JSContext, can_gc: CanGc) -> RootedTraceableBox<HeapFloat64Array> {
834 rooted!(in (*cx) let mut array = ptr::null_mut::<JSObject>());
835 create_buffer_source(
836 cx,
837 &self.matrix.borrow().to_array(),
838 array.handle_mut(),
839 can_gc,
840 )
841 .expect("Converting matrix to float64 array should never fail")
842 }
843
844 #[expect(unsafe_code)]
846 fn Stringifier(&self) -> Fallible<DOMString> {
847 let mat = self.matrix.borrow();
850 if !mat.m11.is_finite() ||
851 !mat.m12.is_finite() ||
852 !mat.m13.is_finite() ||
853 !mat.m14.is_finite() ||
854 !mat.m21.is_finite() ||
855 !mat.m22.is_finite() ||
856 !mat.m23.is_finite() ||
857 !mat.m24.is_finite() ||
858 !mat.m31.is_finite() ||
859 !mat.m32.is_finite() ||
860 !mat.m33.is_finite() ||
861 !mat.m34.is_finite() ||
862 !mat.m41.is_finite() ||
863 !mat.m42.is_finite() ||
864 !mat.m43.is_finite() ||
865 !mat.m44.is_finite()
866 {
867 return Err(error::Error::InvalidState(None));
868 }
869
870 let cx = GlobalScope::get_cx();
871 let to_string = |f: f64| {
872 let value = jsval::DoubleValue(f);
873
874 unsafe {
875 rooted!(in(*cx) let mut rooted_value = value);
876 let serialization = std::ptr::NonNull::new(ToString(*cx, rooted_value.handle()))
877 .expect("Pointer cannot be null");
878 jsstr_to_string(*cx, serialization)
879 }
880 };
881
882 let string = if self.is2D() {
885 format!(
899 "matrix({}, {}, {}, {}, {}, {})",
900 to_string(mat.m11),
901 to_string(mat.m12),
902 to_string(mat.m21),
903 to_string(mat.m22),
904 to_string(mat.m41),
905 to_string(mat.m42)
906 )
907 .into()
908 }
909 else {
911 format!(
940 "matrix3d({}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {})",
941 to_string(mat.m11),
942 to_string(mat.m12),
943 to_string(mat.m13),
944 to_string(mat.m14),
945 to_string(mat.m21),
946 to_string(mat.m22),
947 to_string(mat.m23),
948 to_string(mat.m24),
949 to_string(mat.m31),
950 to_string(mat.m32),
951 to_string(mat.m33),
952 to_string(mat.m34),
953 to_string(mat.m41),
954 to_string(mat.m42),
955 to_string(mat.m43),
956 to_string(mat.m44)
957 )
958 .into()
959 };
960
961 Ok(string)
962 }
963}
964
965impl Serializable for DOMMatrixReadOnly {
966 type Index = DomMatrixIndex;
967 type Data = DomMatrix;
968
969 fn serialize(&self) -> Result<(DomMatrixId, Self::Data), ()> {
970 let serialized = if self.is2D() {
971 DomMatrix {
972 matrix: Transform3D::new(
973 self.M11(),
974 self.M12(),
975 f64::NAN,
976 f64::NAN,
977 self.M21(),
978 self.M22(),
979 f64::NAN,
980 f64::NAN,
981 f64::NAN,
982 f64::NAN,
983 f64::NAN,
984 f64::NAN,
985 self.M41(),
986 self.M42(),
987 f64::NAN,
988 f64::NAN,
989 ),
990 is_2d: true,
991 }
992 } else {
993 DomMatrix {
994 matrix: *self.matrix(),
995 is_2d: false,
996 }
997 };
998 Ok((DomMatrixId::new(), serialized))
999 }
1000
1001 fn deserialize(
1002 owner: &GlobalScope,
1003 serialized: Self::Data,
1004 can_gc: CanGc,
1005 ) -> Result<DomRoot<Self>, ()>
1006 where
1007 Self: Sized,
1008 {
1009 if serialized.is_2d {
1010 Ok(Self::new(
1011 owner,
1012 true,
1013 Transform3D::new(
1014 serialized.matrix.m11,
1015 serialized.matrix.m12,
1016 0.0,
1017 0.0,
1018 serialized.matrix.m21,
1019 serialized.matrix.m22,
1020 0.0,
1021 0.0,
1022 0.0,
1023 0.0,
1024 1.0,
1025 0.0,
1026 serialized.matrix.m41,
1027 serialized.matrix.m42,
1028 0.0,
1029 1.0,
1030 ),
1031 can_gc,
1032 ))
1033 } else {
1034 Ok(Self::new(owner, false, serialized.matrix, can_gc))
1035 }
1036 }
1037
1038 fn serialized_storage<'a>(
1039 data: StructuredData<'a, '_>,
1040 ) -> &'a mut Option<FxHashMap<DomMatrixId, Self::Data>> {
1041 match data {
1042 StructuredData::Reader(reader) => &mut reader.matrices,
1043 StructuredData::Writer(writer) => &mut writer.matrices,
1044 }
1045 }
1046}
1047
1048pub(crate) fn entries_to_matrix(entries: &[f64]) -> Fallible<(bool, Transform3D<f64>)> {
1050 if let Ok(array) = entries.try_into() {
1051 Ok((true, Transform2D::from_array(array).to_3d()))
1052 } else if let Ok(array) = entries.try_into() {
1053 Ok((false, Transform3D::from_array(array)))
1054 } else {
1055 let err_msg = cformat!("Expected 6 or 16 entries, but found {}.", entries.len());
1056 Err(error::Error::Type(err_msg))
1057 }
1058}
1059
1060fn validate_and_fixup_2d(dict: &DOMMatrix2DInit) -> Fallible<Transform2D<f64>> {
1062 let same_value_zero = |x: f64, y: f64| -> bool { x.is_nan() && y.is_nan() || x == y };
1064
1065 if dict.a.is_some() &&
1068 dict.m11.is_some() &&
1069 !same_value_zero(dict.a.unwrap(), dict.m11.unwrap()) ||
1070 dict.b.is_some() &&
1071 dict.m12.is_some() &&
1072 !same_value_zero(dict.b.unwrap(), dict.m12.unwrap()) ||
1073 dict.c.is_some() &&
1074 dict.m21.is_some() &&
1075 !same_value_zero(dict.c.unwrap(), dict.m21.unwrap()) ||
1076 dict.d.is_some() &&
1077 dict.m22.is_some() &&
1078 !same_value_zero(dict.d.unwrap(), dict.m22.unwrap()) ||
1079 dict.e.is_some() &&
1080 dict.m41.is_some() &&
1081 !same_value_zero(dict.e.unwrap(), dict.m41.unwrap()) ||
1082 dict.f.is_some() &&
1083 dict.m42.is_some() &&
1084 !same_value_zero(dict.f.unwrap(), dict.m42.unwrap())
1085 {
1086 return Err(error::Error::Type(
1087 c"Property mismatch on matrix initialization.".to_owned(),
1088 ));
1089 }
1090
1091 let m11 = dict.m11.unwrap_or(dict.a.unwrap_or(1.0));
1094
1095 let m12 = dict.m12.unwrap_or(dict.b.unwrap_or(0.0));
1098
1099 let m21 = dict.m21.unwrap_or(dict.c.unwrap_or(0.0));
1102
1103 let m22 = dict.m22.unwrap_or(dict.d.unwrap_or(1.0));
1106
1107 let m41 = dict.m41.unwrap_or(dict.e.unwrap_or(0.0));
1110
1111 let m42 = dict.m42.unwrap_or(dict.f.unwrap_or(0.0));
1114
1115 Ok(Transform2D::new(m11, m12, m21, m22, m41, m42))
1116}
1117
1118fn validate_and_fixup(dict: &DOMMatrixInit) -> Fallible<(bool, Transform3D<f64>)> {
1120 let transform2d = validate_and_fixup_2d(&dict.parent)?;
1122
1123 if dict.is2D == Some(true) &&
1128 (dict.m13 != 0.0 ||
1129 dict.m14 != 0.0 ||
1130 dict.m23 != 0.0 ||
1131 dict.m24 != 0.0 ||
1132 dict.m31 != 0.0 ||
1133 dict.m32 != 0.0 ||
1134 dict.m34 != 0.0 ||
1135 dict.m43 != 0.0 ||
1136 dict.m33 != 1.0 ||
1137 dict.m44 != 1.0)
1138 {
1139 return Err(error::Error::Type(
1140 c"The is2D member is set to true but the input matrix is a 3d matrix.".to_owned(),
1141 ));
1142 }
1143
1144 let mut is_2d = dict.is2D;
1145
1146 if is_2d.is_none() &&
1151 (dict.m13 != 0.0 ||
1152 dict.m14 != 0.0 ||
1153 dict.m23 != 0.0 ||
1154 dict.m24 != 0.0 ||
1155 dict.m31 != 0.0 ||
1156 dict.m32 != 0.0 ||
1157 dict.m34 != 0.0 ||
1158 dict.m43 != 0.0 ||
1159 dict.m33 != 1.0 ||
1160 dict.m44 != 1.0)
1161 {
1162 is_2d = Some(false);
1163 }
1164
1165 let is_2d = is_2d.unwrap_or(true);
1167
1168 let mut transform = transform2d.to_3d();
1169 transform.m13 = dict.m13;
1170 transform.m14 = dict.m14;
1171 transform.m23 = dict.m23;
1172 transform.m24 = dict.m24;
1173 transform.m31 = dict.m31;
1174 transform.m32 = dict.m32;
1175 transform.m33 = dict.m33;
1176 transform.m34 = dict.m34;
1177 transform.m43 = dict.m43;
1178 transform.m44 = dict.m44;
1179
1180 Ok((is_2d, transform))
1181}
1182
1183pub(crate) fn dommatrix2dinit_to_matrix(dict: &DOMMatrix2DInit) -> Fallible<Transform2D<f64>> {
1185 validate_and_fixup_2d(dict)
1191}
1192
1193pub(crate) fn dommatrixinit_to_matrix(dict: &DOMMatrixInit) -> Fallible<(bool, Transform3D<f64>)> {
1195 validate_and_fixup(dict)
1201}
1202
1203#[inline]
1204fn normalize_point(x: f64, y: f64, z: f64) -> (f64, f64, f64) {
1205 let len = (x * x + y * y + z * z).sqrt();
1206 if len == 0.0 {
1207 (0.0, 0.0, 0.0)
1208 } else {
1209 (x / len, y / len, z / len)
1210 }
1211}
1212
1213pub(crate) fn transform_to_matrix(value: String) -> Fallible<(bool, Transform3D<f64>)> {
1214 use style::properties::longhands::transform;
1215
1216 let mut input = ParserInput::new(&value);
1217 let mut parser = Parser::new(&mut input);
1218 let url_data = Url::parse("about:blank").unwrap().into();
1219 let context =
1220 parser_context_for_anonymous_content(CssRuleType::Style, ParsingMode::DEFAULT, &url_data);
1221
1222 let transform = match parser.parse_entirely(|t| transform::parse(&context, t)) {
1223 Ok(result) => result,
1224 Err(..) => return Err(error::Error::Syntax(None)),
1225 };
1226
1227 let (m, is_3d) = match transform.to_transform_3d_matrix_f64(None) {
1228 Ok(result) => result,
1229 Err(..) => return Err(error::Error::Syntax(None)),
1230 };
1231
1232 Ok((!is_3d, m))
1233}