1use std::cell::Cell;
6use std::{f64, ptr};
7
8use cssparser::Parser;
9use dom_struct::dom_struct;
10use euclid::Angle;
11use euclid::default::{Transform2D, Transform3D};
12use js::context::NoGC;
13use js::jsapi::JSObject;
14use js::jsval;
15use js::rust::{CustomAutoRooterGuard, HandleObject};
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;
26
27use crate::css::css::{ANONYMOUS_CONTENT_URL_DATA, parser_context_for_anonymous_content};
28use crate::dom::bindings::buffer_source::create_buffer_source;
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;
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;
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 cx: &mut js::context::JSContext,
61 global: &GlobalScope,
62 is2D: bool,
63 matrix: Transform3D<f64>,
64 ) -> DomRoot<Self> {
65 Self::new_with_proto(cx, global, None, is2D, matrix)
66 }
67
68 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
69 fn new_with_proto(
70 cx: &mut js::context::JSContext,
71 global: &GlobalScope,
72 proto: Option<HandleObject>,
73 is2D: bool,
74 matrix: Transform3D<f64>,
75 ) -> DomRoot<Self> {
76 let dommatrix = Self::new_inherited(is2D, matrix);
77 reflect_dom_object_with_proto(cx, Box::new(dommatrix), global, proto)
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 cx: &mut js::context::JSContext,
479 global: &GlobalScope,
480 proto: Option<HandleObject>,
481 init: Option<StringOrUnrestrictedDoubleSequence>,
482 ) -> Fallible<DomRoot<Self>> {
483 if init.is_none() {
484 return Ok(Self::new_with_proto(
485 cx,
486 global,
487 proto,
488 true,
489 Transform3D::identity(),
490 ));
491 }
492 match init.unwrap() {
493 StringOrUnrestrictedDoubleSequence::String(ref s) => {
494 if !global.is::<Window>() {
495 return Err(error::Error::Type(
496 c"String constructor is only supported in the main thread.".to_owned(),
497 ));
498 }
499 if s.is_empty() {
500 return Ok(Self::new(cx, global, true, Transform3D::identity()));
501 }
502 transform_to_matrix(&s.str())
503 .map(|(is2D, matrix)| Self::new_with_proto(cx, global, proto, is2D, matrix))
504 },
505 StringOrUnrestrictedDoubleSequence::UnrestrictedDoubleSequence(ref entries) => {
506 entries_to_matrix(&entries[..])
507 .map(|(is2D, matrix)| Self::new_with_proto(cx, global, proto, is2D, matrix))
508 },
509 }
510 }
511
512 fn FromMatrix(
514 cx: &mut js::context::JSContext,
515 global: &GlobalScope,
516 other: &DOMMatrixInit,
517 ) -> Fallible<DomRoot<Self>> {
518 dommatrixinit_to_matrix(other).map(|(is2D, matrix)| Self::new(cx, global, is2D, matrix))
519 }
520
521 fn FromFloat32Array(
523 cx: &mut js::context::JSContext,
524 global: &GlobalScope,
525 array: CustomAutoRooterGuard<Float32Array>,
526 ) -> Fallible<DomRoot<DOMMatrixReadOnly>> {
527 let vec: Vec<f64> = array
528 .to_vec()
529 .unwrap_or_default()
530 .iter()
531 .map(|&x| x as f64)
532 .collect();
533 DOMMatrixReadOnly::Constructor(
534 cx,
535 global,
536 None,
537 Some(StringOrUnrestrictedDoubleSequence::UnrestrictedDoubleSequence(vec)),
538 )
539 }
540
541 fn FromFloat64Array(
543 cx: &mut js::context::JSContext,
544 global: &GlobalScope,
545 array: CustomAutoRooterGuard<Float64Array>,
546 ) -> Fallible<DomRoot<DOMMatrixReadOnly>> {
547 let vec: Vec<f64> = array.to_vec().unwrap_or_default();
548 DOMMatrixReadOnly::Constructor(
549 cx,
550 global,
551 None,
552 Some(StringOrUnrestrictedDoubleSequence::UnrestrictedDoubleSequence(vec)),
553 )
554 }
555
556 fn M11(&self) -> f64 {
558 self.matrix.borrow().m11
559 }
560
561 fn M12(&self) -> f64 {
563 self.matrix.borrow().m12
564 }
565
566 fn M13(&self) -> f64 {
568 self.matrix.borrow().m13
569 }
570
571 fn M14(&self) -> f64 {
573 self.matrix.borrow().m14
574 }
575
576 fn M21(&self) -> f64 {
578 self.matrix.borrow().m21
579 }
580
581 fn M22(&self) -> f64 {
583 self.matrix.borrow().m22
584 }
585
586 fn M23(&self) -> f64 {
588 self.matrix.borrow().m23
589 }
590
591 fn M24(&self) -> f64 {
593 self.matrix.borrow().m24
594 }
595
596 fn M31(&self) -> f64 {
598 self.matrix.borrow().m31
599 }
600
601 fn M32(&self) -> f64 {
603 self.matrix.borrow().m32
604 }
605
606 fn M33(&self) -> f64 {
608 self.matrix.borrow().m33
609 }
610
611 fn M34(&self) -> f64 {
613 self.matrix.borrow().m34
614 }
615
616 fn M41(&self) -> f64 {
618 self.matrix.borrow().m41
619 }
620
621 fn M42(&self) -> f64 {
623 self.matrix.borrow().m42
624 }
625
626 fn M43(&self) -> f64 {
628 self.matrix.borrow().m43
629 }
630
631 fn M44(&self) -> f64 {
633 self.matrix.borrow().m44
634 }
635
636 fn A(&self) -> f64 {
638 self.M11()
639 }
640
641 fn B(&self) -> f64 {
643 self.M12()
644 }
645
646 fn C(&self) -> f64 {
648 self.M21()
649 }
650
651 fn D(&self) -> f64 {
653 self.M22()
654 }
655
656 fn E(&self) -> f64 {
658 self.M41()
659 }
660
661 fn F(&self) -> f64 {
663 self.M42()
664 }
665
666 fn Is2D(&self) -> bool {
668 self.is2D.get()
669 }
670
671 fn IsIdentity(&self) -> bool {
673 let matrix = self.matrix.borrow();
674 matrix.m12 == 0.0 &&
675 matrix.m13 == 0.0 &&
676 matrix.m14 == 0.0 &&
677 matrix.m21 == 0.0 &&
678 matrix.m23 == 0.0 &&
679 matrix.m24 == 0.0 &&
680 matrix.m31 == 0.0 &&
681 matrix.m32 == 0.0 &&
682 matrix.m34 == 0.0 &&
683 matrix.m41 == 0.0 &&
684 matrix.m42 == 0.0 &&
685 matrix.m43 == 0.0 &&
686 matrix.m11 == 1.0 &&
687 matrix.m22 == 1.0 &&
688 matrix.m33 == 1.0 &&
689 matrix.m44 == 1.0
690 }
691
692 fn Translate(
694 &self,
695 cx: &mut js::context::JSContext,
696 tx: f64,
697 ty: f64,
698 tz: f64,
699 ) -> DomRoot<DOMMatrix> {
700 DOMMatrix::from_readonly(&self.global(), self, cx).TranslateSelf(tx, ty, tz)
701 }
702
703 fn Scale(
705 &self,
706 cx: &mut js::context::JSContext,
707 scaleX: f64,
708 scaleY: Option<f64>,
709 scaleZ: f64,
710 originX: f64,
711 originY: f64,
712 originZ: f64,
713 ) -> DomRoot<DOMMatrix> {
714 DOMMatrix::from_readonly(&self.global(), self, cx)
715 .ScaleSelf(scaleX, scaleY, scaleZ, originX, originY, originZ)
716 }
717
718 fn ScaleNonUniform(
720 &self,
721 cx: &mut js::context::JSContext,
722 scaleX: f64,
723 scaleY: f64,
724 ) -> DomRoot<DOMMatrix> {
725 DOMMatrix::from_readonly(&self.global(), self, cx).ScaleSelf(
726 scaleX,
727 Some(scaleY),
728 1.0,
729 0.0,
730 0.0,
731 0.0,
732 )
733 }
734
735 fn Scale3d(
737 &self,
738 cx: &mut js::context::JSContext,
739 scale: f64,
740 originX: f64,
741 originY: f64,
742 originZ: f64,
743 ) -> DomRoot<DOMMatrix> {
744 DOMMatrix::from_readonly(&self.global(), self, cx)
745 .Scale3dSelf(scale, originX, originY, originZ)
746 }
747
748 fn Rotate(
750 &self,
751 cx: &mut js::context::JSContext,
752 rotX: f64,
753 rotY: Option<f64>,
754 rotZ: Option<f64>,
755 ) -> DomRoot<DOMMatrix> {
756 DOMMatrix::from_readonly(&self.global(), self, cx).RotateSelf(rotX, rotY, rotZ)
757 }
758
759 fn RotateFromVector(
761 &self,
762 cx: &mut js::context::JSContext,
763 x: f64,
764 y: f64,
765 ) -> DomRoot<DOMMatrix> {
766 DOMMatrix::from_readonly(&self.global(), self, cx).RotateFromVectorSelf(x, y)
767 }
768
769 fn RotateAxisAngle(
771 &self,
772 cx: &mut js::context::JSContext,
773 x: f64,
774 y: f64,
775 z: f64,
776 angle: f64,
777 ) -> DomRoot<DOMMatrix> {
778 DOMMatrix::from_readonly(&self.global(), self, cx).RotateAxisAngleSelf(x, y, z, angle)
779 }
780
781 fn SkewX(&self, cx: &mut js::context::JSContext, sx: f64) -> DomRoot<DOMMatrix> {
783 DOMMatrix::from_readonly(&self.global(), self, cx).SkewXSelf(sx)
784 }
785
786 fn SkewY(&self, cx: &mut js::context::JSContext, sy: f64) -> DomRoot<DOMMatrix> {
788 DOMMatrix::from_readonly(&self.global(), self, cx).SkewYSelf(sy)
789 }
790
791 fn Multiply(
793 &self,
794 cx: &mut js::context::JSContext,
795 other: &DOMMatrixInit,
796 ) -> Fallible<DomRoot<DOMMatrix>> {
797 DOMMatrix::from_readonly(&self.global(), self, cx).MultiplySelf(other)
798 }
799
800 fn FlipX(&self, cx: &mut js::context::JSContext) -> DomRoot<DOMMatrix> {
802 let is2D = self.is2D.get();
803 let flip = Transform3D::new(
804 -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,
805 );
806 let matrix = flip.then(&self.matrix.borrow());
807 DOMMatrix::new(cx, &self.global(), is2D, matrix)
808 }
809
810 fn FlipY(&self, cx: &mut js::context::JSContext) -> DomRoot<DOMMatrix> {
812 let is2D = self.is2D.get();
813 let flip = Transform3D::new(
814 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,
815 );
816 let matrix = flip.then(&self.matrix.borrow());
817 DOMMatrix::new(cx, &self.global(), is2D, matrix)
818 }
819
820 fn Inverse(&self, cx: &mut js::context::JSContext) -> DomRoot<DOMMatrix> {
822 DOMMatrix::from_readonly(&self.global(), self, cx).InvertSelf()
823 }
824
825 fn TransformPoint(
827 &self,
828 cx: &mut js::context::JSContext,
829 point: &DOMPointInit,
830 ) -> DomRoot<DOMPoint> {
831 let mat = self.matrix.borrow();
838 let x = point.x * mat.m11 + point.y * mat.m21 + point.z * mat.m31 + point.w * mat.m41;
839 let y = point.x * mat.m12 + point.y * mat.m22 + point.z * mat.m32 + point.w * mat.m42;
840 let z = point.x * mat.m13 + point.y * mat.m23 + point.z * mat.m33 + point.w * mat.m43;
841 let w = point.x * mat.m14 + point.y * mat.m24 + point.z * mat.m34 + point.w * mat.m44;
842
843 DOMPoint::new(cx, &self.global(), x, y, z, w)
844 }
845
846 fn ToFloat32Array(
848 &self,
849 cx: &mut js::context::JSContext,
850 ) -> RootedTraceableBox<HeapFloat32Array> {
851 let vec: Vec<f32> = self
852 .matrix
853 .borrow()
854 .to_array()
855 .iter()
856 .map(|&x| x as f32)
857 .collect();
858 rooted!(&in(cx) let mut array = ptr::null_mut::<JSObject>());
859 create_buffer_source(cx, &vec, array.handle_mut())
860 .expect("Converting matrix to float32 array should never fail")
861 }
862
863 fn ToFloat64Array(
865 &self,
866 cx: &mut js::context::JSContext,
867 ) -> RootedTraceableBox<HeapFloat64Array> {
868 rooted!(&in(cx) let mut array = ptr::null_mut::<JSObject>());
869 create_buffer_source(cx, &self.matrix.borrow().to_array(), array.handle_mut())
870 .expect("Converting matrix to float64 array should never fail")
871 }
872
873 fn Stringifier(&self, cx: &mut js::context::JSContext) -> Fallible<DOMString> {
875 let mat = self.matrix.borrow();
878 if !mat.m11.is_finite() ||
879 !mat.m12.is_finite() ||
880 !mat.m13.is_finite() ||
881 !mat.m14.is_finite() ||
882 !mat.m21.is_finite() ||
883 !mat.m22.is_finite() ||
884 !mat.m23.is_finite() ||
885 !mat.m24.is_finite() ||
886 !mat.m31.is_finite() ||
887 !mat.m32.is_finite() ||
888 !mat.m33.is_finite() ||
889 !mat.m34.is_finite() ||
890 !mat.m41.is_finite() ||
891 !mat.m42.is_finite() ||
892 !mat.m43.is_finite() ||
893 !mat.m44.is_finite()
894 {
895 return Err(error::Error::InvalidState(None));
896 }
897
898 let mut to_string = |f: f64| {
899 rooted!(&in(cx) let rooted_value = jsval::DoubleValue(f));
900 DOMString::from_js_string(cx, rooted_value.handle())
901 .unwrap_or_else(|_| panic!("Pointer cannot be null"))
902 };
903
904 let string = if self.is2D() {
907 format!(
921 "matrix({}, {}, {}, {}, {}, {})",
922 to_string(mat.m11),
923 to_string(mat.m12),
924 to_string(mat.m21),
925 to_string(mat.m22),
926 to_string(mat.m41),
927 to_string(mat.m42)
928 )
929 .into()
930 }
931 else {
933 format!(
962 "matrix3d({}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {})",
963 to_string(mat.m11),
964 to_string(mat.m12),
965 to_string(mat.m13),
966 to_string(mat.m14),
967 to_string(mat.m21),
968 to_string(mat.m22),
969 to_string(mat.m23),
970 to_string(mat.m24),
971 to_string(mat.m31),
972 to_string(mat.m32),
973 to_string(mat.m33),
974 to_string(mat.m34),
975 to_string(mat.m41),
976 to_string(mat.m42),
977 to_string(mat.m43),
978 to_string(mat.m44)
979 )
980 .into()
981 };
982
983 Ok(string)
984 }
985}
986
987impl Serializable for DOMMatrixReadOnly {
988 type Index = DomMatrixIndex;
989 type Data = DomMatrix;
990
991 fn serialize(&self, _no_gc: &NoGC) -> Result<(DomMatrixId, Self::Data), ()> {
992 let serialized = if self.is2D() {
993 DomMatrix {
994 matrix: Transform3D::new(
995 self.M11(),
996 self.M12(),
997 f64::NAN,
998 f64::NAN,
999 self.M21(),
1000 self.M22(),
1001 f64::NAN,
1002 f64::NAN,
1003 f64::NAN,
1004 f64::NAN,
1005 f64::NAN,
1006 f64::NAN,
1007 self.M41(),
1008 self.M42(),
1009 f64::NAN,
1010 f64::NAN,
1011 ),
1012 is_2d: true,
1013 }
1014 } else {
1015 DomMatrix {
1016 matrix: *self.matrix(),
1017 is_2d: false,
1018 }
1019 };
1020 Ok((DomMatrixId::new(), serialized))
1021 }
1022
1023 fn deserialize(
1024 cx: &mut js::context::JSContext,
1025 owner: &GlobalScope,
1026 serialized: Self::Data,
1027 ) -> Result<DomRoot<Self>, ()>
1028 where
1029 Self: Sized,
1030 {
1031 if serialized.is_2d {
1032 Ok(Self::new(
1033 cx,
1034 owner,
1035 true,
1036 Transform3D::new(
1037 serialized.matrix.m11,
1038 serialized.matrix.m12,
1039 0.0,
1040 0.0,
1041 serialized.matrix.m21,
1042 serialized.matrix.m22,
1043 0.0,
1044 0.0,
1045 0.0,
1046 0.0,
1047 1.0,
1048 0.0,
1049 serialized.matrix.m41,
1050 serialized.matrix.m42,
1051 0.0,
1052 1.0,
1053 ),
1054 ))
1055 } else {
1056 Ok(Self::new(cx, owner, false, serialized.matrix))
1057 }
1058 }
1059
1060 fn serialized_storage<'a>(
1061 data: StructuredData<'a, '_>,
1062 ) -> &'a mut Option<FxHashMap<DomMatrixId, Self::Data>> {
1063 match data {
1064 StructuredData::Reader(reader) => &mut reader.matrices,
1065 StructuredData::Writer(writer) => &mut writer.matrices,
1066 }
1067 }
1068}
1069
1070pub(crate) fn entries_to_matrix(entries: &[f64]) -> Fallible<(bool, Transform3D<f64>)> {
1072 if let Ok(array) = entries.try_into() {
1073 Ok((true, Transform2D::from_array(array).to_3d()))
1074 } else if let Ok(array) = entries.try_into() {
1075 Ok((false, Transform3D::from_array(array)))
1076 } else {
1077 let err_msg = cformat!("Expected 6 or 16 entries, but found {}.", entries.len());
1078 Err(error::Error::Type(err_msg))
1079 }
1080}
1081
1082fn validate_and_fixup_2d(dict: &DOMMatrix2DInit) -> Fallible<Transform2D<f64>> {
1084 let same_value_zero = |x: f64, y: f64| -> bool { x.is_nan() && y.is_nan() || x == y };
1086
1087 if dict.a.is_some() &&
1090 dict.m11.is_some() &&
1091 !same_value_zero(dict.a.unwrap(), dict.m11.unwrap()) ||
1092 dict.b.is_some() &&
1093 dict.m12.is_some() &&
1094 !same_value_zero(dict.b.unwrap(), dict.m12.unwrap()) ||
1095 dict.c.is_some() &&
1096 dict.m21.is_some() &&
1097 !same_value_zero(dict.c.unwrap(), dict.m21.unwrap()) ||
1098 dict.d.is_some() &&
1099 dict.m22.is_some() &&
1100 !same_value_zero(dict.d.unwrap(), dict.m22.unwrap()) ||
1101 dict.e.is_some() &&
1102 dict.m41.is_some() &&
1103 !same_value_zero(dict.e.unwrap(), dict.m41.unwrap()) ||
1104 dict.f.is_some() &&
1105 dict.m42.is_some() &&
1106 !same_value_zero(dict.f.unwrap(), dict.m42.unwrap())
1107 {
1108 return Err(error::Error::Type(
1109 c"Property mismatch on matrix initialization.".to_owned(),
1110 ));
1111 }
1112
1113 let m11 = dict.m11.unwrap_or(dict.a.unwrap_or(1.0));
1116
1117 let m12 = dict.m12.unwrap_or(dict.b.unwrap_or(0.0));
1120
1121 let m21 = dict.m21.unwrap_or(dict.c.unwrap_or(0.0));
1124
1125 let m22 = dict.m22.unwrap_or(dict.d.unwrap_or(1.0));
1128
1129 let m41 = dict.m41.unwrap_or(dict.e.unwrap_or(0.0));
1132
1133 let m42 = dict.m42.unwrap_or(dict.f.unwrap_or(0.0));
1136
1137 Ok(Transform2D::new(m11, m12, m21, m22, m41, m42))
1138}
1139
1140fn validate_and_fixup(dict: &DOMMatrixInit) -> Fallible<(bool, Transform3D<f64>)> {
1142 let transform2d = validate_and_fixup_2d(&dict.parent)?;
1144
1145 if dict.is2D == Some(true) &&
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 return Err(error::Error::Type(
1162 c"The is2D member is set to true but the input matrix is a 3d matrix.".to_owned(),
1163 ));
1164 }
1165
1166 let mut is_2d = dict.is2D;
1167
1168 if is_2d.is_none() &&
1173 (dict.m13 != 0.0 ||
1174 dict.m14 != 0.0 ||
1175 dict.m23 != 0.0 ||
1176 dict.m24 != 0.0 ||
1177 dict.m31 != 0.0 ||
1178 dict.m32 != 0.0 ||
1179 dict.m34 != 0.0 ||
1180 dict.m43 != 0.0 ||
1181 dict.m33 != 1.0 ||
1182 dict.m44 != 1.0)
1183 {
1184 is_2d = Some(false);
1185 }
1186
1187 let is_2d = is_2d.unwrap_or(true);
1189
1190 let mut transform = transform2d.to_3d();
1191 transform.m13 = dict.m13;
1192 transform.m14 = dict.m14;
1193 transform.m23 = dict.m23;
1194 transform.m24 = dict.m24;
1195 transform.m31 = dict.m31;
1196 transform.m32 = dict.m32;
1197 transform.m33 = dict.m33;
1198 transform.m34 = dict.m34;
1199 transform.m43 = dict.m43;
1200 transform.m44 = dict.m44;
1201
1202 Ok((is_2d, transform))
1203}
1204
1205pub(crate) fn dommatrix2dinit_to_matrix(dict: &DOMMatrix2DInit) -> Fallible<Transform2D<f64>> {
1207 validate_and_fixup_2d(dict)
1213}
1214
1215pub(crate) fn dommatrixinit_to_matrix(dict: &DOMMatrixInit) -> Fallible<(bool, Transform3D<f64>)> {
1217 validate_and_fixup(dict)
1223}
1224
1225#[inline]
1226fn normalize_point(x: f64, y: f64, z: f64) -> (f64, f64, f64) {
1227 let len = (x * x + y * y + z * z).sqrt();
1228 if len == 0.0 {
1229 (0.0, 0.0, 0.0)
1230 } else {
1231 (x / len, y / len, z / len)
1232 }
1233}
1234
1235pub(crate) fn transform_to_matrix(value: &str) -> Fallible<(bool, Transform3D<f64>)> {
1236 use style::properties::longhands::transform;
1237
1238 let mut parser = Parser::new(value);
1239 let context = parser_context_for_anonymous_content(
1240 CssRuleType::Style,
1241 ParsingMode::DEFAULT,
1242 &ANONYMOUS_CONTENT_URL_DATA,
1243 );
1244
1245 let transform = match parser.parse_entirely(|t| transform::parse(&context, t)) {
1246 Ok(result) => result,
1247 Err(..) => return Err(error::Error::Syntax(None)),
1248 };
1249
1250 let (m, is_3d) = match transform.to_transform_3d_matrix_f64(None) {
1251 Ok(result) => result,
1252 Err(..) => return Err(error::Error::Syntax(None)),
1253 };
1254
1255 Ok((!is_3d, m))
1256}