Skip to main content

script/dom/
domquad.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use dom_struct::dom_struct;
6use js::context::JSContext;
7use js::rust::HandleObject;
8use rustc_hash::FxHashMap;
9use script_bindings::reflector::{Reflector, reflect_dom_object_with_proto_and_cx};
10use servo_base::id::{DomQuadId, DomQuadIndex};
11use servo_constellation_traits::{DomPoint, DomQuad};
12
13use crate::dom::bindings::codegen::Bindings::DOMPointBinding::{DOMPointInit, DOMPointMethods};
14use crate::dom::bindings::codegen::Bindings::DOMQuadBinding::{DOMQuadInit, DOMQuadMethods};
15use crate::dom::bindings::codegen::Bindings::DOMRectReadOnlyBinding::DOMRectInit;
16use crate::dom::bindings::error::Fallible;
17use crate::dom::bindings::reflector::DomGlobal;
18use crate::dom::bindings::root::{Dom, DomRoot};
19use crate::dom::bindings::serializable::Serializable;
20use crate::dom::bindings::structuredclone::StructuredData;
21use crate::dom::dompoint::DOMPoint;
22use crate::dom::domrect::DOMRect;
23use crate::dom::globalscope::GlobalScope;
24use crate::script_runtime::CanGc;
25
26/// <https://drafts.fxtf.org/geometry/#DOMQuad>
27#[dom_struct]
28pub(crate) struct DOMQuad {
29    reflector_: Reflector,
30    p1: Dom<DOMPoint>,
31    p2: Dom<DOMPoint>,
32    p3: Dom<DOMPoint>,
33    p4: Dom<DOMPoint>,
34}
35
36impl DOMQuad {
37    fn new_inherited(p1: &DOMPoint, p2: &DOMPoint, p3: &DOMPoint, p4: &DOMPoint) -> DOMQuad {
38        DOMQuad {
39            reflector_: Reflector::new(),
40            p1: Dom::from_ref(p1),
41            p2: Dom::from_ref(p2),
42            p3: Dom::from_ref(p3),
43            p4: Dom::from_ref(p4),
44        }
45    }
46
47    pub(crate) fn new(
48        cx: &mut JSContext,
49        global: &GlobalScope,
50        p1: &DOMPoint,
51        p2: &DOMPoint,
52        p3: &DOMPoint,
53        p4: &DOMPoint,
54    ) -> DomRoot<DOMQuad> {
55        Self::new_with_proto(cx, global, None, p1, p2, p3, p4)
56    }
57
58    fn new_with_proto(
59        cx: &mut JSContext,
60        global: &GlobalScope,
61        proto: Option<HandleObject>,
62        p1: &DOMPoint,
63        p2: &DOMPoint,
64        p3: &DOMPoint,
65        p4: &DOMPoint,
66    ) -> DomRoot<DOMQuad> {
67        reflect_dom_object_with_proto_and_cx(
68            Box::new(DOMQuad::new_inherited(p1, p2, p3, p4)),
69            global,
70            proto,
71            cx,
72        )
73    }
74}
75
76impl DOMQuadMethods<crate::DomTypeHolder> for DOMQuad {
77    /// <https://drafts.fxtf.org/geometry/#dom-domquad-domquad>
78    fn Constructor(
79        cx: &mut JSContext,
80        global: &GlobalScope,
81        proto: Option<HandleObject>,
82        p1: &DOMPointInit,
83        p2: &DOMPointInit,
84        p3: &DOMPointInit,
85        p4: &DOMPointInit,
86    ) -> Fallible<DomRoot<DOMQuad>> {
87        let p1 = DOMPoint::new_from_init(cx, global, p1);
88        let p2 = DOMPoint::new_from_init(cx, global, p2);
89        let p3 = DOMPoint::new_from_init(cx, global, p3);
90        let p4 = DOMPoint::new_from_init(cx, global, p4);
91        Ok(DOMQuad::new_with_proto(
92            cx, global, proto, &p1, &p2, &p3, &p4,
93        ))
94    }
95
96    /// <https://drafts.fxtf.org/geometry/#dom-domquad-fromrect>
97    fn FromRect(cx: &mut JSContext, global: &GlobalScope, other: &DOMRectInit) -> DomRoot<DOMQuad> {
98        let p1 = DOMPoint::new(cx, global, other.x, other.y, 0f64, 1f64);
99        let p2 = DOMPoint::new(cx, global, other.x + other.width, other.y, 0f64, 1f64);
100        let p3 = DOMPoint::new(
101            cx,
102            global,
103            other.x + other.width,
104            other.y + other.height,
105            0f64,
106            1f64,
107        );
108        let p4 = DOMPoint::new(cx, global, other.x, other.y + other.height, 0f64, 1f64);
109        DOMQuad::new(cx, global, &p1, &p2, &p3, &p4)
110    }
111
112    /// <https://drafts.fxtf.org/geometry/#dom-domquad-fromquad>
113    fn FromQuad(cx: &mut JSContext, global: &GlobalScope, other: &DOMQuadInit) -> DomRoot<DOMQuad> {
114        let p1 = DOMPoint::new_from_init(cx, global, &other.p1);
115        let p2 = DOMPoint::new_from_init(cx, global, &other.p2);
116        let p3 = DOMPoint::new_from_init(cx, global, &other.p3);
117        let p4 = DOMPoint::new_from_init(cx, global, &other.p4);
118        DOMQuad::new(cx, global, &p1, &p2, &p3, &p4)
119    }
120
121    /// <https://drafts.fxtf.org/geometry/#dom-domquad-p1>
122    fn P1(&self) -> DomRoot<DOMPoint> {
123        DomRoot::from_ref(&self.p1)
124    }
125
126    /// <https://drafts.fxtf.org/geometry/#dom-domquad-p2>
127    fn P2(&self) -> DomRoot<DOMPoint> {
128        DomRoot::from_ref(&self.p2)
129    }
130
131    /// <https://drafts.fxtf.org/geometry/#dom-domquad-p3>
132    fn P3(&self) -> DomRoot<DOMPoint> {
133        DomRoot::from_ref(&self.p3)
134    }
135
136    /// <https://drafts.fxtf.org/geometry/#dom-domquad-p4>
137    fn P4(&self) -> DomRoot<DOMPoint> {
138        DomRoot::from_ref(&self.p4)
139    }
140
141    /// <https://drafts.fxtf.org/geometry/#dom-domquad-getbounds>
142    fn GetBounds(&self, cx: &mut JSContext) -> DomRoot<DOMRect> {
143        // https://drafts.fxtf.org/geometry/#nan-safe-minimum
144        let nan_safe_minimum = |a: f64, b: f64| {
145            if a.is_nan() || b.is_nan() {
146                f64::NAN
147            } else {
148                a.min(b)
149            }
150        };
151
152        // https://drafts.fxtf.org/geometry/#nan-safe-maximum
153        let nan_safe_maximum = |a: f64, b: f64| {
154            if a.is_nan() || b.is_nan() {
155                f64::NAN
156            } else {
157                a.max(b)
158            }
159        };
160
161        // Step 1. Let bounds be a DOMRect object.
162        // NOTE: We construct the object at the end
163
164        // Step 2. Let left be the NaN-safe minimum of point 1’s x coordinate,
165        // point 2’s x coordinate, point 3’s x coordinate and point 4’s x coordinate.
166        let left = nan_safe_minimum(
167            nan_safe_minimum(self.p1.X(), self.p2.X()),
168            nan_safe_minimum(self.p3.X(), self.p4.X()),
169        );
170
171        // Step 3. Let top be the NaN-safe minimum of point 1’s y coordinate,
172        // point 2’s y coordinate, point 3’s y coordinate and point 4’s y coordinate.
173        let top = nan_safe_minimum(
174            nan_safe_minimum(self.p1.Y(), self.p2.Y()),
175            nan_safe_minimum(self.p3.Y(), self.p4.Y()),
176        );
177
178        // Step 4. Let right be the NaN-safe maximum of point 1’s x coordinate,
179        // point 2’s x coordinate, point 3’s x coordinate and point 4’s x coordinate.
180        let right = nan_safe_maximum(
181            nan_safe_maximum(self.p1.X(), self.p2.X()),
182            nan_safe_maximum(self.p3.X(), self.p4.X()),
183        );
184
185        // Step 5. Let bottom be the NaN-safe maximum of point 1’s y coordinate,
186        // point 2’s y coordinate, point 3’s y coordinate and point 4’s y coordinate.
187        let bottom = nan_safe_maximum(
188            nan_safe_maximum(self.p1.Y(), self.p2.Y()),
189            nan_safe_maximum(self.p3.Y(), self.p4.Y()),
190        );
191
192        // Step 6. Set x coordinate of bounds to left, y coordinate of bounds to top,
193        // width dimension of bounds to right - left and height dimension of bounds to bottom - top.
194        // NOTE: Combined with Step 1.
195        DOMRect::new(
196            &self.global(),
197            left,
198            top,
199            right - left,
200            bottom - top,
201            CanGc::from_cx(cx),
202        )
203    }
204}
205
206impl Serializable for DOMQuad {
207    type Index = DomQuadIndex;
208    type Data = DomQuad;
209
210    fn serialize(&self) -> Result<(DomQuadId, Self::Data), ()> {
211        let make_point = |src: DomRoot<DOMPoint>| -> DomPoint {
212            DomPoint {
213                x: src.X(),
214                y: src.Y(),
215                z: src.Z(),
216                w: src.W(),
217            }
218        };
219        let serialized = DomQuad {
220            p1: make_point(self.P1()),
221            p2: make_point(self.P2()),
222            p3: make_point(self.P3()),
223            p4: make_point(self.P4()),
224        };
225        Ok((DomQuadId::new(), serialized))
226    }
227
228    #[expect(unsafe_code)]
229    fn deserialize(
230        owner: &GlobalScope,
231        serialized: Self::Data,
232        _can_gc: CanGc,
233    ) -> Result<DomRoot<Self>, ()>
234    where
235        Self: Sized,
236    {
237        // TODO: https://github.com/servo/servo/issues/44588
238        let mut cx = unsafe { script_bindings::script_runtime::temp_cx() };
239        let p1 = DOMPoint::new(
240            &mut cx,
241            owner,
242            serialized.p1.x,
243            serialized.p1.y,
244            serialized.p1.z,
245            serialized.p1.w,
246        );
247        let p2 = DOMPoint::new(
248            &mut cx,
249            owner,
250            serialized.p2.x,
251            serialized.p2.y,
252            serialized.p2.z,
253            serialized.p2.w,
254        );
255        let p3 = DOMPoint::new(
256            &mut cx,
257            owner,
258            serialized.p3.x,
259            serialized.p3.y,
260            serialized.p3.z,
261            serialized.p3.w,
262        );
263        let p4 = DOMPoint::new(
264            &mut cx,
265            owner,
266            serialized.p4.x,
267            serialized.p4.y,
268            serialized.p4.z,
269            serialized.p4.w,
270        );
271        Ok(Self::new(&mut cx, owner, &p1, &p2, &p3, &p4))
272    }
273
274    fn serialized_storage<'a>(
275        data: StructuredData<'a, '_>,
276    ) -> &'a mut Option<FxHashMap<DomQuadId, Self::Data>> {
277        match data {
278            StructuredData::Reader(reader) => &mut reader.quads,
279            StructuredData::Writer(writer) => &mut writer.quads,
280        }
281    }
282}