1use std::cell::Cell;
6use std::collections::HashMap;
7use std::{f64, ptr};
8
9use base::id::{DomMatrixId, DomMatrixIndex};
10use constellation_traits::DomMatrix;
11use cssparser::{Parser, ParserInput};
12use dom_struct::dom_struct;
13use euclid::Angle;
14use euclid::default::{Transform2D, Transform3D};
15use js::conversions::jsstr_to_string;
16use js::jsapi::JSObject;
17use js::jsval;
18use js::rust::{CustomAutoRooterGuard, HandleObject, ToString};
19use js::typedarray::{Float32Array, Float64Array};
20use style::parser::ParserContext;
21use url::Url;
22
23use crate::dom::bindings::buffer_source::create_buffer_source;
24use crate::dom::bindings::cell::{DomRefCell, Ref};
25use crate::dom::bindings::codegen::Bindings::DOMMatrixBinding::{
26 DOMMatrix2DInit, DOMMatrixInit, DOMMatrixMethods,
27};
28use crate::dom::bindings::codegen::Bindings::DOMMatrixReadOnlyBinding::DOMMatrixReadOnlyMethods;
29use crate::dom::bindings::codegen::Bindings::DOMPointBinding::DOMPointInit;
30use crate::dom::bindings::codegen::UnionTypes::StringOrUnrestrictedDoubleSequence;
31use crate::dom::bindings::error;
32use crate::dom::bindings::error::Fallible;
33use crate::dom::bindings::inheritance::Castable;
34use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object_with_proto};
35use crate::dom::bindings::root::DomRoot;
36use crate::dom::bindings::serializable::Serializable;
37use crate::dom::bindings::str::DOMString;
38use crate::dom::bindings::structuredclone::StructuredData;
39use crate::dom::dommatrix::DOMMatrix;
40use crate::dom::dompoint::DOMPoint;
41use crate::dom::globalscope::GlobalScope;
42use crate::dom::window::Window;
43use crate::script_runtime::{CanGc, JSContext};
44
45#[dom_struct]
46#[allow(non_snake_case)]
47pub(crate) struct DOMMatrixReadOnly {
48 reflector_: Reflector,
49 #[no_trace]
50 matrix: DomRefCell<Transform3D<f64>>,
51 is2D: Cell<bool>,
52}
53
54#[allow(non_snake_case)]
55impl DOMMatrixReadOnly {
56 pub(crate) fn new(
57 global: &GlobalScope,
58 is2D: bool,
59 matrix: Transform3D<f64>,
60 can_gc: CanGc,
61 ) -> DomRoot<Self> {
62 Self::new_with_proto(global, None, is2D, matrix, can_gc)
63 }
64
65 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
66 fn new_with_proto(
67 global: &GlobalScope,
68 proto: Option<HandleObject>,
69 is2D: bool,
70 matrix: Transform3D<f64>,
71 can_gc: CanGc,
72 ) -> DomRoot<Self> {
73 let dommatrix = Self::new_inherited(is2D, matrix);
74 reflect_dom_object_with_proto(Box::new(dommatrix), global, proto, can_gc)
75 }
76
77 pub(crate) fn new_inherited(is2D: bool, matrix: Transform3D<f64>) -> Self {
78 DOMMatrixReadOnly {
79 reflector_: Reflector::new(),
80 matrix: DomRefCell::new(matrix),
81 is2D: Cell::new(is2D),
82 }
83 }
84
85 pub(crate) fn matrix(&self) -> Ref<'_, Transform3D<f64>> {
86 self.matrix.borrow()
87 }
88
89 pub(crate) fn is2D(&self) -> bool {
90 self.is2D.get()
91 }
92
93 pub(crate) fn set_m11(&self, value: f64) {
95 self.matrix.borrow_mut().m11 = value;
96 }
97
98 pub(crate) fn set_m12(&self, value: f64) {
100 self.matrix.borrow_mut().m12 = value;
101 }
102
103 pub(crate) fn set_m13(&self, value: f64) {
105 self.matrix.borrow_mut().m13 = value;
109 if value.abs() != 0. {
110 self.is2D.set(false);
111 }
112 }
113
114 pub(crate) fn set_m14(&self, value: f64) {
116 self.matrix.borrow_mut().m14 = value;
119
120 if value.abs() != 0. {
121 self.is2D.set(false);
122 }
123 }
124
125 pub(crate) fn set_m21(&self, value: f64) {
127 self.matrix.borrow_mut().m21 = value;
128 }
129
130 pub(crate) fn set_m22(&self, value: f64) {
132 self.matrix.borrow_mut().m22 = value;
133 }
134
135 pub(crate) fn set_m23(&self, value: f64) {
137 self.matrix.borrow_mut().m23 = value;
140
141 if value.abs() != 0. {
142 self.is2D.set(false);
143 }
144 }
145
146 pub(crate) fn set_m24(&self, value: f64) {
148 self.matrix.borrow_mut().m24 = value;
151
152 if value.abs() != 0. {
153 self.is2D.set(false);
154 }
155 }
156
157 pub(crate) fn set_m31(&self, value: f64) {
159 self.matrix.borrow_mut().m31 = value;
162
163 if value.abs() != 0. {
164 self.is2D.set(false);
165 }
166 }
167
168 pub(crate) fn set_m32(&self, value: f64) {
170 self.matrix.borrow_mut().m32 = value;
173
174 if value.abs() != 0. {
175 self.is2D.set(false);
176 }
177 }
178
179 pub(crate) fn set_m33(&self, value: f64) {
181 self.matrix.borrow_mut().m33 = value;
184
185 if value != 1. {
186 self.is2D.set(false);
187 }
188 }
189
190 pub(crate) fn set_m34(&self, value: f64) {
192 self.matrix.borrow_mut().m34 = value;
195
196 if value.abs() != 0. {
197 self.is2D.set(false);
198 }
199 }
200
201 pub(crate) fn set_m41(&self, value: f64) {
203 self.matrix.borrow_mut().m41 = value;
204 }
205
206 pub(crate) fn set_m42(&self, value: f64) {
208 self.matrix.borrow_mut().m42 = value;
209 }
210
211 pub(crate) fn set_m43(&self, value: f64) {
213 self.matrix.borrow_mut().m43 = value;
216
217 if value.abs() != 0. {
218 self.is2D.set(false);
219 }
220 }
221
222 pub(crate) fn set_m44(&self, value: f64) {
224 self.matrix.borrow_mut().m44 = value;
227
228 if value != 1. {
229 self.is2D.set(false);
230 }
231 }
232
233 pub(crate) fn multiply_self(&self, other: &DOMMatrixInit) -> Fallible<()> {
235 dommatrixinit_to_matrix(other).map(|(is2D, other_matrix)| {
237 let mut matrix = self.matrix.borrow_mut();
239 *matrix = other_matrix.then(&matrix);
240 if !is2D {
242 self.is2D.set(false);
243 }
244 })
246 }
247
248 pub(crate) fn pre_multiply_self(&self, other: &DOMMatrixInit) -> Fallible<()> {
250 dommatrixinit_to_matrix(other).map(|(is2D, other_matrix)| {
252 let mut matrix = self.matrix.borrow_mut();
254 *matrix = matrix.then(&other_matrix);
255 if !is2D {
257 self.is2D.set(false);
258 }
259 })
261 }
262
263 pub(crate) fn translate_self(&self, tx: f64, ty: f64, tz: f64) {
265 let translation = Transform3D::translation(tx, ty, tz);
267 let mut matrix = self.matrix.borrow_mut();
268 *matrix = translation.then(&matrix);
269 if tz != 0.0 {
271 self.is2D.set(false);
272 }
273 }
275
276 pub(crate) fn scale_self(
278 &self,
279 scaleX: f64,
280 scaleY: Option<f64>,
281 scaleZ: f64,
282 mut originX: f64,
283 mut originY: f64,
284 mut originZ: f64,
285 ) {
286 self.translate_self(originX, originY, originZ);
288 let scaleY = scaleY.unwrap_or(scaleX);
290 {
292 let scale3D = Transform3D::scale(scaleX, scaleY, scaleZ);
293 let mut matrix = self.matrix.borrow_mut();
294 *matrix = scale3D.then(&matrix);
295 }
296 originX = -originX;
298 originY = -originY;
299 originZ = -originZ;
300 self.translate_self(originX, originY, originZ);
302 if scaleZ != 1.0 || originZ != 0.0 {
304 self.is2D.set(false);
305 }
306 }
308
309 pub(crate) fn scale_3d_self(&self, scale: f64, originX: f64, originY: f64, originZ: f64) {
311 self.translate_self(originX, originY, originZ);
313 {
315 let scale3D = Transform3D::scale(scale, scale, scale);
316 let mut matrix = self.matrix.borrow_mut();
317 *matrix = scale3D.then(&matrix);
318 }
319 self.translate_self(-originX, -originY, -originZ);
321 if scale != 1.0 {
323 self.is2D.set(false);
324 }
325 }
327
328 pub(crate) fn rotate_self(&self, mut rotX: f64, mut rotY: Option<f64>, mut rotZ: Option<f64>) {
330 if rotY.is_none() && rotZ.is_none() {
332 rotZ = Some(rotX);
333 rotX = 0.0;
334 rotY = Some(0.0);
335 }
336 let rotY = rotY.unwrap_or(0.0);
338 let rotZ = rotZ.unwrap_or(0.0);
340 if rotX != 0.0 || rotY != 0.0 {
342 self.is2D.set(false);
343 }
344 if rotZ != 0.0 {
345 let rotation = Transform3D::rotation(0.0, 0.0, 1.0, Angle::radians(rotZ.to_radians()));
347 let mut matrix = self.matrix.borrow_mut();
348 *matrix = rotation.then(&matrix);
349 }
350 if rotY != 0.0 {
351 let rotation = Transform3D::rotation(0.0, 1.0, 0.0, Angle::radians(rotY.to_radians()));
353 let mut matrix = self.matrix.borrow_mut();
354 *matrix = rotation.then(&matrix);
355 }
356 if rotX != 0.0 {
357 let rotation = Transform3D::rotation(1.0, 0.0, 0.0, Angle::radians(rotX.to_radians()));
359 let mut matrix = self.matrix.borrow_mut();
360 *matrix = rotation.then(&matrix);
361 }
362 }
364
365 pub(crate) fn rotate_from_vector_self(&self, x: f64, y: f64) {
367 if y != 0.0 || x < 0.0 {
369 let rotZ = Angle::radians(f64::atan2(y, x));
371 let rotation = Transform3D::rotation(0.0, 0.0, 1.0, rotZ);
372 let mut matrix = self.matrix.borrow_mut();
373 *matrix = rotation.then(&matrix);
374 }
375 }
377
378 pub(crate) fn rotate_axis_angle_self(&self, x: f64, y: f64, z: f64, angle: f64) {
380 let (norm_x, norm_y, norm_z) = normalize_point(x, y, z);
382 let rotation =
384 Transform3D::rotation(norm_x, norm_y, norm_z, Angle::radians(angle.to_radians()));
385 let mut matrix = self.matrix.borrow_mut();
386 *matrix = rotation.then(&matrix);
387 if x != 0.0 || y != 0.0 {
389 self.is2D.set(false);
390 }
391 }
393
394 pub(crate) fn skew_x_self(&self, sx: f64) {
396 let skew = Transform3D::skew(Angle::radians(sx.to_radians()), Angle::radians(0.0));
398 let mut matrix = self.matrix.borrow_mut();
399 *matrix = skew.then(&matrix);
400 }
402
403 pub(crate) fn skew_y_self(&self, sy: f64) {
405 let skew = Transform3D::skew(Angle::radians(0.0), Angle::radians(sy.to_radians()));
407 let mut matrix = self.matrix.borrow_mut();
408 *matrix = skew.then(&matrix);
409 }
411
412 pub(crate) fn invert_self(&self) {
414 let mut matrix = self.matrix.borrow_mut();
415 *matrix = matrix.inverse().unwrap_or_else(|| {
417 self.is2D.set(false);
419 Transform3D::new(
420 f64::NAN,
421 f64::NAN,
422 f64::NAN,
423 f64::NAN,
424 f64::NAN,
425 f64::NAN,
426 f64::NAN,
427 f64::NAN,
428 f64::NAN,
429 f64::NAN,
430 f64::NAN,
431 f64::NAN,
432 f64::NAN,
433 f64::NAN,
434 f64::NAN,
435 f64::NAN,
436 )
437 })
438 }
440}
441
442#[allow(non_snake_case)]
443impl DOMMatrixReadOnlyMethods<crate::DomTypeHolder> for DOMMatrixReadOnly {
444 fn Constructor(
446 global: &GlobalScope,
447 proto: Option<HandleObject>,
448 can_gc: CanGc,
449 init: Option<StringOrUnrestrictedDoubleSequence>,
450 ) -> Fallible<DomRoot<Self>> {
451 if init.is_none() {
452 return Ok(Self::new_with_proto(
453 global,
454 proto,
455 true,
456 Transform3D::identity(),
457 can_gc,
458 ));
459 }
460 match init.unwrap() {
461 StringOrUnrestrictedDoubleSequence::String(ref s) => {
462 if !global.is::<Window>() {
463 return Err(error::Error::Type(
464 "String constructor is only supported in the main thread.".to_owned(),
465 ));
466 }
467 if s.is_empty() {
468 return Ok(Self::new(global, true, Transform3D::identity(), can_gc));
469 }
470 transform_to_matrix(s.to_string())
471 .map(|(is2D, matrix)| Self::new_with_proto(global, proto, is2D, matrix, can_gc))
472 },
473 StringOrUnrestrictedDoubleSequence::UnrestrictedDoubleSequence(ref entries) => {
474 entries_to_matrix(&entries[..])
475 .map(|(is2D, matrix)| Self::new_with_proto(global, proto, is2D, matrix, can_gc))
476 },
477 }
478 }
479
480 fn FromMatrix(
482 global: &GlobalScope,
483 other: &DOMMatrixInit,
484 can_gc: CanGc,
485 ) -> Fallible<DomRoot<Self>> {
486 dommatrixinit_to_matrix(other).map(|(is2D, matrix)| Self::new(global, is2D, matrix, can_gc))
487 }
488
489 fn FromFloat32Array(
491 global: &GlobalScope,
492 array: CustomAutoRooterGuard<Float32Array>,
493 can_gc: CanGc,
494 ) -> Fallible<DomRoot<DOMMatrixReadOnly>> {
495 let vec: Vec<f64> = array.to_vec().iter().map(|&x| x as f64).collect();
496 DOMMatrixReadOnly::Constructor(
497 global,
498 None,
499 can_gc,
500 Some(StringOrUnrestrictedDoubleSequence::UnrestrictedDoubleSequence(vec)),
501 )
502 }
503
504 fn FromFloat64Array(
506 global: &GlobalScope,
507 array: CustomAutoRooterGuard<Float64Array>,
508 can_gc: CanGc,
509 ) -> Fallible<DomRoot<DOMMatrixReadOnly>> {
510 let vec: Vec<f64> = array.to_vec();
511 DOMMatrixReadOnly::Constructor(
512 global,
513 None,
514 can_gc,
515 Some(StringOrUnrestrictedDoubleSequence::UnrestrictedDoubleSequence(vec)),
516 )
517 }
518
519 fn M11(&self) -> f64 {
521 self.matrix.borrow().m11
522 }
523
524 fn M12(&self) -> f64 {
526 self.matrix.borrow().m12
527 }
528
529 fn M13(&self) -> f64 {
531 self.matrix.borrow().m13
532 }
533
534 fn M14(&self) -> f64 {
536 self.matrix.borrow().m14
537 }
538
539 fn M21(&self) -> f64 {
541 self.matrix.borrow().m21
542 }
543
544 fn M22(&self) -> f64 {
546 self.matrix.borrow().m22
547 }
548
549 fn M23(&self) -> f64 {
551 self.matrix.borrow().m23
552 }
553
554 fn M24(&self) -> f64 {
556 self.matrix.borrow().m24
557 }
558
559 fn M31(&self) -> f64 {
561 self.matrix.borrow().m31
562 }
563
564 fn M32(&self) -> f64 {
566 self.matrix.borrow().m32
567 }
568
569 fn M33(&self) -> f64 {
571 self.matrix.borrow().m33
572 }
573
574 fn M34(&self) -> f64 {
576 self.matrix.borrow().m34
577 }
578
579 fn M41(&self) -> f64 {
581 self.matrix.borrow().m41
582 }
583
584 fn M42(&self) -> f64 {
586 self.matrix.borrow().m42
587 }
588
589 fn M43(&self) -> f64 {
591 self.matrix.borrow().m43
592 }
593
594 fn M44(&self) -> f64 {
596 self.matrix.borrow().m44
597 }
598
599 fn A(&self) -> f64 {
601 self.M11()
602 }
603
604 fn B(&self) -> f64 {
606 self.M12()
607 }
608
609 fn C(&self) -> f64 {
611 self.M21()
612 }
613
614 fn D(&self) -> f64 {
616 self.M22()
617 }
618
619 fn E(&self) -> f64 {
621 self.M41()
622 }
623
624 fn F(&self) -> f64 {
626 self.M42()
627 }
628
629 fn Is2D(&self) -> bool {
631 self.is2D.get()
632 }
633
634 fn IsIdentity(&self) -> bool {
636 let matrix = self.matrix.borrow();
637 matrix.m12 == 0.0 &&
638 matrix.m13 == 0.0 &&
639 matrix.m14 == 0.0 &&
640 matrix.m21 == 0.0 &&
641 matrix.m23 == 0.0 &&
642 matrix.m24 == 0.0 &&
643 matrix.m31 == 0.0 &&
644 matrix.m32 == 0.0 &&
645 matrix.m34 == 0.0 &&
646 matrix.m41 == 0.0 &&
647 matrix.m42 == 0.0 &&
648 matrix.m43 == 0.0 &&
649 matrix.m11 == 1.0 &&
650 matrix.m22 == 1.0 &&
651 matrix.m33 == 1.0 &&
652 matrix.m44 == 1.0
653 }
654
655 fn Translate(&self, tx: f64, ty: f64, tz: f64, can_gc: CanGc) -> DomRoot<DOMMatrix> {
657 DOMMatrix::from_readonly(&self.global(), self, can_gc).TranslateSelf(tx, ty, tz)
658 }
659
660 fn Scale(
662 &self,
663 scaleX: f64,
664 scaleY: Option<f64>,
665 scaleZ: f64,
666 originX: f64,
667 originY: f64,
668 originZ: f64,
669 can_gc: CanGc,
670 ) -> DomRoot<DOMMatrix> {
671 DOMMatrix::from_readonly(&self.global(), self, can_gc)
672 .ScaleSelf(scaleX, scaleY, scaleZ, originX, originY, originZ)
673 }
674
675 fn ScaleNonUniform(&self, scaleX: f64, scaleY: f64, can_gc: CanGc) -> DomRoot<DOMMatrix> {
677 DOMMatrix::from_readonly(&self.global(), self, can_gc).ScaleSelf(
678 scaleX,
679 Some(scaleY),
680 1.0,
681 0.0,
682 0.0,
683 0.0,
684 )
685 }
686
687 fn Scale3d(
689 &self,
690 scale: f64,
691 originX: f64,
692 originY: f64,
693 originZ: f64,
694 can_gc: CanGc,
695 ) -> DomRoot<DOMMatrix> {
696 DOMMatrix::from_readonly(&self.global(), self, can_gc)
697 .Scale3dSelf(scale, originX, originY, originZ)
698 }
699
700 fn Rotate(
702 &self,
703 rotX: f64,
704 rotY: Option<f64>,
705 rotZ: Option<f64>,
706 can_gc: CanGc,
707 ) -> DomRoot<DOMMatrix> {
708 DOMMatrix::from_readonly(&self.global(), self, can_gc).RotateSelf(rotX, rotY, rotZ)
709 }
710
711 fn RotateFromVector(&self, x: f64, y: f64, can_gc: CanGc) -> DomRoot<DOMMatrix> {
713 DOMMatrix::from_readonly(&self.global(), self, can_gc).RotateFromVectorSelf(x, y)
714 }
715
716 fn RotateAxisAngle(
718 &self,
719 x: f64,
720 y: f64,
721 z: f64,
722 angle: f64,
723 can_gc: CanGc,
724 ) -> DomRoot<DOMMatrix> {
725 DOMMatrix::from_readonly(&self.global(), self, can_gc).RotateAxisAngleSelf(x, y, z, angle)
726 }
727
728 fn SkewX(&self, sx: f64, can_gc: CanGc) -> DomRoot<DOMMatrix> {
730 DOMMatrix::from_readonly(&self.global(), self, can_gc).SkewXSelf(sx)
731 }
732
733 fn SkewY(&self, sy: f64, can_gc: CanGc) -> DomRoot<DOMMatrix> {
735 DOMMatrix::from_readonly(&self.global(), self, can_gc).SkewYSelf(sy)
736 }
737
738 fn Multiply(&self, other: &DOMMatrixInit, can_gc: CanGc) -> Fallible<DomRoot<DOMMatrix>> {
740 DOMMatrix::from_readonly(&self.global(), self, can_gc).MultiplySelf(other)
741 }
742
743 fn FlipX(&self, can_gc: CanGc) -> DomRoot<DOMMatrix> {
745 let is2D = self.is2D.get();
746 let flip = Transform3D::new(
747 -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,
748 );
749 let matrix = flip.then(&self.matrix.borrow());
750 DOMMatrix::new(&self.global(), is2D, matrix, can_gc)
751 }
752
753 fn FlipY(&self, can_gc: CanGc) -> DomRoot<DOMMatrix> {
755 let is2D = self.is2D.get();
756 let flip = Transform3D::new(
757 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,
758 );
759 let matrix = flip.then(&self.matrix.borrow());
760 DOMMatrix::new(&self.global(), is2D, matrix, can_gc)
761 }
762
763 fn Inverse(&self, can_gc: CanGc) -> DomRoot<DOMMatrix> {
765 DOMMatrix::from_readonly(&self.global(), self, can_gc).InvertSelf()
766 }
767
768 fn TransformPoint(&self, point: &DOMPointInit, can_gc: CanGc) -> DomRoot<DOMPoint> {
770 let mat = self.matrix.borrow();
777 let x = point.x * mat.m11 + point.y * mat.m21 + point.z * mat.m31 + point.w * mat.m41;
778 let y = point.x * mat.m12 + point.y * mat.m22 + point.z * mat.m32 + point.w * mat.m42;
779 let z = point.x * mat.m13 + point.y * mat.m23 + point.z * mat.m33 + point.w * mat.m43;
780 let w = point.x * mat.m14 + point.y * mat.m24 + point.z * mat.m34 + point.w * mat.m44;
781
782 DOMPoint::new(&self.global(), x, y, z, w, can_gc)
783 }
784
785 fn ToFloat32Array(&self, cx: JSContext, can_gc: CanGc) -> Float32Array {
787 let vec: Vec<f32> = self
788 .matrix
789 .borrow()
790 .to_array()
791 .iter()
792 .map(|&x| x as f32)
793 .collect();
794 rooted!(in (*cx) let mut array = ptr::null_mut::<JSObject>());
795 create_buffer_source(cx, &vec, array.handle_mut(), can_gc)
796 .expect("Converting matrix to float32 array should never fail")
797 }
798
799 fn ToFloat64Array(&self, cx: JSContext, can_gc: CanGc) -> Float64Array {
801 rooted!(in (*cx) let mut array = ptr::null_mut::<JSObject>());
802 create_buffer_source(
803 cx,
804 &self.matrix.borrow().to_array(),
805 array.handle_mut(),
806 can_gc,
807 )
808 .expect("Converting matrix to float64 array should never fail")
809 }
810
811 #[allow(unsafe_code)]
813 fn Stringifier(&self) -> Fallible<DOMString> {
814 let mat = self.matrix.borrow();
817 if !mat.m11.is_finite() ||
818 !mat.m12.is_finite() ||
819 !mat.m13.is_finite() ||
820 !mat.m14.is_finite() ||
821 !mat.m21.is_finite() ||
822 !mat.m22.is_finite() ||
823 !mat.m23.is_finite() ||
824 !mat.m24.is_finite() ||
825 !mat.m31.is_finite() ||
826 !mat.m32.is_finite() ||
827 !mat.m33.is_finite() ||
828 !mat.m34.is_finite() ||
829 !mat.m41.is_finite() ||
830 !mat.m42.is_finite() ||
831 !mat.m43.is_finite() ||
832 !mat.m44.is_finite()
833 {
834 return Err(error::Error::InvalidState);
835 }
836
837 let cx = GlobalScope::get_cx();
838 let to_string = |f: f64| {
839 let value = jsval::DoubleValue(f);
840
841 unsafe {
842 rooted!(in(*cx) let mut rooted_value = value);
843 let serialization = std::ptr::NonNull::new(ToString(*cx, rooted_value.handle()))
844 .expect("Pointer cannot be null");
845 jsstr_to_string(*cx, serialization)
846 }
847 };
848
849 let string = if self.is2D() {
852 format!(
866 "matrix({}, {}, {}, {}, {}, {})",
867 to_string(mat.m11),
868 to_string(mat.m12),
869 to_string(mat.m21),
870 to_string(mat.m22),
871 to_string(mat.m41),
872 to_string(mat.m42)
873 )
874 .into()
875 }
876 else {
878 format!(
907 "matrix3d({}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {})",
908 to_string(mat.m11),
909 to_string(mat.m12),
910 to_string(mat.m13),
911 to_string(mat.m14),
912 to_string(mat.m21),
913 to_string(mat.m22),
914 to_string(mat.m23),
915 to_string(mat.m24),
916 to_string(mat.m31),
917 to_string(mat.m32),
918 to_string(mat.m33),
919 to_string(mat.m34),
920 to_string(mat.m41),
921 to_string(mat.m42),
922 to_string(mat.m43),
923 to_string(mat.m44)
924 )
925 .into()
926 };
927
928 Ok(string)
929 }
930}
931
932impl Serializable for DOMMatrixReadOnly {
933 type Index = DomMatrixIndex;
934 type Data = DomMatrix;
935
936 fn serialize(&self) -> Result<(DomMatrixId, Self::Data), ()> {
937 let serialized = if self.is2D() {
938 DomMatrix {
939 matrix: Transform3D::new(
940 self.M11(),
941 self.M12(),
942 f64::NAN,
943 f64::NAN,
944 self.M21(),
945 self.M22(),
946 f64::NAN,
947 f64::NAN,
948 f64::NAN,
949 f64::NAN,
950 f64::NAN,
951 f64::NAN,
952 self.M41(),
953 self.M42(),
954 f64::NAN,
955 f64::NAN,
956 ),
957 is_2d: true,
958 }
959 } else {
960 DomMatrix {
961 matrix: *self.matrix(),
962 is_2d: false,
963 }
964 };
965 Ok((DomMatrixId::new(), serialized))
966 }
967
968 fn deserialize(
969 owner: &GlobalScope,
970 serialized: Self::Data,
971 can_gc: CanGc,
972 ) -> Result<DomRoot<Self>, ()>
973 where
974 Self: Sized,
975 {
976 if serialized.is_2d {
977 Ok(Self::new(
978 owner,
979 true,
980 Transform3D::new(
981 serialized.matrix.m11,
982 serialized.matrix.m12,
983 0.0,
984 0.0,
985 serialized.matrix.m21,
986 serialized.matrix.m22,
987 0.0,
988 0.0,
989 0.0,
990 0.0,
991 1.0,
992 0.0,
993 serialized.matrix.m41,
994 serialized.matrix.m42,
995 0.0,
996 1.0,
997 ),
998 can_gc,
999 ))
1000 } else {
1001 Ok(Self::new(owner, false, serialized.matrix, can_gc))
1002 }
1003 }
1004
1005 fn serialized_storage<'a>(
1006 data: StructuredData<'a, '_>,
1007 ) -> &'a mut Option<HashMap<DomMatrixId, Self::Data>> {
1008 match data {
1009 StructuredData::Reader(reader) => &mut reader.matrices,
1010 StructuredData::Writer(writer) => &mut writer.matrices,
1011 }
1012 }
1013}
1014
1015pub(crate) fn entries_to_matrix(entries: &[f64]) -> Fallible<(bool, Transform3D<f64>)> {
1017 if let Ok(array) = entries.try_into() {
1018 Ok((true, Transform2D::from_array(array).to_3d()))
1019 } else if let Ok(array) = entries.try_into() {
1020 Ok((false, Transform3D::from_array(array)))
1021 } else {
1022 let err_msg = format!("Expected 6 or 16 entries, but found {}.", entries.len());
1023 Err(error::Error::Type(err_msg.to_owned()))
1024 }
1025}
1026
1027fn validate_and_fixup_2d(dict: &DOMMatrix2DInit) -> Fallible<Transform2D<f64>> {
1029 let same_value_zero = |x: f64, y: f64| -> bool { x.is_nan() && y.is_nan() || x == y };
1031
1032 if dict.a.is_some() &&
1035 dict.m11.is_some() &&
1036 !same_value_zero(dict.a.unwrap(), dict.m11.unwrap()) ||
1037 dict.b.is_some() &&
1038 dict.m12.is_some() &&
1039 !same_value_zero(dict.b.unwrap(), dict.m12.unwrap()) ||
1040 dict.c.is_some() &&
1041 dict.m21.is_some() &&
1042 !same_value_zero(dict.c.unwrap(), dict.m21.unwrap()) ||
1043 dict.d.is_some() &&
1044 dict.m22.is_some() &&
1045 !same_value_zero(dict.d.unwrap(), dict.m22.unwrap()) ||
1046 dict.e.is_some() &&
1047 dict.m41.is_some() &&
1048 !same_value_zero(dict.e.unwrap(), dict.m41.unwrap()) ||
1049 dict.f.is_some() &&
1050 dict.m42.is_some() &&
1051 !same_value_zero(dict.f.unwrap(), dict.m42.unwrap())
1052 {
1053 return Err(error::Error::Type(
1054 "Property mismatch on matrix initialization.".to_owned(),
1055 ));
1056 }
1057
1058 let m11 = dict.m11.unwrap_or(dict.a.unwrap_or(1.0));
1061
1062 let m12 = dict.m12.unwrap_or(dict.b.unwrap_or(0.0));
1065
1066 let m21 = dict.m21.unwrap_or(dict.c.unwrap_or(0.0));
1069
1070 let m22 = dict.m22.unwrap_or(dict.d.unwrap_or(1.0));
1073
1074 let m41 = dict.m41.unwrap_or(dict.e.unwrap_or(0.0));
1077
1078 let m42 = dict.m42.unwrap_or(dict.f.unwrap_or(0.0));
1081
1082 Ok(Transform2D::new(m11, m12, m21, m22, m41, m42))
1083}
1084
1085fn validate_and_fixup(dict: &DOMMatrixInit) -> Fallible<(bool, Transform3D<f64>)> {
1087 let transform2d = validate_and_fixup_2d(&dict.parent)?;
1089
1090 if dict.is2D == Some(true) &&
1095 (dict.m13 != 0.0 ||
1096 dict.m14 != 0.0 ||
1097 dict.m23 != 0.0 ||
1098 dict.m24 != 0.0 ||
1099 dict.m31 != 0.0 ||
1100 dict.m32 != 0.0 ||
1101 dict.m34 != 0.0 ||
1102 dict.m43 != 0.0 ||
1103 dict.m33 != 1.0 ||
1104 dict.m44 != 1.0)
1105 {
1106 return Err(error::Error::Type(
1107 "The is2D member is set to true but the input matrix is a 3d matrix.".to_owned(),
1108 ));
1109 }
1110
1111 let mut is_2d = dict.is2D;
1112
1113 if is_2d.is_none() &&
1118 (dict.m13 != 0.0 ||
1119 dict.m14 != 0.0 ||
1120 dict.m23 != 0.0 ||
1121 dict.m24 != 0.0 ||
1122 dict.m31 != 0.0 ||
1123 dict.m32 != 0.0 ||
1124 dict.m34 != 0.0 ||
1125 dict.m43 != 0.0 ||
1126 dict.m33 != 1.0 ||
1127 dict.m44 != 1.0)
1128 {
1129 is_2d = Some(false);
1130 }
1131
1132 let is_2d = is_2d.unwrap_or(true);
1134
1135 let mut transform = transform2d.to_3d();
1136 transform.m13 = dict.m13;
1137 transform.m14 = dict.m14;
1138 transform.m23 = dict.m23;
1139 transform.m24 = dict.m24;
1140 transform.m31 = dict.m31;
1141 transform.m32 = dict.m32;
1142 transform.m33 = dict.m33;
1143 transform.m34 = dict.m34;
1144 transform.m43 = dict.m43;
1145 transform.m44 = dict.m44;
1146
1147 Ok((is_2d, transform))
1148}
1149
1150pub(crate) fn dommatrix2dinit_to_matrix(dict: &DOMMatrix2DInit) -> Fallible<Transform2D<f64>> {
1152 validate_and_fixup_2d(dict)
1158}
1159
1160pub(crate) fn dommatrixinit_to_matrix(dict: &DOMMatrixInit) -> Fallible<(bool, Transform3D<f64>)> {
1162 validate_and_fixup(dict)
1168}
1169
1170#[inline]
1171fn normalize_point(x: f64, y: f64, z: f64) -> (f64, f64, f64) {
1172 let len = (x * x + y * y + z * z).sqrt();
1173 if len == 0.0 {
1174 (0.0, 0.0, 0.0)
1175 } else {
1176 (x / len, y / len, z / len)
1177 }
1178}
1179
1180pub(crate) fn transform_to_matrix(value: String) -> Fallible<(bool, Transform3D<f64>)> {
1181 use style::properties::longhands::transform;
1182
1183 let mut input = ParserInput::new(&value);
1184 let mut parser = Parser::new(&mut input);
1185 let url_data = Url::parse("about:blank").unwrap().into();
1186 let context = ParserContext::new(
1187 ::style::stylesheets::Origin::Author,
1188 &url_data,
1189 Some(::style::stylesheets::CssRuleType::Style),
1190 ::style_traits::ParsingMode::DEFAULT,
1191 ::style::context::QuirksMode::NoQuirks,
1192 Default::default(),
1193 None,
1194 None,
1195 );
1196
1197 let transform = match parser.parse_entirely(|t| transform::parse(&context, t)) {
1198 Ok(result) => result,
1199 Err(..) => return Err(error::Error::Syntax),
1200 };
1201
1202 let (m, is_3d) = match transform.to_transform_3d_matrix_f64(None) {
1203 Ok(result) => result,
1204 Err(..) => return Err(error::Error::Syntax),
1205 };
1206
1207 Ok((!is_3d, m))
1208}