Skip to main content

script/dom/
domrectreadonly.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 std::cell::Cell;
6
7use dom_struct::dom_struct;
8use js::context::JSContext;
9use js::rust::HandleObject;
10use rustc_hash::FxHashMap;
11use script_bindings::reflector::{
12    Reflector, reflect_dom_object_with_cx, reflect_dom_object_with_proto_and_cx,
13};
14use servo_base::id::{DomRectId, DomRectIndex};
15use servo_constellation_traits::DomRect;
16
17use crate::dom::bindings::codegen::Bindings::DOMRectReadOnlyBinding::{
18    DOMRectInit, DOMRectReadOnlyMethods,
19};
20use crate::dom::bindings::error::Fallible;
21use crate::dom::bindings::root::DomRoot;
22use crate::dom::bindings::serializable::Serializable;
23use crate::dom::bindings::structuredclone::StructuredData;
24use crate::dom::globalscope::GlobalScope;
25use crate::script_runtime::CanGc;
26
27#[dom_struct]
28pub(crate) struct DOMRectReadOnly {
29    reflector_: Reflector,
30    x: Cell<f64>,
31    y: Cell<f64>,
32    width: Cell<f64>,
33    height: Cell<f64>,
34}
35
36impl DOMRectReadOnly {
37    pub(crate) fn new_inherited(x: f64, y: f64, width: f64, height: f64) -> DOMRectReadOnly {
38        DOMRectReadOnly {
39            x: Cell::new(x),
40            y: Cell::new(y),
41            width: Cell::new(width),
42            height: Cell::new(height),
43            reflector_: Reflector::new(),
44        }
45    }
46
47    pub(crate) fn new(
48        cx: &mut JSContext,
49        global: &GlobalScope,
50        proto: Option<HandleObject>,
51        x: f64,
52        y: f64,
53        width: f64,
54        height: f64,
55    ) -> DomRoot<DOMRectReadOnly> {
56        reflect_dom_object_with_proto_and_cx(
57            Box::new(DOMRectReadOnly::new_inherited(x, y, width, height)),
58            global,
59            proto,
60            cx,
61        )
62    }
63
64    pub(crate) fn new_from_dictionary(
65        cx: &mut JSContext,
66        global: &GlobalScope,
67        proto: Option<HandleObject>,
68        dictionary: &DOMRectInit,
69    ) -> DomRoot<DOMRectReadOnly> {
70        reflect_dom_object_with_proto_and_cx(
71            Box::new(create_a_domrectreadonly_from_the_dictionary(dictionary)),
72            global,
73            proto,
74            cx,
75        )
76    }
77
78    pub(crate) fn set_x(&self, value: f64) {
79        self.x.set(value);
80    }
81
82    pub(crate) fn set_y(&self, value: f64) {
83        self.y.set(value);
84    }
85
86    pub(crate) fn set_width(&self, value: f64) {
87        self.width.set(value);
88    }
89
90    pub(crate) fn set_height(&self, value: f64) {
91        self.height.set(value);
92    }
93}
94
95impl DOMRectReadOnlyMethods<crate::DomTypeHolder> for DOMRectReadOnly {
96    /// <https://drafts.fxtf.org/geometry/#dom-domrectreadonly-domrectreadonly>
97    fn Constructor(
98        cx: &mut JSContext,
99        global: &GlobalScope,
100        proto: Option<HandleObject>,
101        x: f64,
102        y: f64,
103        width: f64,
104        height: f64,
105    ) -> Fallible<DomRoot<DOMRectReadOnly>> {
106        Ok(DOMRectReadOnly::new(cx, global, proto, x, y, width, height))
107    }
108
109    // https://drafts.fxtf.org/geometry/#dom-domrectreadonly-fromrect
110    #[cfg_attr(crown, expect(crown::unrooted_must_root))]
111    fn FromRect(
112        cx: &mut JSContext,
113        global: &GlobalScope,
114        other: &DOMRectInit,
115    ) -> DomRoot<DOMRectReadOnly> {
116        let dom_rect = create_a_domrectreadonly_from_the_dictionary(other);
117
118        reflect_dom_object_with_cx(Box::new(dom_rect), global, cx)
119    }
120
121    /// <https://drafts.fxtf.org/geometry/#dom-domrectreadonly-x>
122    fn X(&self) -> f64 {
123        self.x.get()
124    }
125
126    /// <https://drafts.fxtf.org/geometry/#dom-domrectreadonly-y>
127    fn Y(&self) -> f64 {
128        self.y.get()
129    }
130
131    /// <https://drafts.fxtf.org/geometry/#dom-domrectreadonly-width>
132    fn Width(&self) -> f64 {
133        self.width.get()
134    }
135
136    /// <https://drafts.fxtf.org/geometry/#dom-domrectreadonly-height>
137    fn Height(&self) -> f64 {
138        self.height.get()
139    }
140
141    /// <https://drafts.fxtf.org/geometry/#dom-domrectreadonly-top>
142    fn Top(&self) -> f64 {
143        let height = self.height.get();
144        if height >= 0f64 {
145            self.y.get()
146        } else {
147            self.y.get() + height
148        }
149    }
150
151    /// <https://drafts.fxtf.org/geometry/#dom-domrectreadonly-right>
152    fn Right(&self) -> f64 {
153        let width = self.width.get();
154        if width < 0f64 {
155            self.x.get()
156        } else {
157            self.x.get() + width
158        }
159    }
160
161    /// <https://drafts.fxtf.org/geometry/#dom-domrectreadonly-bottom>
162    fn Bottom(&self) -> f64 {
163        let height = self.height.get();
164        if height < 0f64 {
165            self.y.get()
166        } else {
167            self.y.get() + height
168        }
169    }
170
171    /// <https://drafts.fxtf.org/geometry/#dom-domrectreadonly-left>
172    fn Left(&self) -> f64 {
173        let width = self.width.get();
174        if width >= 0f64 {
175            self.x.get()
176        } else {
177            self.x.get() + width
178        }
179    }
180}
181
182/// <https://drafts.fxtf.org/geometry/#ref-for-create-a-domrectreadonly-from-the-dictionary>
183pub(super) fn create_a_domrectreadonly_from_the_dictionary(other: &DOMRectInit) -> DOMRectReadOnly {
184    // NOTE: We trivially combine all three steps into one
185
186    // Step 1. Let rect be a new DOMRectReadOnly or DOMRect as appropriate.
187
188    // Step 2. Set rect’s variables x coordinate to other’s x dictionary member, y coordinate to other’s y
189    // dictionary member, width dimension to other’s width dictionary member and height dimension to
190    // other’s height dictionary member.
191
192    // Step 3. Return rect.
193
194    DOMRectReadOnly {
195        reflector_: Reflector::new(),
196        x: Cell::new(other.x),
197        y: Cell::new(other.y),
198        width: Cell::new(other.width),
199        height: Cell::new(other.height),
200    }
201}
202
203type Type = DomRectId;
204
205impl Serializable for DOMRectReadOnly {
206    type Index = DomRectIndex;
207    type Data = DomRect;
208
209    fn serialize(&self) -> Result<(DomRectId, Self::Data), ()> {
210        let serialized = DomRect {
211            x: self.X(),
212            y: self.Y(),
213            width: self.Width(),
214            height: self.Height(),
215        };
216        Ok((DomRectId::new(), serialized))
217    }
218
219    #[expect(unsafe_code)]
220    fn deserialize(
221        owner: &GlobalScope,
222        serialized: Self::Data,
223        _can_gc: CanGc,
224    ) -> Result<DomRoot<Self>, ()>
225    where
226        Self: Sized,
227    {
228        // TODO: https://github.com/servo/servo/issues/44588
229        let mut cx = unsafe { script_bindings::script_runtime::temp_cx() };
230        Ok(Self::new(
231            &mut cx,
232            owner,
233            None,
234            serialized.x,
235            serialized.y,
236            serialized.width,
237            serialized.height,
238        ))
239    }
240
241    fn serialized_storage<'a>(
242        data: StructuredData<'a, '_>,
243    ) -> &'a mut Option<FxHashMap<Type, Self::Data>> {
244        match data {
245            StructuredData::Reader(reader) => &mut reader.rects,
246            StructuredData::Writer(writer) => &mut writer.rects,
247        }
248    }
249}