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;
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(cx, Box::new(dommatrix), global, proto)
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 let (is_2d, matrix) = transform_to_matrix(&transformList.str())?;
492 self.parent.set_is2D(is_2d);
494 self.parent.set_matrix(matrix);
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, _no_gc: &NoGC) -> 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 cx,
549 owner,
550 true,
551 Transform3D::new(
552 serialized.matrix.m11,
553 serialized.matrix.m12,
554 0.0,
555 0.0,
556 serialized.matrix.m21,
557 serialized.matrix.m22,
558 0.0,
559 0.0,
560 0.0,
561 0.0,
562 1.0,
563 0.0,
564 serialized.matrix.m41,
565 serialized.matrix.m42,
566 0.0,
567 1.0,
568 ),
569 ))
570 } else {
571 Ok(Self::new(cx, owner, false, serialized.matrix))
572 }
573 }
574
575 fn serialized_storage<'a>(
576 data: StructuredData<'a, '_>,
577 ) -> &'a mut Option<FxHashMap<DomMatrixId, Self::Data>> {
578 match data {
579 StructuredData::Reader(reader) => &mut reader.matrices,
580 StructuredData::Writer(writer) => &mut writer.matrices,
581 }
582 }
583}