script/dom/document/
iframe_collection.rs1use std::cell::Ref;
6use std::default::Default;
7use std::iter;
8
9use embedder_traits::ViewportDetails;
10use js::context::{JSContext, NoGC};
11use layout_api::IFrameSizes;
12use paint_api::PinchZoomInfos;
13use script_bindings::cell::DomRefCell;
14use servo_base::id::BrowsingContextId;
15use servo_constellation_traits::{IFrameSizeMsg, ScriptToConstellationMessage, WindowSizeType};
16
17use crate::dom::NodeTraits;
18use crate::dom::bindings::inheritance::Castable;
19use crate::dom::bindings::root::{Dom, DomRoot};
20use crate::dom::html::htmliframeelement::HTMLIFrameElement;
21use crate::dom::iterators::ShadowIncluding;
22use crate::dom::node::Node;
23use crate::dom::types::Window;
24use crate::event_loop::script_thread::with_script_thread;
25
26#[derive(JSTraceable, MallocSizeOf)]
27#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
28pub(crate) struct IFrame {
29 pub(crate) element: Dom<HTMLIFrameElement>,
30 #[no_trace]
31 pub(crate) size: Option<ViewportDetails>,
32}
33
34#[derive(Default, JSTraceable, MallocSizeOf)]
35#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
36pub(crate) struct IFrameCollection {
37 iframes: DomRefCell<Vec<IFrame>>,
40 iframes_in_insertion_order: DomRefCell<Vec<Dom<HTMLIFrameElement>>>,
43}
44
45impl IFrameCollection {
46 pub(crate) fn new() -> Self {
47 Self {
48 iframes: Default::default(),
49 iframes_in_insertion_order: Default::default(),
50 }
51 }
52
53 pub(crate) fn add(&self, no_gc: &NoGC, iframe_element: &HTMLIFrameElement) {
54 let iframe_node = iframe_element.upcast::<Node>();
55
56 let size = self.remove(no_gc, iframe_element);
60
61 let insertion_index = iframe_node
66 .following_nodes(
67 iframe_element.owner_document().upcast::<Node>(),
68 ShadowIncluding::Yes,
69 )
70 .find_map(DomRoot::downcast::<HTMLIFrameElement>)
71 .and_then(|following_iframe| {
72 self.iframes
73 .borrow()
74 .iter()
75 .position(|iframe| *iframe.element == *following_iframe)
76 })
77 .unwrap_or(self.iframes.borrow().len());
78
79 self.iframes.safe_borrow_mut(no_gc).insert(
80 insertion_index,
81 IFrame {
82 element: Dom::from_ref(iframe_element),
83 size,
84 },
85 );
86
87 self.iframes_in_insertion_order
88 .safe_borrow_mut(no_gc)
89 .push(Dom::from_ref(iframe_element));
90 }
91
92 pub(crate) fn remove(
93 &self,
94 no_gc: &NoGC,
95 iframe_element: &HTMLIFrameElement,
96 ) -> Option<ViewportDetails> {
97 self.iframes_in_insertion_order
98 .safe_borrow_mut(no_gc)
99 .retain(|iframe| *iframe != iframe_element);
100 let mut iframes = self.iframes.safe_borrow_mut(no_gc);
101 iframes
102 .iter()
103 .position(|iframe| &*iframe.element == iframe_element)
104 .and_then(|index| iframes.remove(index).size)
105 }
106
107 pub(crate) fn at_insertion_index(&self, index: usize) -> Option<BrowsingContextId> {
111 self.iframes_in_insertion_order
112 .borrow()
113 .iter()
114 .filter_map(|iframe| iframe.browsing_context_id())
115 .nth(index)
116 }
117
118 pub(crate) fn active_iframe_count(&self) -> usize {
121 self.iframes_in_insertion_order
122 .borrow()
123 .iter()
124 .filter(|iframe| iframe.browsing_context_id().is_some())
125 .count()
126 }
127
128 pub(crate) fn element(
130 &self,
131 browsing_context_id: BrowsingContextId,
132 ) -> Option<DomRoot<HTMLIFrameElement>> {
133 self.iframes
134 .borrow()
135 .iter()
136 .find(|iframe| iframe.element.browsing_context_id() == Some(browsing_context_id))
137 .map(|iframe| iframe.element.as_rooted())
138 }
139
140 pub(crate) fn viewport_details(
142 &self,
143 browsing_context_id: BrowsingContextId,
144 ) -> Option<ViewportDetails> {
145 self.iframes
146 .borrow()
147 .iter()
148 .find(|iframe| iframe.element.browsing_context_id() == Some(browsing_context_id))
149 .and_then(|iframe| iframe.size)
150 }
151
152 fn set_viewport_details(
155 &self,
156 no_gc: &NoGC,
157 browsing_context_id: BrowsingContextId,
158 new_size: ViewportDetails,
159 ) -> Option<ViewportDetails> {
160 self.iframes
163 .safe_borrow_mut(no_gc)
164 .iter_mut()
165 .find(|iframe| iframe.element.browsing_context_id() == Some(browsing_context_id))
166 .and_then(|iframe| iframe.size.replace(new_size))
167 }
168
169 pub(crate) fn handle_new_iframe_sizes_after_layout(
173 &self,
174 cx: &mut JSContext,
175 window: &Window,
176 new_iframe_sizes: IFrameSizes,
177 ) {
178 if new_iframe_sizes.is_empty() {
179 return;
180 }
181
182 let size_messages: Vec<_> = new_iframe_sizes
183 .into_iter()
184 .filter_map(|(browsing_context_id, iframe_size)| {
185 let viewport_details = iframe_size.viewport_details;
189 with_script_thread(|script_thread| {
190 script_thread.handle_resize_message(
191 iframe_size.pipeline_id,
192 viewport_details,
193 WindowSizeType::Resize,
194 );
195
196 script_thread.handle_update_pinch_zoom_infos(
200 cx,
201 iframe_size.pipeline_id,
202 PinchZoomInfos::new_from_viewport_size(viewport_details.size),
203 )
204 });
205
206 let old_viewport_details =
207 self.set_viewport_details(cx.no_gc(), browsing_context_id, viewport_details);
208 if old_viewport_details == Some(viewport_details) {
211 return None;
212 }
213
214 let size_type = match old_viewport_details {
215 Some(_) => WindowSizeType::Resize,
216 None => WindowSizeType::Initial,
217 };
218
219 Some(IFrameSizeMsg {
220 browsing_context_id,
221 size: viewport_details,
222 type_: size_type,
223 })
224 })
225 .collect();
226
227 if !size_messages.is_empty() {
228 window.send_to_constellation(ScriptToConstellationMessage::IFrameSizes(size_messages));
229 }
230 }
231
232 pub(crate) fn iter(&self) -> impl Iterator<Item = DomRoot<HTMLIFrameElement>> + use<'_> {
233 let mut items = Some(Ref::map(self.iframes.borrow(), |vec| &vec[..]));
234 iter::from_fn(move || {
235 let mut item = None;
236 let rest = Ref::map(items.take()?, |items| {
237 let mut iter = items.iter();
238 item = iter.next().map(|item| item.element.as_rooted());
239 iter.as_slice()
240 });
241 if item.is_some() {
242 items = Some(rest);
243 }
244 item
245 })
246 }
247}