1use 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 pub(crate) fn set_m11(&self, value: f64) {
123 self.matrix.borrow_mut().m11 = value;
124 }
125
126 pub(crate) fn set_m12(&self, value: f64) {
128 self.matrix.borrow_mut().m12 = value;
129 }
130
131 pub(crate) fn set_m13(&self, value: f64) {
133 self.matrix.borrow_mut().m13 = value;
137 if value.abs() != 0. {
138 self.is2D.set(false);
139 }
140 }
141
142 pub(crate) fn set_m14(&self, value: f64) {
144 self.matrix.borrow_mut().m14 = value;
147
148 if value.abs() != 0. {
149 self.is2D.set(false);
150 }
151 }
152
153 pub(crate) fn set_m21(&self, value: f64) {
155 self.matrix.borrow_mut().m21 = value;
156 }
157
158 pub(crate) fn set_m22(&self, value: f64) {
160 self.matrix.borrow_mut().m22 = value;
161 }
162
163 pub(crate) fn set_m23(&self, value: f64) {
165 self.matrix.borrow_mut().m23 = value;
168
169 if value.abs() != 0. {
170 self.is2D.set(false);
171 }
172 }
173
174 pub(crate) fn set_m24(&self, value: f64) {
176 self.matrix.borrow_mut().m24 = value;
179
180 if value.abs() != 0. {
181 self.is2D.set(false);
182 }
183 }
184
185 pub(crate) fn set_m31(&self, value: f64) {
187 self.matrix.borrow_mut().m31 = value;
190
191 if value.abs() != 0. {
192 self.is2D.set(false);
193 }
194 }
195
196 pub(crate) fn set_m32(&self, value: f64) {
198 self.matrix.borrow_mut().m32 = value;
201
202 if value.abs() != 0. {
203 self.is2D.set(false);
204 }
205 }
206
207 pub(crate) fn set_m33(&self, value: f64) {
209 self.matrix.borrow_mut().m33 = value;
212
213 if value != 1. {
214 self.is2D.set(false);
215 }
216 }
217
218 pub(crate) fn set_m34(&self, value: f64) {
220 self.matrix.borrow_mut().m34 = value;
223
224 if value.abs() != 0. {
225 self.is2D.set(false);
226 }
227 }
228
229 pub(crate) fn set_m41(&self, value: f64) {
231 self.matrix.borrow_mut().m41 = value;
232 }
233
234 pub(crate) fn set_m42(&self, value: f64) {
236 self.matrix.borrow_mut().m42 = value;
237 }
238
239 pub(crate) fn set_m43(&self, value: f64) {
241 self.matrix.borrow_mut().m43 = value;
244
245 if value.abs() != 0. {
246 self.is2D.set(false);
247 }
248 }
249
250 pub(crate) fn set_m44(&self, value: f64) {
252 self.matrix.borrow_mut().m44 = value;
255
256 if value != 1. {
257 self.is2D.set(false);
258 }
259 }
260
261 pub(crate) fn multiply_self(&self, other: &DOMMatrixInit) -> Fallible<()> {
263 dommatrixinit_to_matrix(other).map(|(is2D, other_matrix)| {
265 let mut matrix = self.matrix.borrow_mut();
267 *matrix = other_matrix.then(&matrix);
268 if !is2D {
270 self.is2D.set(false);
271 }
272 })
274 }
275
276 pub(crate) fn pre_multiply_self(&self, other: &DOMMatrixInit) -> Fallible<()> {
278 dommatrixinit_to_matrix(other).map(|(is2D, other_matrix)| {
280 let mut matrix = self.matrix.borrow_mut();
282 *matrix = matrix.then(&other_matrix);
283 if !is2D {
285 self.is2D.set(false);
286 }
287 })
289 }
290
291 pub(crate) fn translate_self(&self, tx: f64, ty: f64, tz: f64) {
293 let translation = Transform3D::translation(tx, ty, tz);
295 let mut matrix = self.matrix.borrow_mut();
296 *matrix = translation.then(&matrix);
297 if tz != 0.0 {
299 self.is2D.set(false);
300 }
301 }
303
304 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 self.translate_self(originX, originY, originZ);
316 let scaleY = scaleY.unwrap_or(scaleX);
318 {
320 let scale3D = Transform3D::scale(scaleX, scaleY, scaleZ);
321 let mut matrix = self.matrix.borrow_mut();
322 *matrix = scale3D.then(&matrix);
323 }
324 originX = -originX;
326 originY = -originY;
327 originZ = -originZ;
328 self.translate_self(originX, originY, originZ);
330 if scaleZ != 1.0 || originZ != 0.0 {
332 self.is2D.set(false);
333 }
334 }
336
337 pub(crate) fn scale_3d_self(&self, scale: f64, originX: f64, originY: f64, originZ: f64) {
339 self.translate_self(originX, originY, originZ);
341 {
343 let scale3D = Transform3D::scale(scale, scale, scale);
344 let mut matrix = self.matrix.borrow_mut();
345 *matrix = scale3D.then(&matrix);
346 }
347 self.translate_self(-originX, -originY, -originZ);
349 if scale != 1.0 {
351 self.is2D.set(false);
352 }
353 }
355
356 pub(crate) fn rotate_self(&self, mut rotX: f64, mut rotY: Option<f64>, mut rotZ: Option<f64>) {
358 if rotY.is_none() && rotZ.is_none() {
360 rotZ = Some(rotX);
361 rotX = 0.0;
362 rotY = Some(0.0);
363 }
364 let rotY = rotY.unwrap_or(0.0);
366 let rotZ = rotZ.unwrap_or(0.0);
368 if rotX != 0.0 || rotY != 0.0 {
370 self.is2D.set(false);
371 }
372 if rotZ != 0.0 {
373 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 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 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 }
392
393 pub(crate) fn rotate_from_vector_self(&self, x: f64, y: f64) {
395 if y != 0.0 || x < 0.0 {
397 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 }
405
406 pub(crate) fn rotate_axis_angle_self(&self, x: f64, y: f64, z: f64, angle: f64) {
408 let (norm_x, norm_y, norm_z) = normalize_point(x, y, z);
410 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 if x != 0.0 || y != 0.0 {
417 self.is2D.set(false);
418 }
419 }
421
422 pub(crate) fn skew_x_self(&self, sx: f64) {
424 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 }
430
431 pub(crate) fn skew_y_self(&self, sy: f64) {
433 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 }
439
440 pub(crate) fn invert_self(&self) {
442 let mut matrix = self.matrix.borrow_mut();
443 let inverted = match self.is2D() {
445 true => matrix.to_2d().inverse().map(|m| m.to_3d()),
446 false => matrix.inverse(),
447 };
448
449 *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 }
474}
475
476#[expect(non_snake_case)]
477impl DOMMatrixReadOnlyMethods<crate::DomTypeHolder> for DOMMatrixReadOnly {
478 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 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 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 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 fn M11(&self) -> f64 {
555 self.matrix.borrow().m11
556 }
557
558 fn M12(&self) -> f64 {
560 self.matrix.borrow().m12
561 }
562
563 fn M13(&self) -> f64 {
565 self.matrix.borrow().m13
566 }
567
568 fn M14(&self) -> f64 {
570 self.matrix.borrow().m14
571 }
572
573 fn M21(&self) -> f64 {
575 self.matrix.borrow().m21
576 }
577
578 fn M22(&self) -> f64 {
580 self.matrix.borrow().m22
581 }
582
583 fn M23(&self) -> f64 {
585 self.matrix.borrow().m23
586 }
587
588 fn M24(&self) -> f64 {
590 self.matrix.borrow().m24
591 }
592
593 fn M31(&self) -> f64 {
595 self.matrix.borrow().m31
596 }
597
598 fn M32(&self) -> f64 {
600 self.matrix.borrow().m32
601 }
602
603 fn M33(&self) -> f64 {
605 self.matrix.borrow().m33
606 }
607
608 fn M34(&self) -> f64 {
610 self.matrix.borrow().m34
611 }
612
613 fn M41(&self) -> f64 {
615 self.matrix.borrow().m41
616 }
617
618 fn M42(&self) -> f64 {
620 self.matrix.borrow().m42
621 }
622
623 fn M43(&self) -> f64 {
625 self.matrix.borrow().m43
626 }
627
628 fn M44(&self) -> f64 {
630 self.matrix.borrow().m44
631 }
632
633 fn A(&self) -> f64 {
635 self.M11()
636 }
637
638 fn B(&self) -> f64 {
640 self.M12()
641 }
642
643 fn C(&self) -> f64 {
645 self.M21()
646 }
647
648 fn D(&self) -> f64 {
650 self.M22()
651 }
652
653 fn E(&self) -> f64 {
655 self.M41()
656 }
657
658 fn F(&self) -> f64 {
660 self.M42()
661 }
662
663 fn Is2D(&self) -> bool {
665 self.is2D.get()
666 }
667
668 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 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 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 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 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 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 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 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 fn SkewX(&self, sx: f64, can_gc: CanGc) -> DomRoot<DOMMatrix> {
764 DOMMatrix::from_readonly(&self.global(), self, can_gc).SkewXSelf(sx)
765 }
766
767 fn SkewY(&self, sy: f64, can_gc: CanGc) -> DomRoot<DOMMatrix> {
769 DOMMatrix::from_readonly(&self.global(), self, can_gc).SkewYSelf(sy)
770 }
771
772 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 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 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 fn Inverse(&self, can_gc: CanGc) -> DomRoot<DOMMatrix> {
799 DOMMatrix::from_readonly(&self.global(), self, can_gc).InvertSelf()
800 }
801
802 fn TransformPoint(
804 &self,
805 cx: &mut js::context::JSContext,
806 point: &DOMPointInit,
807 ) -> DomRoot<DOMPoint> {
808 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 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 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 #[expect(unsafe_code)]
851 fn Stringifier(&self) -> Fallible<DOMString> {
852 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 let string = if self.is2D() {
890 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 else {
916 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
1053pub(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
1065fn validate_and_fixup_2d(dict: &DOMMatrix2DInit) -> Fallible<Transform2D<f64>> {
1067 let same_value_zero = |x: f64, y: f64| -> bool { x.is_nan() && y.is_nan() || x == y };
1069
1070 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 let m11 = dict.m11.unwrap_or(dict.a.unwrap_or(1.0));
1099
1100 let m12 = dict.m12.unwrap_or(dict.b.unwrap_or(0.0));
1103
1104 let m21 = dict.m21.unwrap_or(dict.c.unwrap_or(0.0));
1107
1108 let m22 = dict.m22.unwrap_or(dict.d.unwrap_or(1.0));
1111
1112 let m41 = dict.m41.unwrap_or(dict.e.unwrap_or(0.0));
1115
1116 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
1123fn validate_and_fixup(dict: &DOMMatrixInit) -> Fallible<(bool, Transform3D<f64>)> {
1125 let transform2d = validate_and_fixup_2d(&dict.parent)?;
1127
1128 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 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 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
1188pub(crate) fn dommatrix2dinit_to_matrix(dict: &DOMMatrix2DInit) -> Fallible<Transform2D<f64>> {
1190 validate_and_fixup_2d(dict)
1196}
1197
1198pub(crate) fn dommatrixinit_to_matrix(dict: &DOMMatrixInit) -> Fallible<(bool, Transform3D<f64>)> {
1200 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}