1use std::rc::Rc;
6
7use app_units::Au;
8use dom_struct::dom_struct;
9use euclid::default::Rect;
10use js::rust::HandleObject;
11
12use crate::dom::bindings::callback::ExceptionHandling;
13use crate::dom::bindings::cell::DomRefCell;
14use crate::dom::bindings::codegen::Bindings::ResizeObserverBinding::{
15 ResizeObserverBoxOptions, ResizeObserverCallback, ResizeObserverMethods, ResizeObserverOptions,
16};
17use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
18use crate::dom::bindings::inheritance::Castable;
19use crate::dom::bindings::reflector::{Reflector, reflect_dom_object_with_proto};
20use crate::dom::bindings::root::{Dom, DomRoot};
21use crate::dom::domrectreadonly::DOMRectReadOnly;
22use crate::dom::element::Element;
23use crate::dom::node::{Node, NodeTraits};
24use crate::dom::resizeobserverentry::ResizeObserverEntry;
25use crate::dom::resizeobserversize::{ResizeObserverSize, ResizeObserverSizeImpl};
26use crate::dom::window::Window;
27use crate::script_runtime::CanGc;
28
29#[derive(Debug, Default, PartialEq, PartialOrd)]
31pub(crate) struct ResizeObservationDepth(usize);
32
33impl ResizeObservationDepth {
34 pub(crate) fn max() -> ResizeObservationDepth {
35 ResizeObservationDepth(usize::MAX)
36 }
37}
38
39#[dom_struct]
42pub(crate) struct ResizeObserver {
43 reflector_: Reflector,
44
45 #[ignore_malloc_size_of = "Rc are hard"]
47 callback: Rc<ResizeObserverCallback>,
48
49 observation_targets: DomRefCell<Vec<(ResizeObservation, Dom<Element>)>>,
56}
57
58impl ResizeObserver {
59 pub(crate) fn new_inherited(callback: Rc<ResizeObserverCallback>) -> ResizeObserver {
60 ResizeObserver {
61 reflector_: Reflector::new(),
62 callback,
63 observation_targets: Default::default(),
64 }
65 }
66
67 fn new(
68 window: &Window,
69 proto: Option<HandleObject>,
70 callback: Rc<ResizeObserverCallback>,
71 can_gc: CanGc,
72 ) -> DomRoot<ResizeObserver> {
73 let observer = Box::new(ResizeObserver::new_inherited(callback));
74 reflect_dom_object_with_proto(observer, window, proto, can_gc)
75 }
76
77 pub(crate) fn gather_active_resize_observations_at_depth(
81 &self,
82 depth: &ResizeObservationDepth,
83 has_active: &mut bool,
84 ) {
85 for (observation, target) in self.observation_targets.borrow_mut().iter_mut() {
90 observation.state = Default::default();
91
92 if let Some(size) = observation.is_active(target) {
94 let target_depth = calculate_depth_for_node(target);
96
97 if target_depth > *depth {
99 observation.state = ObservationState::Active(size);
100 *has_active = true;
101 }
102 else {
104 observation.state = ObservationState::Skipped;
105 }
106 }
107 }
108 }
109
110 pub(crate) fn broadcast_active_resize_observations(
112 &self,
113 shallowest_target_depth: &mut ResizeObservationDepth,
114 can_gc: CanGc,
115 ) {
116 let mut has_active_observation_targets = false;
121
122 let mut entries: Vec<DomRoot<ResizeObserverEntry>> = Default::default();
124
125 for (observation, target) in self.observation_targets.borrow_mut().iter_mut() {
127 let box_size = {
128 let ObservationState::Active(box_size) = observation.state else {
129 continue;
130 };
131 box_size
132 };
133 has_active_observation_targets = true;
134
135 let width = box_size.width().to_f64_px();
139 let height = box_size.height().to_f64_px();
140 let size_impl = ResizeObserverSizeImpl::new(width, height);
141 let window = target.owner_window();
142 let observer_size = ResizeObserverSize::new(&window, size_impl, can_gc);
143
144 let content_rect = DOMRectReadOnly::new(
146 window.upcast(),
147 None,
148 box_size.origin.x.to_f64_px(),
149 box_size.origin.y.to_f64_px(),
150 width,
151 height,
152 can_gc,
153 );
154 let entry = ResizeObserverEntry::new(
155 &window,
156 target,
157 &content_rect,
158 &[],
159 &[&*observer_size],
160 &[],
161 can_gc,
162 );
163 entries.push(entry);
164
165 observation.last_reported_sizes[0] = size_impl;
170 observation.state = ObservationState::Done;
171 let target_depth = calculate_depth_for_node(target);
172 if target_depth < *shallowest_target_depth {
173 *shallowest_target_depth = target_depth;
174 }
175 }
176
177 if !has_active_observation_targets {
178 return;
179 }
180
181 let _ = self
183 .callback
184 .Call_(self, entries, self, ExceptionHandling::Report, can_gc);
185
186 }
189
190 pub(crate) fn has_skipped_resize_observations(&self) -> bool {
192 self.observation_targets
193 .borrow()
194 .iter()
195 .any(|(observation, _)| observation.state == ObservationState::Skipped)
196 }
197}
198
199impl ResizeObserverMethods<crate::DomTypeHolder> for ResizeObserver {
200 fn Constructor(
202 window: &Window,
203 proto: Option<HandleObject>,
204 can_gc: CanGc,
205 callback: Rc<ResizeObserverCallback>,
206 ) -> DomRoot<ResizeObserver> {
207 let rooted_observer = ResizeObserver::new(window, proto, callback, can_gc);
208 let document = window.Document();
209 document.add_resize_observer(&rooted_observer);
210 rooted_observer
211 }
212
213 fn Observe(&self, target: &Element, options: &ResizeObserverOptions) {
215 let is_present = self
216 .observation_targets
217 .borrow()
218 .iter()
219 .any(|(_obs, other)| &**other == target);
220 if is_present {
221 self.Unobserve(target);
222 }
223
224 let resize_observation = ResizeObservation::new(options.box_);
225
226 self.observation_targets
227 .borrow_mut()
228 .push((resize_observation, Dom::from_ref(target)));
229 target
230 .owner_window()
231 .Document()
232 .set_resize_observer_started_observing_target(true);
233 }
234
235 fn Unobserve(&self, target: &Element) {
237 self.observation_targets
238 .borrow_mut()
239 .retain_mut(|(_obs, other)| !(&**other == target));
240 }
241
242 fn Disconnect(&self) {
244 self.observation_targets.borrow_mut().clear();
245 }
246}
247
248#[derive(Default, MallocSizeOf, PartialEq)]
250enum ObservationState {
251 #[default]
252 Done,
253 Active(Rect<Au>),
257 Skipped,
259}
260
261#[derive(JSTraceable, MallocSizeOf)]
266struct ResizeObservation {
267 observed_box: ResizeObserverBoxOptions,
269 last_reported_sizes: Vec<ResizeObserverSizeImpl>,
271 #[no_trace]
273 state: ObservationState,
274}
275
276impl ResizeObservation {
277 pub(crate) fn new(observed_box: ResizeObserverBoxOptions) -> ResizeObservation {
279 let size_impl = ResizeObserverSizeImpl::new(0.0, 0.0);
280 ResizeObservation {
281 observed_box,
282 last_reported_sizes: vec![size_impl],
283 state: Default::default(),
284 }
285 }
286
287 fn is_active(&self, target: &Element) -> Option<Rect<Au>> {
291 let last_reported_size = self.last_reported_sizes[0];
292 let box_size = calculate_box_size(target, &self.observed_box);
293 let is_active = box_size.width().to_f64_px() != last_reported_size.inline_size() ||
294 box_size.height().to_f64_px() != last_reported_size.block_size();
295 if is_active { Some(box_size) } else { None }
296 }
297}
298
299fn calculate_depth_for_node(target: &Element) -> ResizeObservationDepth {
301 let node = target.upcast::<Node>();
302 let depth = node.inclusive_ancestors_in_flat_tree().count() - 1;
303 ResizeObservationDepth(depth)
304}
305
306fn calculate_box_size(target: &Element, observed_box: &ResizeObserverBoxOptions) -> Rect<Au> {
308 match observed_box {
309 ResizeObserverBoxOptions::Content_box => {
310 target
313 .upcast::<Node>()
314 .border_boxes()
315 .pop()
316 .unwrap_or_else(Rect::zero)
317 },
318 _ => Rect::zero(),
320 }
321}