script/
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 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    // Get the browsing context for a pipeline that may exist in another
64    // script thread.  If the browsing context already exists in the
65    // `window_proxies` map, we return it, otherwise we recursively
66    // get the browsing context for the parent if there is one,
67    // construct a new dissimilar-origin browsing context, add it
68    // to the `window_proxies` map, and return it.
69    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    // Get the browsing context for a pipeline that exists in this
109    // script thread.  If the browsing context already exists in the
110    // `window_proxies` map, we return it, otherwise we recursively
111    // get the browsing context for the parent if there is one,
112    // construct a new similar-origin browsing context, add it
113    // to the `window_proxies` map, and return it.
114    #[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            // Note: we do not set the window to be the currently-active one,
128            // this will be done instead when the script-thread handles the `SetDocumentActivity` msg.
129            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}