1use dom_struct::dom_struct;
6use euclid::default::Transform3D;
7use js::context::JSContext;
8use js::rust::{CustomAutoRooterGuard, HandleObject};
9use js::typedarray::{Float32Array, Float64Array};
10use rustc_hash::FxHashMap;
11use script_bindings::reflector::reflect_dom_object_with_proto;
12use script_bindings::str::DOMString;
13use servo_base::id::{DomMatrixId, DomMatrixIndex};
14use servo_constellation_traits::DomMatrix;
15
16use crate::dom::bindings::codegen::Bindings::DOMMatrixBinding::{DOMMatrixInit, DOMMatrixMethods};
17use crate::dom::bindings::codegen::Bindings::DOMMatrixReadOnlyBinding::DOMMatrixReadOnlyMethods;
18use crate::dom::bindings::codegen::UnionTypes::StringOrUnrestrictedDoubleSequence;
19use crate::dom::bindings::error;
20use crate::dom::bindings::error::Fallible;
21use crate::dom::bindings::inheritance::Castable;
22use crate::dom::bindings::root::DomRoot;
23use crate::dom::bindings::serializable::Serializable;
24use crate::dom::bindings::structuredclone::StructuredData;
25use crate::dom::dommatrixreadonly::{
26 DOMMatrixReadOnly, dommatrixinit_to_matrix, entries_to_matrix, transform_to_matrix,
27};
28use crate::dom::globalscope::GlobalScope;
29use crate::dom::window::Window;
30use crate::script_runtime::CanGc;
31
32#[dom_struct]
33pub(crate) struct DOMMatrix {
34 parent: DOMMatrixReadOnly,
35}
36
37#[expect(non_snake_case)]
38impl DOMMatrix {
39 pub(crate) fn new(
40 global: &GlobalScope,
41 is2D: bool,
42 matrix: Transform3D<f64>,
43 can_gc: CanGc,
44 ) -> DomRoot<Self> {
45 Self::new_with_proto(global, None, is2D, matrix, can_gc)
46 }
47
48 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
49 fn new_with_proto(
50 global: &GlobalScope,
51 proto: Option<HandleObject>,
52 is2D: bool,
53 matrix: Transform3D<f64>,
54 can_gc: CanGc,
55 ) -> DomRoot<Self> {
56 let dommatrix = Self::new_inherited(is2D, matrix);
57 reflect_dom_object_with_proto(Box::new(dommatrix), global, proto, can_gc)
58 }
59
60 pub(crate) fn new_inherited(is2D: bool, matrix: Transform3D<f64>) -> Self {
61 DOMMatrix {
62 parent: DOMMatrixReadOnly::new_inherited(is2D, matrix),
63 }
64 }
65
66 pub(crate) fn from_readonly(
67 global: &GlobalScope,
68 ro: &DOMMatrixReadOnly,
69 can_gc: CanGc,
70 ) -> DomRoot<Self> {
71 Self::new(global, ro.is2D(), *ro.matrix(), can_gc)
72 }
73}
74
75#[expect(non_snake_case)]
76impl DOMMatrixMethods<crate::DomTypeHolder> for DOMMatrix {
77 fn Constructor(
79 global: &GlobalScope,
80 proto: Option<HandleObject>,
81 can_gc: CanGc,
82 init: Option<StringOrUnrestrictedDoubleSequence>,
83 ) -> Fallible<DomRoot<Self>> {
84 if init.is_none() {
85 return Ok(Self::new_with_proto(
86 global,
87 proto,
88 true,
89 Transform3D::identity(),
90 can_gc,
91 ));
92 }
93 match init.unwrap() {
94 StringOrUnrestrictedDoubleSequence::String(ref s) => {
95 if !global.is::<Window>() {
96 return Err(error::Error::Type(
97 c"String constructor is only supported in the main thread.".to_owned(),
98 ));
99 }
100 if s.is_empty() {
101 return Ok(Self::new(global, true, Transform3D::identity(), can_gc));
102 }
103 transform_to_matrix(&s.str())
104 .map(|(is2D, matrix)| Self::new_with_proto(global, proto, is2D, matrix, can_gc))
105 },
106 StringOrUnrestrictedDoubleSequence::UnrestrictedDoubleSequence(ref entries) => {
107 entries_to_matrix(&entries[..])
108 .map(|(is2D, matrix)| Self::new_with_proto(global, proto, is2D, matrix, can_gc))
109 },
110 }
111 }
112
113 fn FromMatrix(
115 global: &GlobalScope,
116 other: &DOMMatrixInit,
117 can_gc: CanGc,
118 ) -> Fallible<DomRoot<Self>> {
119 dommatrixinit_to_matrix(other).map(|(is2D, matrix)| Self::new(global, is2D, matrix, can_gc))
120 }
121
122 fn FromFloat32Array(
124 global: &GlobalScope,
125 array: CustomAutoRooterGuard<Float32Array>,
126 can_gc: CanGc,
127 ) -> Fallible<DomRoot<DOMMatrix>> {
128 let vec: Vec<f64> = array.to_vec().iter().map(|&x| x as f64).collect();
129 DOMMatrix::Constructor(
130 global,
131 None,
132 can_gc,
133 Some(StringOrUnrestrictedDoubleSequence::UnrestrictedDoubleSequence(vec)),
134 )
135 }
136
137 fn FromFloat64Array(
139 global: &GlobalScope,
140 array: CustomAutoRooterGuard<Float64Array>,
141 can_gc: CanGc,
142 ) -> Fallible<DomRoot<DOMMatrix>> {
143 let vec: Vec<f64> = array.to_vec();
144 DOMMatrix::Constructor(
145 global,
146 None,
147 can_gc,
148 Some(StringOrUnrestrictedDoubleSequence::UnrestrictedDoubleSequence(vec)),
149 )
150 }
151
152 fn M11(&self) -> f64 {
154 self.upcast::<DOMMatrixReadOnly>().M11()
155 }
156
157 fn SetM11(&self, value: f64) {
159 self.upcast::<DOMMatrixReadOnly>().set_m11(value);
160 }
161
162 fn M12(&self) -> f64 {
164 self.upcast::<DOMMatrixReadOnly>().M12()
165 }
166
167 fn SetM12(&self, value: f64) {
169 self.upcast::<DOMMatrixReadOnly>().set_m12(value);
170 }
171
172 fn M13(&self) -> f64 {
174 self.upcast::<DOMMatrixReadOnly>().M13()
175 }
176
177 fn SetM13(&self, value: f64) {
179 self.upcast::<DOMMatrixReadOnly>().set_m13(value);
180 }
181
182 fn M14(&self) -> f64 {
184 self.upcast::<DOMMatrixReadOnly>().M14()
185 }
186
187 fn SetM14(&self, value: f64) {
189 self.upcast::<DOMMatrixReadOnly>().set_m14(value);
190 }
191
192 fn M21(&self) -> f64 {
194 self.upcast::<DOMMatrixReadOnly>().M21()
195 }
196
197 fn SetM21(&self, value: f64) {
199 self.upcast::<DOMMatrixReadOnly>().set_m21(value);
200 }
201
202 fn M22(&self) -> f64 {
204 self.upcast::<DOMMatrixReadOnly>().M22()
205 }
206
207 fn SetM22(&self, value: f64) {
209 self.upcast::<DOMMatrixReadOnly>().set_m22(value);
210 }
211
212 fn M23(&self) -> f64 {
214 self.upcast::<DOMMatrixReadOnly>().M23()
215 }
216
217 fn SetM23(&self, value: f64) {
219 self.upcast::<DOMMatrixReadOnly>().set_m23(value);
220 }
221
222 fn M24(&self) -> f64 {
224 self.upcast::<DOMMatrixReadOnly>().M24()
225 }
226
227 fn SetM24(&self, value: f64) {
229 self.upcast::<DOMMatrixReadOnly>().set_m24(value);
230 }
231
232 fn M31(&self) -> f64 {
234 self.upcast::<DOMMatrixReadOnly>().M31()
235 }
236
237 fn SetM31(&self, value: f64) {
239 self.upcast::<DOMMatrixReadOnly>().set_m31(value);
240 }
241
242 fn M32(&self) -> f64 {
244 self.upcast::<DOMMatrixReadOnly>().M32()
245 }
246
247 fn SetM32(&self, value: f64) {
249 self.upcast::<DOMMatrixReadOnly>().set_m32(value);
250 }
251
252 fn M33(&self) -> f64 {
254 self.upcast::<DOMMatrixReadOnly>().M33()
255 }
256
257 fn SetM33(&self, value: f64) {
259 self.upcast::<DOMMatrixReadOnly>().set_m33(value);
260 }
261
262 fn M34(&self) -> f64 {
264 self.upcast::<DOMMatrixReadOnly>().M34()
265 }
266
267 fn SetM34(&self, value: f64) {
269 self.upcast::<DOMMatrixReadOnly>().set_m34(value);
270 }
271
272 fn M41(&self) -> f64 {
274 self.upcast::<DOMMatrixReadOnly>().M41()
275 }
276
277 fn SetM41(&self, value: f64) {
279 self.upcast::<DOMMatrixReadOnly>().set_m41(value);
280 }
281
282 fn M42(&self) -> f64 {
284 self.upcast::<DOMMatrixReadOnly>().M42()
285 }
286
287 fn SetM42(&self, value: f64) {
289 self.upcast::<DOMMatrixReadOnly>().set_m42(value);
290 }
291
292 fn M43(&self) -> f64 {
294 self.upcast::<DOMMatrixReadOnly>().M43()
295 }
296
297 fn SetM43(&self, value: f64) {
299 self.upcast::<DOMMatrixReadOnly>().set_m43(value);
300 }
301
302 fn M44(&self) -> f64 {
304 self.upcast::<DOMMatrixReadOnly>().M44()
305 }
306
307 fn SetM44(&self, value: f64) {
309 self.upcast::<DOMMatrixReadOnly>().set_m44(value);
310 }
311
312 fn A(&self) -> f64 {
314 self.upcast::<DOMMatrixReadOnly>().A()
315 }
316
317 fn SetA(&self, value: f64) {
319 self.upcast::<DOMMatrixReadOnly>().set_m11(value);
320 }
321
322 fn B(&self) -> f64 {
324 self.upcast::<DOMMatrixReadOnly>().B()
325 }
326
327 fn SetB(&self, value: f64) {
329 self.upcast::<DOMMatrixReadOnly>().set_m12(value);
330 }
331
332 fn C(&self) -> f64 {
334 self.upcast::<DOMMatrixReadOnly>().C()
335 }
336
337 fn SetC(&self, value: f64) {
339 self.upcast::<DOMMatrixReadOnly>().set_m21(value);
340 }
341
342 fn D(&self) -> f64 {
344 self.upcast::<DOMMatrixReadOnly>().D()
345 }
346
347 fn SetD(&self, value: f64) {
349 self.upcast::<DOMMatrixReadOnly>().set_m22(value);
350 }
351
352 fn E(&self) -> f64 {
354 self.upcast::<DOMMatrixReadOnly>().E()
355 }
356
357 fn SetE(&self, value: f64) {
359 self.upcast::<DOMMatrixReadOnly>().set_m41(value);
360 }
361
362 fn F(&self) -> f64 {
364 self.upcast::<DOMMatrixReadOnly>().F()
365 }
366
367 fn SetF(&self, value: f64) {
369 self.upcast::<DOMMatrixReadOnly>().set_m42(value);
370 }
371
372 fn MultiplySelf(&self, other: &DOMMatrixInit) -> Fallible<DomRoot<DOMMatrix>> {
374 self.upcast::<DOMMatrixReadOnly>()
376 .multiply_self(other)
377 .and(Ok(DomRoot::from_ref(self)))
379 }
380
381 fn PreMultiplySelf(&self, other: &DOMMatrixInit) -> Fallible<DomRoot<DOMMatrix>> {
383 self.upcast::<DOMMatrixReadOnly>()
385 .pre_multiply_self(other)
386 .and(Ok(DomRoot::from_ref(self)))
388 }
389
390 fn TranslateSelf(&self, tx: f64, ty: f64, tz: f64) -> DomRoot<DOMMatrix> {
392 self.upcast::<DOMMatrixReadOnly>()
394 .translate_self(tx, ty, tz);
395 DomRoot::from_ref(self)
397 }
398
399 fn ScaleSelf(
401 &self,
402 scaleX: f64,
403 scaleY: Option<f64>,
404 scaleZ: f64,
405 originX: f64,
406 originY: f64,
407 originZ: f64,
408 ) -> DomRoot<DOMMatrix> {
409 self.upcast::<DOMMatrixReadOnly>()
411 .scale_self(scaleX, scaleY, scaleZ, originX, originY, originZ);
412 DomRoot::from_ref(self)
414 }
415
416 fn Scale3dSelf(
418 &self,
419 scale: f64,
420 originX: f64,
421 originY: f64,
422 originZ: f64,
423 ) -> DomRoot<DOMMatrix> {
424 self.upcast::<DOMMatrixReadOnly>()
426 .scale_3d_self(scale, originX, originY, originZ);
427 DomRoot::from_ref(self)
429 }
430
431 fn RotateSelf(&self, rotX: f64, rotY: Option<f64>, rotZ: Option<f64>) -> DomRoot<DOMMatrix> {
433 self.upcast::<DOMMatrixReadOnly>()
435 .rotate_self(rotX, rotY, rotZ);
436 DomRoot::from_ref(self)
438 }
439
440 fn RotateFromVectorSelf(&self, x: f64, y: f64) -> DomRoot<DOMMatrix> {
442 self.upcast::<DOMMatrixReadOnly>()
444 .rotate_from_vector_self(x, y);
445 DomRoot::from_ref(self)
447 }
448
449 fn RotateAxisAngleSelf(&self, x: f64, y: f64, z: f64, angle: f64) -> DomRoot<DOMMatrix> {
451 self.upcast::<DOMMatrixReadOnly>()
453 .rotate_axis_angle_self(x, y, z, angle);
454 DomRoot::from_ref(self)
456 }
457
458 fn SkewXSelf(&self, sx: f64) -> DomRoot<DOMMatrix> {
460 self.upcast::<DOMMatrixReadOnly>().skew_x_self(sx);
462 DomRoot::from_ref(self)
464 }
465
466 fn SkewYSelf(&self, sy: f64) -> DomRoot<DOMMatrix> {
468 self.upcast::<DOMMatrixReadOnly>().skew_y_self(sy);
470 DomRoot::from_ref(self)
472 }
473
474 fn InvertSelf(&self) -> DomRoot<DOMMatrix> {
476 self.upcast::<DOMMatrixReadOnly>().invert_self();
478 DomRoot::from_ref(self)
480 }
481
482 fn SetMatrixValue(&self, transformList: DOMString) -> Fallible<DomRoot<DOMMatrix>> {
484 match transform_to_matrix(&transformList.str()) {
488 Ok(tuple) => {
489 self.parent.set_is2D(tuple.0);
491 self.parent.set_matrix(tuple.1);
493 },
494 Err(error) => return Err(error),
495 }
496
497 Ok(DomRoot::from_ref(self))
499 }
500}
501
502impl Serializable for DOMMatrix {
503 type Index = DomMatrixIndex;
504 type Data = DomMatrix;
505
506 fn serialize(&self) -> Result<(DomMatrixId, Self::Data), ()> {
507 let serialized = if self.parent.is2D() {
508 DomMatrix {
509 matrix: Transform3D::new(
510 self.M11(),
511 self.M12(),
512 f64::NAN,
513 f64::NAN,
514 self.M21(),
515 self.M22(),
516 f64::NAN,
517 f64::NAN,
518 f64::NAN,
519 f64::NAN,
520 f64::NAN,
521 f64::NAN,
522 self.M41(),
523 self.M42(),
524 f64::NAN,
525 f64::NAN,
526 ),
527 is_2d: true,
528 }
529 } else {
530 DomMatrix {
531 matrix: *self.parent.matrix(),
532 is_2d: false,
533 }
534 };
535 Ok((DomMatrixId::new(), serialized))
536 }
537
538 fn deserialize(
539 cx: &mut JSContext,
540 owner: &GlobalScope,
541 serialized: Self::Data,
542 ) -> Result<DomRoot<Self>, ()>
543 where
544 Self: Sized,
545 {
546 if serialized.is_2d {
547 Ok(Self::new(
548 owner,
549 true,
550 Transform3D::new(
551 serialized.matrix.m11,
552 serialized.matrix.m12,
553 0.0,
554 0.0,
555 serialized.matrix.m21,
556 serialized.matrix.m22,
557 0.0,
558 0.0,
559 0.0,
560 0.0,
561 1.0,
562 0.0,
563 serialized.matrix.m41,
564 serialized.matrix.m42,
565 0.0,
566 1.0,
567 ),
568 CanGc::from_cx(cx),
569 ))
570 } else {
571 Ok(Self::new(
572 owner,
573 false,
574 serialized.matrix,
575 CanGc::from_cx(cx),
576 ))
577 }
578 }
579
580 fn serialized_storage<'a>(
581 data: StructuredData<'a, '_>,
582 ) -> &'a mut Option<FxHashMap<DomMatrixId, Self::Data>> {
583 match data {
584 StructuredData::Reader(reader) => &mut reader.matrices,
585 StructuredData::Writer(writer) => &mut writer.matrices,
586 }
587 }
588}