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    /// TODO: this map grows, but never shrinks. Issue #15258.
26    map: DomRefCell<HashMapTracedValues<BrowsingContextId, Dom<WindowProxy>, FxBuildHasher>>,
27}
28
29impl ScriptWindowProxies {
30    pub(crate) fn find_window_proxy(&self, id: BrowsingContextId) -> Option<DomRoot<WindowProxy>> {
31        self.map
32            .borrow()
33            .get(&id)
34            .map(|context| DomRoot::from_ref(&**context))
35    }
36
37    pub(crate) fn find_window_proxy_by_name(
38        &self,
39        name: &DOMString,
40    ) -> Option<DomRoot<WindowProxy>> {
41        for (_, proxy) in self.map.borrow().iter() {
42            if proxy.get_name() == *name {
43                return Some(DomRoot::from_ref(&**proxy));
44            }
45        }
46        None
47    }
48
49    pub(crate) fn get(&self, id: BrowsingContextId) -> Option<DomRoot<WindowProxy>> {
50        self.map
51            .borrow()
52            .get(&id)
53            .map(|context| DomRoot::from_ref(&**context))
54    }
55
56    pub(crate) fn insert(&self, id: BrowsingContextId, proxy: DomRoot<WindowProxy>) {
57        self.map.borrow_mut().insert(id, Dom::from_ref(&*proxy));
58    }
59
60    // Get the browsing context for a pipeline that may exist in another
61    // script thread.  If the browsing context already exists in the
62    // `window_proxies` map, we return it, otherwise we recursively
63    // get the browsing context for the parent if there is one,
64    // construct a new dissimilar-origin browsing context, add it
65    // to the `window_proxies` map, and return it.
66    pub(crate) fn remote_window_proxy(
67        &self,
68        senders: &ScriptThreadSenders,
69        global_to_clone: &GlobalScope,
70        webview_id: WebViewId,
71        pipeline_id: PipelineId,
72        opener: Option<BrowsingContextId>,
73    ) -> Option<DomRoot<WindowProxy>> {
74        let (browsing_context_id, parent_pipeline_id) =
75            self.ask_constellation_for_browsing_context_info(senders, pipeline_id)?;
76        if let Some(window_proxy) = self.get(browsing_context_id) {
77            return Some(window_proxy);
78        }
79
80        let parent_browsing_context = parent_pipeline_id.and_then(|parent_id| {
81            self.remote_window_proxy(senders, global_to_clone, webview_id, parent_id, opener)
82        });
83
84        let opener_browsing_context = opener.and_then(|id| self.find_window_proxy(id));
85
86        let creator = CreatorBrowsingContextInfo::from(
87            parent_browsing_context.as_deref(),
88            opener_browsing_context.as_deref(),
89        );
90
91        let window_proxy = WindowProxy::new_dissimilar_origin(
92            global_to_clone,
93            browsing_context_id,
94            webview_id,
95            parent_browsing_context.as_deref(),
96            opener,
97            creator,
98        );
99        self.insert(browsing_context_id, DomRoot::from_ref(&*window_proxy));
100        Some(window_proxy)
101    }
102
103    // Get the browsing context for a pipeline that exists in this
104    // script thread.  If the browsing context already exists in the
105    // `window_proxies` map, we return it, otherwise we recursively
106    // get the browsing context for the parent if there is one,
107    // construct a new similar-origin browsing context, add it
108    // to the `window_proxies` map, and return it.
109    #[allow(clippy::too_many_arguments)]
110    pub(crate) fn local_window_proxy(
111        &self,
112        senders: &ScriptThreadSenders,
113        documents: &DomRefCell<DocumentCollection>,
114        window: &Window,
115        browsing_context_id: BrowsingContextId,
116        webview_id: WebViewId,
117        parent_info: Option<PipelineId>,
118        opener: Option<BrowsingContextId>,
119    ) -> DomRoot<WindowProxy> {
120        if let Some(window_proxy) = self.get(browsing_context_id) {
121            // Note: we do not set the window to be the currently-active one,
122            // this will be done instead when the script-thread handles the `SetDocumentActivity` msg.
123            return window_proxy;
124        }
125        let iframe = parent_info.and_then(|parent_id| {
126            documents
127                .borrow()
128                .find_iframe(parent_id, browsing_context_id)
129        });
130        let parent_browsing_context = match (parent_info, iframe.as_ref()) {
131            (_, Some(iframe)) => Some(iframe.owner_window().window_proxy()),
132            (Some(parent_id), _) => {
133                self.remote_window_proxy(senders, window.upcast(), webview_id, parent_id, opener)
134            },
135            _ => None,
136        };
137
138        let opener_browsing_context = opener.and_then(|id| self.find_window_proxy(id));
139
140        let creator = CreatorBrowsingContextInfo::from(
141            parent_browsing_context.as_deref(),
142            opener_browsing_context.as_deref(),
143        );
144
145        let window_proxy = WindowProxy::new(
146            window,
147            browsing_context_id,
148            webview_id,
149            iframe.as_deref().map(Castable::upcast),
150            parent_browsing_context.as_deref(),
151            opener,
152            creator,
153        );
154        self.insert(browsing_context_id, DomRoot::from_ref(&*window_proxy));
155        window_proxy
156    }
157
158    fn ask_constellation_for_browsing_context_info(
159        &self,
160        senders: &ScriptThreadSenders,
161        pipeline_id: PipelineId,
162    ) -> Option<(BrowsingContextId, Option<PipelineId>)> {
163        let (result_sender, result_receiver) = ipc::channel().unwrap();
164        let msg = ScriptToConstellationMessage::GetBrowsingContextInfo(pipeline_id, result_sender);
165        senders
166            .pipeline_to_constellation_sender
167            .send((pipeline_id, msg))
168            .expect("Failed to send to constellation.");
169        result_receiver
170            .recv()
171            .expect("Failed to get browsing context info from constellation.")
172    }
173}