1use dom_struct::dom_struct;
6use euclid::default::Transform3D;
7use js::context::{JSContext, NoGC};
8use js::rust::{CustomAutoRooterGuard, HandleObject};
9use js::typedarray::{Float32Array, Float64Array};
10use rustc_hash::FxHashMap;
11use script_bindings::reflector::reflect_dom_object_with_proto_and_cx;
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;
30
31#[dom_struct]
32pub(crate) struct DOMMatrix {
33 parent: DOMMatrixReadOnly,
34}
35
36#[expect(non_snake_case)]
37impl DOMMatrix {
38 pub(crate) fn new(
39 cx: &mut JSContext,
40 global: &GlobalScope,
41 is2D: bool,
42 matrix: Transform3D<f64>,
43 ) -> DomRoot<Self> {
44 Self::new_with_proto(cx, global, None, is2D, matrix)
45 }
46
47 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
48 fn new_with_proto(
49 cx: &mut JSContext,
50 global: &GlobalScope,
51 proto: Option<HandleObject>,
52 is2D: bool,
53 matrix: Transform3D<f64>,
54 ) -> DomRoot<Self> {
55 let dommatrix = Self::new_inherited(is2D, matrix);
56 reflect_dom_object_with_proto_and_cx(Box::new(dommatrix), global, proto, cx)
57 }
58
59 pub(crate) fn new_inherited(is2D: bool, matrix: Transform3D<f64>) -> Self {
60 DOMMatrix {
61 parent: DOMMatrixReadOnly::new_inherited(is2D, matrix),
62 }
63 }
64
65 pub(crate) fn from_readonly(
66 global: &GlobalScope,
67 ro: &DOMMatrixReadOnly,
68 cx: &mut JSContext,
69 ) -> DomRoot<Self> {
70 Self::new(cx, global, ro.is2D(), *ro.matrix())
71 }
72}
73
74#[expect(non_snake_case)]
75impl DOMMatrixMethods<crate::DomTypeHolder> for DOMMatrix {
76 fn Constructor(
78 cx: &mut JSContext,
79 global: &GlobalScope,
80 proto: Option<HandleObject>,
81 init: Option<StringOrUnrestrictedDoubleSequence>,
82 ) -> Fallible<DomRoot<Self>> {
83 if init.is_none() {
84 return Ok(Self::new_with_proto(
85 cx,
86 global,
87 proto,
88 true,
89 Transform3D::identity(),
90 ));
91 }
92 match init.unwrap() {
93 StringOrUnrestrictedDoubleSequence::String(ref s) => {
94 if !global.is::<Window>() {
95 return Err(error::Error::Type(
96 c"String constructor is only supported in the main thread.".to_owned(),
97 ));
98 }
99 if s.is_empty() {
100 return Ok(Self::new(cx, global, true, Transform3D::identity()));
101 }
102 transform_to_matrix(&s.str())
103 .map(|(is2D, matrix)| Self::new_with_proto(cx, global, proto, is2D, matrix))
104 },
105 StringOrUnrestrictedDoubleSequence::UnrestrictedDoubleSequence(ref entries) => {
106 entries_to_matrix(&entries[..])
107 .map(|(is2D, matrix)| Self::new_with_proto(cx, global, proto, is2D, matrix))
108 },
109 }
110 }
111
112 fn FromMatrix(
114 cx: &mut js::context::JSContext,
115 global: &GlobalScope,
116 other: &DOMMatrixInit,
117 ) -> Fallible<DomRoot<Self>> {
118 dommatrixinit_to_matrix(other).map(|(is2D, matrix)| Self::new(cx, global, is2D, matrix))
119 }
120
121 fn FromFloat32Array(
123 cx: &mut js::context::JSContext,
124 global: &GlobalScope,
125 array: CustomAutoRooterGuard<Float32Array>,
126 ) -> Fallible<DomRoot<DOMMatrix>> {
127 let vec: Vec<f64> = array
128 .to_vec()
129 .unwrap_or_default()
130 .iter()
131 .map(|&x| x as f64)
132 .collect();
133 DOMMatrix::Constructor(
134 cx,
135 global,
136 None,
137 Some(StringOrUnrestrictedDoubleSequence::UnrestrictedDoubleSequence(vec)),
138 )
139 }
140
141 fn FromFloat64Array(
143 cx: &mut js::context::JSContext,
144 global: &GlobalScope,
145 array: CustomAutoRooterGuard<Float64Array>,
146 ) -> Fallible<DomRoot<DOMMatrix>> {
147 let vec: Vec<f64> = array.to_vec().unwrap_or_default();
148 DOMMatrix::Constructor(
149 cx,
150 global,
151 None,
152 Some(StringOrUnrestrictedDoubleSequence::UnrestrictedDoubleSequence(vec)),
153 )
154 }
155
156 fn M11(&self) -> f64 {
158 self.upcast::<DOMMatrixReadOnly>().M11()
159 }
160
161 fn SetM11(&self, value: f64) {
163 self.upcast::<DOMMatrixReadOnly>().set_m11(value);
164 }
165
166 fn M12(&self) -> f64 {
168 self.upcast::<DOMMatrixReadOnly>().M12()
169 }
170
171 fn SetM12(&self, value: f64) {
173 self.upcast::<DOMMatrixReadOnly>().set_m12(value);
174 }
175
176 fn M13(&self) -> f64 {
178 self.upcast::<DOMMatrixReadOnly>().M13()
179 }
180
181 fn SetM13(&self, value: f64) {
183 self.upcast::<DOMMatrixReadOnly>().set_m13(value);
184 }
185
186 fn M14(&self) -> f64 {
188 self.upcast::<DOMMatrixReadOnly>().M14()
189 }
190
191 fn SetM14(&self, value: f64) {
193 self.upcast::<DOMMatrixReadOnly>().set_m14(value);
194 }
195
196 fn M21(&self) -> f64 {
198 self.upcast::<DOMMatrixReadOnly>().M21()
199 }
200
201 fn SetM21(&self, value: f64) {
203 self.upcast::<DOMMatrixReadOnly>().set_m21(value);
204 }
205
206 fn M22(&self) -> f64 {
208 self.upcast::<DOMMatrixReadOnly>().M22()
209 }
210
211 fn SetM22(&self, value: f64) {
213 self.upcast::<DOMMatrixReadOnly>().set_m22(value);
214 }
215
216 fn M23(&self) -> f64 {
218 self.upcast::<DOMMatrixReadOnly>().M23()
219 }
220
221 fn SetM23(&self, value: f64) {
223 self.upcast::<DOMMatrixReadOnly>().set_m23(value);
224 }
225
226 fn M24(&self) -> f64 {
228 self.upcast::<DOMMatrixReadOnly>().M24()
229 }
230
231 fn SetM24(&self, value: f64) {
233 self.upcast::<DOMMatrixReadOnly>().set_m24(value);
234 }
235
236 fn M31(&self) -> f64 {
238 self.upcast::<DOMMatrixReadOnly>().M31()
239 }
240
241 fn SetM31(&self, value: f64) {
243 self.upcast::<DOMMatrixReadOnly>().set_m31(value);
244 }
245
246 fn M32(&self) -> f64 {
248 self.upcast::<DOMMatrixReadOnly>().M32()
249 }
250
251 fn SetM32(&self, value: f64) {
253 self.upcast::<DOMMatrixReadOnly>().set_m32(value);
254 }
255
256 fn M33(&self) -> f64 {
258 self.upcast::<DOMMatrixReadOnly>().M33()
259 }
260
261 fn SetM33(&self, value: f64) {
263 self.upcast::<DOMMatrixReadOnly>().set_m33(value);
264 }
265
266 fn M34(&self) -> f64 {
268 self.upcast::<DOMMatrixReadOnly>().M34()
269 }
270
271 fn SetM34(&self, value: f64) {
273 self.upcast::<DOMMatrixReadOnly>().set_m34(value);
274 }
275
276 fn M41(&self) -> f64 {
278 self.upcast::<DOMMatrixReadOnly>().M41()
279 }
280
281 fn SetM41(&self, value: f64) {
283 self.upcast::<DOMMatrixReadOnly>().set_m41(value);
284 }
285
286 fn M42(&self) -> f64 {
288 self.upcast::<DOMMatrixReadOnly>().M42()
289 }
290
291 fn SetM42(&self, value: f64) {
293 self.upcast::<DOMMatrixReadOnly>().set_m42(value);
294 }
295
296 fn M43(&self) -> f64 {
298 self.upcast::<DOMMatrixReadOnly>().M43()
299 }
300
301 fn SetM43(&self, value: f64) {
303 self.upcast::<DOMMatrixReadOnly>().set_m43(value);
304 }
305
306 fn M44(&self) -> f64 {
308 self.upcast::<DOMMatrixReadOnly>().M44()
309 }
310
311 fn SetM44(&self, value: f64) {
313 self.upcast::<DOMMatrixReadOnly>().set_m44(value);
314 }
315
316 fn A(&self) -> f64 {
318 self.upcast::<DOMMatrixReadOnly>().A()
319 }
320
321 fn SetA(&self, value: f64) {
323 self.upcast::<DOMMatrixReadOnly>().set_m11(value);
324 }
325
326 fn B(&self) -> f64 {
328 self.upcast::<DOMMatrixReadOnly>().B()
329 }
330
331 fn SetB(&self, value: f64) {
333 self.upcast::<DOMMatrixReadOnly>().set_m12(value);
334 }
335
336 fn C(&self) -> f64 {
338 self.upcast::<DOMMatrixReadOnly>().C()
339 }
340
341 fn SetC(&self, value: f64) {
343 self.upcast::<DOMMatrixReadOnly>().set_m21(value);
344 }
345
346 fn D(&self) -> f64 {
348 self.upcast::<DOMMatrixReadOnly>().D()
349 }
350
351 fn SetD(&self, value: f64) {
353 self.upcast::<DOMMatrixReadOnly>().set_m22(value);
354 }
355
356 fn E(&self) -> f64 {
358 self.upcast::<DOMMatrixReadOnly>().E()
359 }
360
361 fn SetE(&self, value: f64) {
363 self.upcast::<DOMMatrixReadOnly>().set_m41(value);
364 }
365
366 fn F(&self) -> f64 {
368 self.upcast::<DOMMatrixReadOnly>().F()
369 }
370
371 fn SetF(&self, value: f64) {
373 self.upcast::<DOMMatrixReadOnly>().set_m42(value);
374 }
375
376 fn MultiplySelf(&self, other: &DOMMatrixInit) -> Fallible<DomRoot<DOMMatrix>> {
378 self.upcast::<DOMMatrixReadOnly>()
380 .multiply_self(other)
381 .and(Ok(DomRoot::from_ref(self)))
383 }
384
385 fn PreMultiplySelf(&self, other: &DOMMatrixInit) -> Fallible<DomRoot<DOMMatrix>> {
387 self.upcast::<DOMMatrixReadOnly>()
389 .pre_multiply_self(other)
390 .and(Ok(DomRoot::from_ref(self)))
392 }
393
394 fn TranslateSelf(&self, tx: f64, ty: f64, tz: f64) -> DomRoot<DOMMatrix> {
396 self.upcast::<DOMMatrixReadOnly>()
398 .translate_self(tx, ty, tz);
399 DomRoot::from_ref(self)
401 }
402
403 fn ScaleSelf(
405 &self,
406 scaleX: f64,
407 scaleY: Option<f64>,
408 scaleZ: f64,
409 originX: f64,
410 originY: f64,
411 originZ: f64,
412 ) -> DomRoot<DOMMatrix> {
413 self.upcast::<DOMMatrixReadOnly>()
415 .scale_self(scaleX, scaleY, scaleZ, originX, originY, originZ);
416 DomRoot::from_ref(self)
418 }
419
420 fn Scale3dSelf(
422 &self,
423 scale: f64,
424 originX: f64,
425 originY: f64,
426 originZ: f64,
427 ) -> DomRoot<DOMMatrix> {
428 self.upcast::<DOMMatrixReadOnly>()
430 .scale_3d_self(scale, originX, originY, originZ);
431 DomRoot::from_ref(self)
433 }
434
435 fn RotateSelf(&self, rotX: f64, rotY: Option<f64>, rotZ: Option<f64>) -> DomRoot<DOMMatrix> {
437 self.upcast::<DOMMatrixReadOnly>()
439 .rotate_self(rotX, rotY, rotZ);
440 DomRoot::from_ref(self)
442 }
443
444 fn RotateFromVectorSelf(&self, x: f64, y: f64) -> DomRoot<DOMMatrix> {
446 self.upcast::<DOMMatrixReadOnly>()
448 .rotate_from_vector_self(x, y);
449 DomRoot::from_ref(self)
451 }
452
453 fn RotateAxisAngleSelf(&self, x: f64, y: f64, z: f64, angle: f64) -> DomRoot<DOMMatrix> {
455 self.upcast::<DOMMatrixReadOnly>()
457 .rotate_axis_angle_self(x, y, z, angle);
458 DomRoot::from_ref(self)
460 }
461
462 fn SkewXSelf(&self, sx: f64) -> DomRoot<DOMMatrix> {
464 self.upcast::<DOMMatrixReadOnly>().skew_x_self(sx);
466 DomRoot::from_ref(self)
468 }
469
470 fn SkewYSelf(&self, sy: f64) -> DomRoot<DOMMatrix> {
472 self.upcast::<DOMMatrixReadOnly>().skew_y_self(sy);
474 DomRoot::from_ref(self)
476 }
477
478 fn InvertSelf(&self) -> DomRoot<DOMMatrix> {
480 self.upcast::<DOMMatrixReadOnly>().invert_self();
482 DomRoot::from_ref(self)
484 }
485
486 fn SetMatrixValue(&self, transformList: DOMString) -> Fallible<DomRoot<DOMMatrix>> {
488 match transform_to_matrix(&transformList.str()) {
492 Ok(tuple) => {
493 self.parent.set_is2D(tuple.0);
495 self.parent.set_matrix(tuple.1);
497 },
498 Err(error) => return Err(error),
499 }
500
501 Ok(DomRoot::from_ref(self))
503 }
504}
505
506impl Serializable for DOMMatrix {
507 type Index = DomMatrixIndex;
508 type Data = DomMatrix;
509
510 fn serialize(&self, _no_gc: &NoGC) -> Result<(DomMatrixId, Self::Data), ()> {
511 let serialized = if self.parent.is2D() {
512 DomMatrix {
513 matrix: Transform3D::new(
514 self.M11(),
515 self.M12(),
516 f64::NAN,
517 f64::NAN,
518 self.M21(),
519 self.M22(),
520 f64::NAN,
521 f64::NAN,
522 f64::NAN,
523 f64::NAN,
524 f64::NAN,
525 f64::NAN,
526 self.M41(),
527 self.M42(),
528 f64::NAN,
529 f64::NAN,
530 ),
531 is_2d: true,
532 }
533 } else {
534 DomMatrix {
535 matrix: *self.parent.matrix(),
536 is_2d: false,
537 }
538 };
539 Ok((DomMatrixId::new(), serialized))
540 }
541
542 fn deserialize(
543 cx: &mut JSContext,
544 owner: &GlobalScope,
545 serialized: Self::Data,
546 ) -> Result<DomRoot<Self>, ()>
547 where
548 Self: Sized,
549 {
550 if serialized.is_2d {
551 Ok(Self::new(
552 cx,
553 owner,
554 true,
555 Transform3D::new(
556 serialized.matrix.m11,
557 serialized.matrix.m12,
558 0.0,
559 0.0,
560 serialized.matrix.m21,
561 serialized.matrix.m22,
562 0.0,
563 0.0,
564 0.0,
565 0.0,
566 1.0,
567 0.0,
568 serialized.matrix.m41,
569 serialized.matrix.m42,
570 0.0,
571 1.0,
572 ),
573 ))
574 } else {
575 Ok(Self::new(cx, owner, false, serialized.matrix))
576 }
577 }
578
579 fn serialized_storage<'a>(
580 data: StructuredData<'a, '_>,
581 ) -> &'a mut Option<FxHashMap<DomMatrixId, Self::Data>> {
582 match data {
583 StructuredData::Reader(reader) => &mut reader.matrices,
584 StructuredData::Writer(writer) => &mut writer.matrices,
585 }
586 }
587}