1use base::id::{BrowsingContextId, PipelineId, WebViewId};
6use constellation_traits::ScriptToConstellationMessage;
7use ipc_channel::ipc;
8use rustc_hash::FxBuildHasher;
9use script_bindings::inheritance::Castable;
10use script_bindings::root::{Dom, DomRoot};
11use script_bindings::str::DOMString;
12
13use crate::document_collection::DocumentCollection;
14use crate::dom::bindings::cell::DomRefCell;
15use crate::dom::bindings::trace::HashMapTracedValues;
16use crate::dom::node::NodeTraits;
17use crate::dom::types::{GlobalScope, Window};
18use crate::dom::windowproxy::{CreatorBrowsingContextInfo, WindowProxy};
19use crate::messaging::ScriptThreadSenders;
20
21#[derive(JSTraceable, Default, MallocSizeOf)]
22#[cfg_attr(crown, crown::unrooted_must_root_lint::allow_unrooted_in_rc)]
23#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
24pub(crate) struct ScriptWindowProxies {
25 map: DomRefCell<HashMapTracedValues<BrowsingContextId, Dom<WindowProxy>, FxBuildHasher>>,
26}
27
28impl ScriptWindowProxies {
29 pub(crate) fn find_window_proxy(&self, id: BrowsingContextId) -> Option<DomRoot<WindowProxy>> {
30 self.map
31 .borrow()
32 .get(&id)
33 .map(|context| DomRoot::from_ref(&**context))
34 }
35
36 pub(crate) fn find_window_proxy_by_name(
37 &self,
38 name: &DOMString,
39 ) -> Option<DomRoot<WindowProxy>> {
40 for (_, proxy) in self.map.borrow().iter() {
41 if proxy.get_name() == *name {
42 return Some(DomRoot::from_ref(&**proxy));
43 }
44 }
45 None
46 }
47
48 pub(crate) fn get(&self, id: BrowsingContextId) -> Option<DomRoot<WindowProxy>> {
49 self.map
50 .borrow()
51 .get(&id)
52 .map(|context| DomRoot::from_ref(&**context))
53 }
54
55 pub(crate) fn insert(&self, id: BrowsingContextId, proxy: DomRoot<WindowProxy>) {
56 self.map.borrow_mut().insert(id, Dom::from_ref(&*proxy));
57 }
58
59 pub(crate) fn remove(&self, id: BrowsingContextId) {
60 self.map.borrow_mut().remove(&id);
61 }
62
63 pub(crate) fn remote_window_proxy(
70 &self,
71 cx: &mut js::context::JSContext,
72 senders: &ScriptThreadSenders,
73 global_to_clone: &GlobalScope,
74 webview_id: WebViewId,
75 pipeline_id: PipelineId,
76 opener: Option<BrowsingContextId>,
77 ) -> Option<DomRoot<WindowProxy>> {
78 let (browsing_context_id, parent_pipeline_id) =
79 self.ask_constellation_for_browsing_context_info(senders, webview_id, pipeline_id)?;
80 if let Some(window_proxy) = self.get(browsing_context_id) {
81 return Some(window_proxy);
82 }
83
84 let parent_browsing_context = parent_pipeline_id.and_then(|parent_id| {
85 self.remote_window_proxy(cx, senders, global_to_clone, webview_id, parent_id, opener)
86 });
87
88 let opener_browsing_context = opener.and_then(|id| self.find_window_proxy(id));
89
90 let creator = CreatorBrowsingContextInfo::from(
91 parent_browsing_context.as_deref(),
92 opener_browsing_context.as_deref(),
93 );
94
95 let window_proxy = WindowProxy::new_dissimilar_origin(
96 cx,
97 global_to_clone,
98 browsing_context_id,
99 webview_id,
100 parent_browsing_context.as_deref(),
101 opener,
102 creator,
103 );
104 self.insert(browsing_context_id, DomRoot::from_ref(&*window_proxy));
105 Some(window_proxy)
106 }
107
108 #[expect(clippy::too_many_arguments)]
115 pub(crate) fn local_window_proxy(
116 &self,
117 cx: &mut js::context::JSContext,
118 senders: &ScriptThreadSenders,
119 documents: &DomRefCell<DocumentCollection>,
120 window: &Window,
121 browsing_context_id: BrowsingContextId,
122 webview_id: WebViewId,
123 parent_info: Option<PipelineId>,
124 opener: Option<BrowsingContextId>,
125 ) -> DomRoot<WindowProxy> {
126 if let Some(window_proxy) = self.get(browsing_context_id) {
127 return window_proxy;
130 }
131 let iframe = parent_info.and_then(|parent_id| {
132 documents
133 .borrow()
134 .find_iframe(parent_id, browsing_context_id)
135 });
136 let parent_browsing_context = match (parent_info, iframe.as_ref()) {
137 (_, Some(iframe)) => Some(iframe.owner_window().window_proxy()),
138 (Some(parent_id), _) => self.remote_window_proxy(
139 cx,
140 senders,
141 window.upcast(),
142 webview_id,
143 parent_id,
144 opener,
145 ),
146 _ => None,
147 };
148
149 let opener_browsing_context = opener.and_then(|id| self.find_window_proxy(id));
150
151 let creator = CreatorBrowsingContextInfo::from(
152 parent_browsing_context.as_deref(),
153 opener_browsing_context.as_deref(),
154 );
155
156 let window_proxy = WindowProxy::new(
157 window,
158 browsing_context_id,
159 webview_id,
160 iframe.as_deref().map(Castable::upcast),
161 parent_browsing_context.as_deref(),
162 opener,
163 creator,
164 );
165 self.insert(browsing_context_id, DomRoot::from_ref(&*window_proxy));
166 window_proxy
167 }
168
169 fn ask_constellation_for_browsing_context_info(
170 &self,
171 senders: &ScriptThreadSenders,
172 webview_id: WebViewId,
173 pipeline_id: PipelineId,
174 ) -> Option<(BrowsingContextId, Option<PipelineId>)> {
175 let (result_sender, result_receiver) = ipc::channel().unwrap();
176 let msg = ScriptToConstellationMessage::GetBrowsingContextInfo(pipeline_id, result_sender);
177 senders
178 .pipeline_to_constellation_sender
179 .send((webview_id, pipeline_id, msg))
180 .expect("Failed to send to constellation.");
181 result_receiver
182 .recv()
183 .expect("Failed to get browsing context info from constellation.")
184 }
185}