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