Skip to main content

script/event_loop/
script_window_proxies.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 rustc_hash::FxBuildHasher;
6use script_bindings::cell::DomRefCell;
7use script_bindings::inheritance::Castable;
8use script_bindings::root::{Dom, DomRoot};
9use servo_base::generic_channel;
10use servo_base::id::{BrowsingContextId, PipelineId, WebViewId};
11use servo_constellation_traits::ScriptToConstellationMessage;
12
13use crate::dom::bindings::trace::HashMapTracedValues;
14use crate::dom::html::htmliframeelement::HTMLIFrameElement;
15use crate::dom::node::NodeTraits;
16use crate::dom::types::{GlobalScope, Window};
17use crate::dom::windowproxy::{CreatorBrowsingContextInfo, WindowProxy};
18use crate::messaging::ScriptThreadSenders;
19
20#[derive(JSTraceable, Default, MallocSizeOf)]
21#[cfg_attr(crown, crown::unrooted_must_root_lint::allow_unrooted_in_rc)]
22#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
23pub(crate) struct ScriptWindowProxies {
24    map: DomRefCell<HashMapTracedValues<BrowsingContextId, Dom<WindowProxy>, FxBuildHasher>>,
25}
26
27impl ScriptWindowProxies {
28    pub(crate) fn find_window_proxy(&self, id: BrowsingContextId) -> Option<DomRoot<WindowProxy>> {
29        self.map
30            .borrow()
31            .get(&id)
32            .map(|context| DomRoot::from_ref(&**context))
33    }
34
35    pub(crate) fn top_level_window_proxies(&self) -> Vec<DomRoot<WindowProxy>> {
36        self.map
37            .borrow()
38            .values()
39            .filter(|proxy| proxy.parent().is_none())
40            .map(|proxy| proxy.as_rooted())
41            .collect()
42    }
43
44    pub(crate) fn insert(&self, id: BrowsingContextId, proxy: &WindowProxy) {
45        self.map.borrow_mut().insert(id, Dom::from_ref(proxy));
46    }
47
48    pub(crate) fn remove(&self, id: BrowsingContextId) {
49        self.map.borrow_mut().remove(&id);
50    }
51
52    // Get the browsing context for a pipeline that may exist in another
53    // script thread.  If the browsing context already exists in the
54    // `window_proxies` map, we return it, otherwise we recursively
55    // get the browsing context for the parent if there is one,
56    // construct a new dissimilar-origin browsing context, add it
57    // to the `window_proxies` map, and return it.
58    pub(crate) fn remote_window_proxy(
59        &self,
60        cx: &mut js::context::JSContext,
61        senders: &ScriptThreadSenders,
62        global_to_clone: &GlobalScope,
63        webview_id: WebViewId,
64        pipeline_id: PipelineId,
65        opener: Option<BrowsingContextId>,
66    ) -> Option<DomRoot<WindowProxy>> {
67        let (browsing_context_id, parent_pipeline_id) =
68            self.ask_constellation_for_browsing_context_info(senders, webview_id, pipeline_id)?;
69        if let Some(window_proxy) = self.find_window_proxy(browsing_context_id) {
70            return Some(window_proxy);
71        }
72
73        let parent_browsing_context = parent_pipeline_id.and_then(|parent_id| {
74            self.remote_window_proxy(cx, senders, global_to_clone, webview_id, parent_id, opener)
75        });
76
77        let opener_browsing_context = opener.and_then(|id| self.find_window_proxy(id));
78
79        let creator = CreatorBrowsingContextInfo::from(
80            parent_browsing_context.as_deref(),
81            opener_browsing_context.as_deref(),
82        );
83
84        let window_proxy = WindowProxy::new_dissimilar_origin(
85            cx,
86            global_to_clone,
87            browsing_context_id,
88            webview_id,
89            parent_browsing_context.as_deref(),
90            opener,
91            creator,
92        );
93        self.insert(browsing_context_id, &window_proxy);
94        Some(window_proxy)
95    }
96
97    // Get the browsing context for a pipeline that exists in this
98    // script thread.  If the browsing context already exists in the
99    // `window_proxies` map, we return it, otherwise we recursively
100    // get the browsing context for the parent if there is one,
101    // construct a new similar-origin browsing context, add it
102    // to the `window_proxies` map, and return it.
103    #[expect(clippy::too_many_arguments)]
104    pub(crate) fn local_window_proxy(
105        &self,
106        cx: &mut js::context::JSContext,
107        senders: &ScriptThreadSenders,
108        window: &Window,
109        browsing_context_id: BrowsingContextId,
110        webview_id: WebViewId,
111        parent_info: Option<PipelineId>,
112        iframe: Option<DomRoot<HTMLIFrameElement>>,
113        opener: Option<BrowsingContextId>,
114    ) -> DomRoot<WindowProxy> {
115        if let Some(window_proxy) = self.find_window_proxy(browsing_context_id) {
116            // Note: we do not set the window to be the currently-active one,
117            // this will be done instead when the script-thread handles the `SetDocumentActivity` msg.
118            return window_proxy;
119        }
120        let parent_browsing_context = match (parent_info, iframe.as_ref()) {
121            (_, Some(iframe)) => Some(iframe.owner_window().window_proxy()),
122            (Some(parent_id), _) => self.remote_window_proxy(
123                cx,
124                senders,
125                window.upcast(),
126                webview_id,
127                parent_id,
128                opener,
129            ),
130            _ => None,
131        };
132
133        let opener_browsing_context = opener.and_then(|id| self.find_window_proxy(id));
134
135        let creator = CreatorBrowsingContextInfo::from(
136            parent_browsing_context.as_deref(),
137            opener_browsing_context.as_deref(),
138        );
139
140        let window_proxy = WindowProxy::new(
141            cx,
142            window,
143            browsing_context_id,
144            webview_id,
145            iframe.as_deref().map(Castable::upcast),
146            parent_browsing_context.as_deref(),
147            opener,
148            creator,
149        );
150        self.insert(browsing_context_id, &window_proxy);
151        window_proxy
152    }
153
154    fn ask_constellation_for_browsing_context_info(
155        &self,
156        senders: &ScriptThreadSenders,
157        webview_id: WebViewId,
158        pipeline_id: PipelineId,
159    ) -> Option<(BrowsingContextId, Option<PipelineId>)> {
160        let (result_sender, result_receiver) = generic_channel::channel().unwrap();
161        let msg = ScriptToConstellationMessage::GetBrowsingContextInfo(pipeline_id, result_sender);
162        senders
163            .pipeline_to_constellation_sender
164            .send((webview_id, pipeline_id, msg))
165            .expect("Failed to send to constellation.");
166        result_receiver
167            .recv()
168            .expect("Failed to get browsing context info from constellation.")
169    }
170}