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};
19use rustc_hash::FxHashMap;
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 set_matrix(&self, value: Transform3D<f64>) {
90 self.set_m11(value.m11);
91 self.set_m12(value.m12);
92 self.set_m13(value.m13);
93 self.set_m14(value.m14);
94 self.set_m21(value.m21);
95 self.set_m22(value.m22);
96 self.set_m23(value.m23);
97 self.set_m24(value.m24);
98 self.set_m31(value.m31);
99 self.set_m32(value.m32);
100 self.set_m33(value.m33);
101 self.set_m34(value.m34);
102 self.set_m41(value.m41);
103 self.set_m42(value.m42);
104 self.set_m43(value.m43);
105 self.set_m44(value.m44);
106 }
107
108 pub(crate) fn is2D(&self) -> bool {
109 self.is2D.get()
110 }
111
112 pub(crate) fn set_is2D(&self, value: bool) {
113 self.is2D.set(value);
114 }
115
116 pub(crate) fn set_m11(&self, value: f64) {
118 self.matrix.borrow_mut().m11 = value;
119 }
120
121 pub(crate) fn set_m12(&self, value: f64) {
123 self.matrix.borrow_mut().m12 = value;
124 }
125
126 pub(crate) fn set_m13(&self, value: f64) {
128 self.matrix.borrow_mut().m13 = value;
132 if value.abs() != 0. {
133 self.is2D.set(false);
134 }
135 }
136
137 pub(crate) fn set_m14(&self, value: f64) {
139 self.matrix.borrow_mut().m14 = value;
142
143 if value.abs() != 0. {
144 self.is2D.set(false);
145 }
146 }
147
148 pub(crate) fn set_m21(&self, value: f64) {
150 self.matrix.borrow_mut().m21 = value;
151 }
152
153 pub(crate) fn set_m22(&self, value: f64) {
155 self.matrix.borrow_mut().m22 = value;
156 }
157
158 pub(crate) fn set_m23(&self, value: f64) {
160 self.matrix.borrow_mut().m23 = value;
163
164 if value.abs() != 0. {
165 self.is2D.set(false);
166 }
167 }
168
169 pub(crate) fn set_m24(&self, value: f64) {
171 self.matrix.borrow_mut().m24 = value;
174
175 if value.abs() != 0. {
176 self.is2D.set(false);
177 }
178 }
179
180 pub(crate) fn set_m31(&self, value: f64) {
182 self.matrix.borrow_mut().m31 = value;
185
186 if value.abs() != 0. {
187 self.is2D.set(false);
188 }
189 }
190
191 pub(crate) fn set_m32(&self, value: f64) {
193 self.matrix.borrow_mut().m32 = value;
196
197 if value.abs() != 0. {
198 self.is2D.set(false);
199 }
200 }
201
202 pub(crate) fn set_m33(&self, value: f64) {
204 self.matrix.borrow_mut().m33 = value;
207
208 if value != 1. {
209 self.is2D.set(false);
210 }
211 }
212
213 pub(crate) fn set_m34(&self, value: f64) {
215 self.matrix.borrow_mut().m34 = value;
218
219 if value.abs() != 0. {
220 self.is2D.set(false);
221 }
222 }
223
224 pub(crate) fn set_m41(&self, value: f64) {
226 self.matrix.borrow_mut().m41 = value;
227 }
228
229 pub(crate) fn set_m42(&self, value: f64) {
231 self.matrix.borrow_mut().m42 = value;
232 }
233
234 pub(crate) fn set_m43(&self, value: f64) {
236 self.matrix.borrow_mut().m43 = value;
239
240 if value.abs() != 0. {
241 self.is2D.set(false);
242 }
243 }
244
245 pub(crate) fn set_m44(&self, value: f64) {
247 self.matrix.borrow_mut().m44 = value;
250
251 if value != 1. {
252 self.is2D.set(false);
253 }
254 }
255
256 pub(crate) fn multiply_self(&self, other: &DOMMatrixInit) -> Fallible<()> {
258 dommatrixinit_to_matrix(other).map(|(is2D, other_matrix)| {
260 let mut matrix = self.matrix.borrow_mut();
262 *matrix = other_matrix.then(&matrix);
263 if !is2D {
265 self.is2D.set(false);
266 }
267 })
269 }
270
271 pub(crate) fn pre_multiply_self(&self, other: &DOMMatrixInit) -> Fallible<()> {
273 dommatrixinit_to_matrix(other).map(|(is2D, other_matrix)| {
275 let mut matrix = self.matrix.borrow_mut();
277 *matrix = matrix.then(&other_matrix);
278 if !is2D {
280 self.is2D.set(false);
281 }
282 })
284 }
285
286 pub(crate) fn translate_self(&self, tx: f64, ty: f64, tz: f64) {
288 let translation = Transform3D::translation(tx, ty, tz);
290 let mut matrix = self.matrix.borrow_mut();
291 *matrix = translation.then(&matrix);
292 if tz != 0.0 {
294 self.is2D.set(false);
295 }
296 }
298
299 pub(crate) fn scale_self(
301 &self,
302 scaleX: f64,
303 scaleY: Option<f64>,
304 scaleZ: f64,
305 mut originX: f64,
306 mut originY: f64,
307 mut originZ: f64,
308 ) {
309 self.translate_self(originX, originY, originZ);
311 let scaleY = scaleY.unwrap_or(scaleX);
313 {
315 let scale3D = Transform3D::scale(scaleX, scaleY, scaleZ);
316 let mut matrix = self.matrix.borrow_mut();
317 *matrix = scale3D.then(&matrix);
318 }
319 originX = -originX;
321 originY = -originY;
322 originZ = -originZ;
323 self.translate_self(originX, originY, originZ);
325 if scaleZ != 1.0 || originZ != 0.0 {
327 self.is2D.set(false);
328 }
329 }
331
332 pub(crate) fn scale_3d_self(&self, scale: f64, originX: f64, originY: f64, originZ: f64) {
334 self.translate_self(originX, originY, originZ);
336 {
338 let scale3D = Transform3D::scale(scale, scale, scale);
339 let mut matrix = self.matrix.borrow_mut();
340 *matrix = scale3D.then(&matrix);
341 }
342 self.translate_self(-originX, -originY, -originZ);
344 if scale != 1.0 {
346 self.is2D.set(false);
347 }
348 }
350
351 pub(crate) fn rotate_self(&self, mut rotX: f64, mut rotY: Option<f64>, mut rotZ: Option<f64>) {
353 if rotY.is_none() && rotZ.is_none() {
355 rotZ = Some(rotX);
356 rotX = 0.0;
357 rotY = Some(0.0);
358 }
359 let rotY = rotY.unwrap_or(0.0);
361 let rotZ = rotZ.unwrap_or(0.0);
363 if rotX != 0.0 || rotY != 0.0 {
365 self.is2D.set(false);
366 }
367 if rotZ != 0.0 {
368 let rotation = Transform3D::rotation(0.0, 0.0, 1.0, Angle::radians(rotZ.to_radians()));
370 let mut matrix = self.matrix.borrow_mut();
371 *matrix = rotation.then(&matrix);
372 }
373 if rotY != 0.0 {
374 let rotation = Transform3D::rotation(0.0, 1.0, 0.0, Angle::radians(rotY.to_radians()));
376 let mut matrix = self.matrix.borrow_mut();
377 *matrix = rotation.then(&matrix);
378 }
379 if rotX != 0.0 {
380 let rotation = Transform3D::rotation(1.0, 0.0, 0.0, Angle::radians(rotX.to_radians()));
382 let mut matrix = self.matrix.borrow_mut();
383 *matrix = rotation.then(&matrix);
384 }
385 }
387
388 pub(crate) fn rotate_from_vector_self(&self, x: f64, y: f64) {
390 if y != 0.0 || x < 0.0 {
392 let rotZ = Angle::radians(f64::atan2(y, x));
394 let rotation = Transform3D::rotation(0.0, 0.0, 1.0, rotZ);
395 let mut matrix = self.matrix.borrow_mut();
396 *matrix = rotation.then(&matrix);
397 }
398 }
400
401 pub(crate) fn rotate_axis_angle_self(&self, x: f64, y: f64, z: f64, angle: f64) {
403 let (norm_x, norm_y, norm_z) = normalize_point(x, y, z);
405 let rotation =
407 Transform3D::rotation(norm_x, norm_y, norm_z, Angle::radians(angle.to_radians()));
408 let mut matrix = self.matrix.borrow_mut();
409 *matrix = rotation.then(&matrix);
410 if x != 0.0 || y != 0.0 {
412 self.is2D.set(false);
413 }
414 }
416
417 pub(crate) fn skew_x_self(&self, sx: f64) {
419 let skew = Transform3D::skew(Angle::radians(sx.to_radians()), Angle::radians(0.0));
421 let mut matrix = self.matrix.borrow_mut();
422 *matrix = skew.then(&matrix);
423 }
425
426 pub(crate) fn skew_y_self(&self, sy: f64) {
428 let skew = Transform3D::skew(Angle::radians(0.0), Angle::radians(sy.to_radians()));
430 let mut matrix = self.matrix.borrow_mut();
431 *matrix = skew.then(&matrix);
432 }
434
435 pub(crate) fn invert_self(&self) {
437 let mut matrix = self.matrix.borrow_mut();
438 let inverted = match self.is2D() {
440 true => matrix.to_2d().inverse().map(|m| m.to_3d()),
441 false => matrix.inverse(),
442 };
443
444 *matrix = inverted.unwrap_or_else(|| -> Transform3D<f64> {
447 self.is2D.set(false);
448 Transform3D::new(
449 f64::NAN,
450 f64::NAN,
451 f64::NAN,
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 )
466 });
467 }
469}
470
471#[allow(non_snake_case)]
472impl DOMMatrixReadOnlyMethods<crate::DomTypeHolder> for DOMMatrixReadOnly {
473 fn Constructor(
475 global: &GlobalScope,
476 proto: Option<HandleObject>,
477 can_gc: CanGc,
478 init: Option<StringOrUnrestrictedDoubleSequence>,
479 ) -> Fallible<DomRoot<Self>> {
480 if init.is_none() {
481 return Ok(Self::new_with_proto(
482 global,
483 proto,
484 true,
485 Transform3D::identity(),
486 can_gc,
487 ));
488 }
489 match init.unwrap() {
490 StringOrUnrestrictedDoubleSequence::String(ref s) => {
491 if !global.is::<Window>() {
492 return Err(error::Error::Type(
493 "String constructor is only supported in the main thread.".to_owned(),
494 ));
495 }
496 if s.is_empty() {
497 return Ok(Self::new(global, true, Transform3D::identity(), can_gc));
498 }
499 transform_to_matrix(s.to_string())
500 .map(|(is2D, matrix)| Self::new_with_proto(global, proto, is2D, matrix, can_gc))
501 },
502 StringOrUnrestrictedDoubleSequence::UnrestrictedDoubleSequence(ref entries) => {
503 entries_to_matrix(&entries[..])
504 .map(|(is2D, matrix)| Self::new_with_proto(global, proto, is2D, matrix, can_gc))
505 },
506 }
507 }
508
509 fn FromMatrix(
511 global: &GlobalScope,
512 other: &DOMMatrixInit,
513 can_gc: CanGc,
514 ) -> Fallible<DomRoot<Self>> {
515 dommatrixinit_to_matrix(other).map(|(is2D, matrix)| Self::new(global, is2D, matrix, can_gc))
516 }
517
518 fn FromFloat32Array(
520 global: &GlobalScope,
521 array: CustomAutoRooterGuard<Float32Array>,
522 can_gc: CanGc,
523 ) -> Fallible<DomRoot<DOMMatrixReadOnly>> {
524 let vec: Vec<f64> = array.to_vec().iter().map(|&x| x as f64).collect();
525 DOMMatrixReadOnly::Constructor(
526 global,
527 None,
528 can_gc,
529 Some(StringOrUnrestrictedDoubleSequence::UnrestrictedDoubleSequence(vec)),
530 )
531 }
532
533 fn FromFloat64Array(
535 global: &GlobalScope,
536 array: CustomAutoRooterGuard<Float64Array>,
537 can_gc: CanGc,
538 ) -> Fallible<DomRoot<DOMMatrixReadOnly>> {
539 let vec: Vec<f64> = array.to_vec();
540 DOMMatrixReadOnly::Constructor(
541 global,
542 None,
543 can_gc,
544 Some(StringOrUnrestrictedDoubleSequence::UnrestrictedDoubleSequence(vec)),
545 )
546 }
547
548 fn M11(&self) -> f64 {
550 self.matrix.borrow().m11
551 }
552
553 fn M12(&self) -> f64 {
555 self.matrix.borrow().m12
556 }
557
558 fn M13(&self) -> f64 {
560 self.matrix.borrow().m13
561 }
562
563 fn M14(&self) -> f64 {
565 self.matrix.borrow().m14
566 }
567
568 fn M21(&self) -> f64 {
570 self.matrix.borrow().m21
571 }
572
573 fn M22(&self) -> f64 {
575 self.matrix.borrow().m22
576 }
577
578 fn M23(&self) -> f64 {
580 self.matrix.borrow().m23
581 }
582
583 fn M24(&self) -> f64 {
585 self.matrix.borrow().m24
586 }
587
588 fn M31(&self) -> f64 {
590 self.matrix.borrow().m31
591 }
592
593 fn M32(&self) -> f64 {
595 self.matrix.borrow().m32
596 }
597
598 fn M33(&self) -> f64 {
600 self.matrix.borrow().m33
601 }
602
603 fn M34(&self) -> f64 {
605 self.matrix.borrow().m34
606 }
607
608 fn M41(&self) -> f64 {
610 self.matrix.borrow().m41
611 }
612
613 fn M42(&self) -> f64 {
615 self.matrix.borrow().m42
616 }
617
618 fn M43(&self) -> f64 {
620 self.matrix.borrow().m43
621 }
622
623 fn M44(&self) -> f64 {
625 self.matrix.borrow().m44
626 }
627
628 fn A(&self) -> f64 {
630 self.M11()
631 }
632
633 fn B(&self) -> f64 {
635 self.M12()
636 }
637
638 fn C(&self) -> f64 {
640 self.M21()
641 }
642
643 fn D(&self) -> f64 {
645 self.M22()
646 }
647
648 fn E(&self) -> f64 {
650 self.M41()
651 }
652
653 fn F(&self) -> f64 {
655 self.M42()
656 }
657
658 fn Is2D(&self) -> bool {
660 self.is2D.get()
661 }
662
663 fn IsIdentity(&self) -> bool {
665 let matrix = self.matrix.borrow();
666 matrix.m12 == 0.0 &&
667 matrix.m13 == 0.0 &&
668 matrix.m14 == 0.0 &&
669 matrix.m21 == 0.0 &&
670 matrix.m23 == 0.0 &&
671 matrix.m24 == 0.0 &&
672 matrix.m31 == 0.0 &&
673 matrix.m32 == 0.0 &&
674 matrix.m34 == 0.0 &&
675 matrix.m41 == 0.0 &&
676 matrix.m42 == 0.0 &&
677 matrix.m43 == 0.0 &&
678 matrix.m11 == 1.0 &&
679 matrix.m22 == 1.0 &&
680 matrix.m33 == 1.0 &&
681 matrix.m44 == 1.0
682 }
683
684 fn Translate(&self, tx: f64, ty: f64, tz: f64, can_gc: CanGc) -> DomRoot<DOMMatrix> {
686 DOMMatrix::from_readonly(&self.global(), self, can_gc).TranslateSelf(tx, ty, tz)
687 }
688
689 fn Scale(
691 &self,
692 scaleX: f64,
693 scaleY: Option<f64>,
694 scaleZ: f64,
695 originX: f64,
696 originY: f64,
697 originZ: f64,
698 can_gc: CanGc,
699 ) -> DomRoot<DOMMatrix> {
700 DOMMatrix::from_readonly(&self.global(), self, can_gc)
701 .ScaleSelf(scaleX, scaleY, scaleZ, originX, originY, originZ)
702 }
703
704 fn ScaleNonUniform(&self, scaleX: f64, scaleY: f64, can_gc: CanGc) -> DomRoot<DOMMatrix> {
706 DOMMatrix::from_readonly(&self.global(), self, can_gc).ScaleSelf(
707 scaleX,
708 Some(scaleY),
709 1.0,
710 0.0,
711 0.0,
712 0.0,
713 )
714 }
715
716 fn Scale3d(
718 &self,
719 scale: f64,
720 originX: f64,
721 originY: f64,
722 originZ: f64,
723 can_gc: CanGc,
724 ) -> DomRoot<DOMMatrix> {
725 DOMMatrix::from_readonly(&self.global(), self, can_gc)
726 .Scale3dSelf(scale, originX, originY, originZ)
727 }
728
729 fn Rotate(
731 &self,
732 rotX: f64,
733 rotY: Option<f64>,
734 rotZ: Option<f64>,
735 can_gc: CanGc,
736 ) -> DomRoot<DOMMatrix> {
737 DOMMatrix::from_readonly(&self.global(), self, can_gc).RotateSelf(rotX, rotY, rotZ)
738 }
739
740 fn RotateFromVector(&self, x: f64, y: f64, can_gc: CanGc) -> DomRoot<DOMMatrix> {
742 DOMMatrix::from_readonly(&self.global(), self, can_gc).RotateFromVectorSelf(x, y)
743 }
744
745 fn RotateAxisAngle(
747 &self,
748 x: f64,
749 y: f64,
750 z: f64,
751 angle: f64,
752 can_gc: CanGc,
753 ) -> DomRoot<DOMMatrix> {
754 DOMMatrix::from_readonly(&self.global(), self, can_gc).RotateAxisAngleSelf(x, y, z, angle)
755 }
756
757 fn SkewX(&self, sx: f64, can_gc: CanGc) -> DomRoot<DOMMatrix> {
759 DOMMatrix::from_readonly(&self.global(), self, can_gc).SkewXSelf(sx)
760 }
761
762 fn SkewY(&self, sy: f64, can_gc: CanGc) -> DomRoot<DOMMatrix> {
764 DOMMatrix::from_readonly(&self.global(), self, can_gc).SkewYSelf(sy)
765 }
766
767 fn Multiply(&self, other: &DOMMatrixInit, can_gc: CanGc) -> Fallible<DomRoot<DOMMatrix>> {
769 DOMMatrix::from_readonly(&self.global(), self, can_gc).MultiplySelf(other)
770 }
771
772 fn FlipX(&self, can_gc: CanGc) -> DomRoot<DOMMatrix> {
774 let is2D = self.is2D.get();
775 let flip = Transform3D::new(
776 -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,
777 );
778 let matrix = flip.then(&self.matrix.borrow());
779 DOMMatrix::new(&self.global(), is2D, matrix, can_gc)
780 }
781
782 fn FlipY(&self, can_gc: CanGc) -> DomRoot<DOMMatrix> {
784 let is2D = self.is2D.get();
785 let flip = Transform3D::new(
786 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,
787 );
788 let matrix = flip.then(&self.matrix.borrow());
789 DOMMatrix::new(&self.global(), is2D, matrix, can_gc)
790 }
791
792 fn Inverse(&self, can_gc: CanGc) -> DomRoot<DOMMatrix> {
794 DOMMatrix::from_readonly(&self.global(), self, can_gc).InvertSelf()
795 }
796
797 fn TransformPoint(&self, point: &DOMPointInit, can_gc: CanGc) -> DomRoot<DOMPoint> {
799 let mat = self.matrix.borrow();
806 let x = point.x * mat.m11 + point.y * mat.m21 + point.z * mat.m31 + point.w * mat.m41;
807 let y = point.x * mat.m12 + point.y * mat.m22 + point.z * mat.m32 + point.w * mat.m42;
808 let z = point.x * mat.m13 + point.y * mat.m23 + point.z * mat.m33 + point.w * mat.m43;
809 let w = point.x * mat.m14 + point.y * mat.m24 + point.z * mat.m34 + point.w * mat.m44;
810
811 DOMPoint::new(&self.global(), x, y, z, w, can_gc)
812 }
813
814 fn ToFloat32Array(&self, cx: JSContext, can_gc: CanGc) -> Float32Array {
816 let vec: Vec<f32> = self
817 .matrix
818 .borrow()
819 .to_array()
820 .iter()
821 .map(|&x| x as f32)
822 .collect();
823 rooted!(in (*cx) let mut array = ptr::null_mut::<JSObject>());
824 create_buffer_source(cx, &vec, array.handle_mut(), can_gc)
825 .expect("Converting matrix to float32 array should never fail")
826 }
827
828 fn ToFloat64Array(&self, cx: JSContext, can_gc: CanGc) -> Float64Array {
830 rooted!(in (*cx) let mut array = ptr::null_mut::<JSObject>());
831 create_buffer_source(
832 cx,
833 &self.matrix.borrow().to_array(),
834 array.handle_mut(),
835 can_gc,
836 )
837 .expect("Converting matrix to float64 array should never fail")
838 }
839
840 #[allow(unsafe_code)]
842 fn Stringifier(&self) -> Fallible<DOMString> {
843 let mat = self.matrix.borrow();
846 if !mat.m11.is_finite() ||
847 !mat.m12.is_finite() ||
848 !mat.m13.is_finite() ||
849 !mat.m14.is_finite() ||
850 !mat.m21.is_finite() ||
851 !mat.m22.is_finite() ||
852 !mat.m23.is_finite() ||
853 !mat.m24.is_finite() ||
854 !mat.m31.is_finite() ||
855 !mat.m32.is_finite() ||
856 !mat.m33.is_finite() ||
857 !mat.m34.is_finite() ||
858 !mat.m41.is_finite() ||
859 !mat.m42.is_finite() ||
860 !mat.m43.is_finite() ||
861 !mat.m44.is_finite()
862 {
863 return Err(error::Error::InvalidState);
864 }
865
866 let cx = GlobalScope::get_cx();
867 let to_string = |f: f64| {
868 let value = jsval::DoubleValue(f);
869
870 unsafe {
871 rooted!(in(*cx) let mut rooted_value = value);
872 let serialization = std::ptr::NonNull::new(ToString(*cx, rooted_value.handle()))
873 .expect("Pointer cannot be null");
874 jsstr_to_string(*cx, serialization)
875 }
876 };
877
878 let string = if self.is2D() {
881 format!(
895 "matrix({}, {}, {}, {}, {}, {})",
896 to_string(mat.m11),
897 to_string(mat.m12),
898 to_string(mat.m21),
899 to_string(mat.m22),
900 to_string(mat.m41),
901 to_string(mat.m42)
902 )
903 .into()
904 }
905 else {
907 format!(
936 "matrix3d({}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {})",
937 to_string(mat.m11),
938 to_string(mat.m12),
939 to_string(mat.m13),
940 to_string(mat.m14),
941 to_string(mat.m21),
942 to_string(mat.m22),
943 to_string(mat.m23),
944 to_string(mat.m24),
945 to_string(mat.m31),
946 to_string(mat.m32),
947 to_string(mat.m33),
948 to_string(mat.m34),
949 to_string(mat.m41),
950 to_string(mat.m42),
951 to_string(mat.m43),
952 to_string(mat.m44)
953 )
954 .into()
955 };
956
957 Ok(string)
958 }
959}
960
961impl Serializable for DOMMatrixReadOnly {
962 type Index = DomMatrixIndex;
963 type Data = DomMatrix;
964
965 fn serialize(&self) -> Result<(DomMatrixId, Self::Data), ()> {
966 let serialized = if self.is2D() {
967 DomMatrix {
968 matrix: Transform3D::new(
969 self.M11(),
970 self.M12(),
971 f64::NAN,
972 f64::NAN,
973 self.M21(),
974 self.M22(),
975 f64::NAN,
976 f64::NAN,
977 f64::NAN,
978 f64::NAN,
979 f64::NAN,
980 f64::NAN,
981 self.M41(),
982 self.M42(),
983 f64::NAN,
984 f64::NAN,
985 ),
986 is_2d: true,
987 }
988 } else {
989 DomMatrix {
990 matrix: *self.matrix(),
991 is_2d: false,
992 }
993 };
994 Ok((DomMatrixId::new(), serialized))
995 }
996
997 fn deserialize(
998 owner: &GlobalScope,
999 serialized: Self::Data,
1000 can_gc: CanGc,
1001 ) -> Result<DomRoot<Self>, ()>
1002 where
1003 Self: Sized,
1004 {
1005 if serialized.is_2d {
1006 Ok(Self::new(
1007 owner,
1008 true,
1009 Transform3D::new(
1010 serialized.matrix.m11,
1011 serialized.matrix.m12,
1012 0.0,
1013 0.0,
1014 serialized.matrix.m21,
1015 serialized.matrix.m22,
1016 0.0,
1017 0.0,
1018 0.0,
1019 0.0,
1020 1.0,
1021 0.0,
1022 serialized.matrix.m41,
1023 serialized.matrix.m42,
1024 0.0,
1025 1.0,
1026 ),
1027 can_gc,
1028 ))
1029 } else {
1030 Ok(Self::new(owner, false, serialized.matrix, can_gc))
1031 }
1032 }
1033
1034 fn serialized_storage<'a>(
1035 data: StructuredData<'a, '_>,
1036 ) -> &'a mut Option<FxHashMap<DomMatrixId, Self::Data>> {
1037 match data {
1038 StructuredData::Reader(reader) => &mut reader.matrices,
1039 StructuredData::Writer(writer) => &mut writer.matrices,
1040 }
1041 }
1042}
1043
1044pub(crate) fn entries_to_matrix(entries: &[f64]) -> Fallible<(bool, Transform3D<f64>)> {
1046 if let Ok(array) = entries.try_into() {
1047 Ok((true, Transform2D::from_array(array).to_3d()))
1048 } else if let Ok(array) = entries.try_into() {
1049 Ok((false, Transform3D::from_array(array)))
1050 } else {
1051 let err_msg = format!("Expected 6 or 16 entries, but found {}.", entries.len());
1052 Err(error::Error::Type(err_msg.to_owned()))
1053 }
1054}
1055
1056fn validate_and_fixup_2d(dict: &DOMMatrix2DInit) -> Fallible<Transform2D<f64>> {
1058 let same_value_zero = |x: f64, y: f64| -> bool { x.is_nan() && y.is_nan() || x == y };
1060
1061 if dict.a.is_some() &&
1064 dict.m11.is_some() &&
1065 !same_value_zero(dict.a.unwrap(), dict.m11.unwrap()) ||
1066 dict.b.is_some() &&
1067 dict.m12.is_some() &&
1068 !same_value_zero(dict.b.unwrap(), dict.m12.unwrap()) ||
1069 dict.c.is_some() &&
1070 dict.m21.is_some() &&
1071 !same_value_zero(dict.c.unwrap(), dict.m21.unwrap()) ||
1072 dict.d.is_some() &&
1073 dict.m22.is_some() &&
1074 !same_value_zero(dict.d.unwrap(), dict.m22.unwrap()) ||
1075 dict.e.is_some() &&
1076 dict.m41.is_some() &&
1077 !same_value_zero(dict.e.unwrap(), dict.m41.unwrap()) ||
1078 dict.f.is_some() &&
1079 dict.m42.is_some() &&
1080 !same_value_zero(dict.f.unwrap(), dict.m42.unwrap())
1081 {
1082 return Err(error::Error::Type(
1083 "Property mismatch on matrix initialization.".to_owned(),
1084 ));
1085 }
1086
1087 let m11 = dict.m11.unwrap_or(dict.a.unwrap_or(1.0));
1090
1091 let m12 = dict.m12.unwrap_or(dict.b.unwrap_or(0.0));
1094
1095 let m21 = dict.m21.unwrap_or(dict.c.unwrap_or(0.0));
1098
1099 let m22 = dict.m22.unwrap_or(dict.d.unwrap_or(1.0));
1102
1103 let m41 = dict.m41.unwrap_or(dict.e.unwrap_or(0.0));
1106
1107 let m42 = dict.m42.unwrap_or(dict.f.unwrap_or(0.0));
1110
1111 Ok(Transform2D::new(m11, m12, m21, m22, m41, m42))
1112}
1113
1114fn validate_and_fixup(dict: &DOMMatrixInit) -> Fallible<(bool, Transform3D<f64>)> {
1116 let transform2d = validate_and_fixup_2d(&dict.parent)?;
1118
1119 if dict.is2D == Some(true) &&
1124 (dict.m13 != 0.0 ||
1125 dict.m14 != 0.0 ||
1126 dict.m23 != 0.0 ||
1127 dict.m24 != 0.0 ||
1128 dict.m31 != 0.0 ||
1129 dict.m32 != 0.0 ||
1130 dict.m34 != 0.0 ||
1131 dict.m43 != 0.0 ||
1132 dict.m33 != 1.0 ||
1133 dict.m44 != 1.0)
1134 {
1135 return Err(error::Error::Type(
1136 "The is2D member is set to true but the input matrix is a 3d matrix.".to_owned(),
1137 ));
1138 }
1139
1140 let mut is_2d = dict.is2D;
1141
1142 if is_2d.is_none() &&
1147 (dict.m13 != 0.0 ||
1148 dict.m14 != 0.0 ||
1149 dict.m23 != 0.0 ||
1150 dict.m24 != 0.0 ||
1151 dict.m31 != 0.0 ||
1152 dict.m32 != 0.0 ||
1153 dict.m34 != 0.0 ||
1154 dict.m43 != 0.0 ||
1155 dict.m33 != 1.0 ||
1156 dict.m44 != 1.0)
1157 {
1158 is_2d = Some(false);
1159 }
1160
1161 let is_2d = is_2d.unwrap_or(true);
1163
1164 let mut transform = transform2d.to_3d();
1165 transform.m13 = dict.m13;
1166 transform.m14 = dict.m14;
1167 transform.m23 = dict.m23;
1168 transform.m24 = dict.m24;
1169 transform.m31 = dict.m31;
1170 transform.m32 = dict.m32;
1171 transform.m33 = dict.m33;
1172 transform.m34 = dict.m34;
1173 transform.m43 = dict.m43;
1174 transform.m44 = dict.m44;
1175
1176 Ok((is_2d, transform))
1177}
1178
1179pub(crate) fn dommatrix2dinit_to_matrix(dict: &DOMMatrix2DInit) -> Fallible<Transform2D<f64>> {
1181 validate_and_fixup_2d(dict)
1187}
1188
1189pub(crate) fn dommatrixinit_to_matrix(dict: &DOMMatrixInit) -> Fallible<(bool, Transform3D<f64>)> {
1191 validate_and_fixup(dict)
1197}
1198
1199#[inline]
1200fn normalize_point(x: f64, y: f64, z: f64) -> (f64, f64, f64) {
1201 let len = (x * x + y * y + z * z).sqrt();
1202 if len == 0.0 {
1203 (0.0, 0.0, 0.0)
1204 } else {
1205 (x / len, y / len, z / len)
1206 }
1207}
1208
1209pub(crate) fn transform_to_matrix(value: String) -> Fallible<(bool, Transform3D<f64>)> {
1210 use style::properties::longhands::transform;
1211
1212 let mut input = ParserInput::new(&value);
1213 let mut parser = Parser::new(&mut input);
1214 let url_data = Url::parse("about:blank").unwrap().into();
1215 let context = ParserContext::new(
1216 ::style::stylesheets::Origin::Author,
1217 &url_data,
1218 Some(::style::stylesheets::CssRuleType::Style),
1219 ::style_traits::ParsingMode::DEFAULT,
1220 ::style::context::QuirksMode::NoQuirks,
1221 Default::default(),
1222 None,
1223 None,
1224 );
1225
1226 let transform = match parser.parse_entirely(|t| transform::parse(&context, t)) {
1227 Ok(result) => result,
1228 Err(..) => return Err(error::Error::Syntax(None)),
1229 };
1230
1231 let (m, is_3d) = match transform.to_transform_3d_matrix_f64(None) {
1232 Ok(result) => result,
1233 Err(..) => return Err(error::Error::Syntax(None)),
1234 };
1235
1236 Ok((!is_3d, m))
1237}