script/dom/
dissimilaroriginwindow.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 dom_struct::dom_struct;
6use js::context::JSContext;
7use js::jsapi::{Heap, JSObject};
8use js::jsval::UndefinedValue;
9use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue, MutableHandleValue};
10use net_traits::response::HttpsState;
11use servo_base::id::PipelineId;
12use servo_constellation_traits::{ScriptToConstellationMessage, StructuredSerializedData};
13use servo_url::ServoUrl;
14
15use crate::dom::bindings::codegen::Bindings::DissimilarOriginWindowBinding;
16use crate::dom::bindings::codegen::Bindings::DissimilarOriginWindowBinding::DissimilarOriginWindowMethods;
17use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowPostMessageOptions;
18use crate::dom::bindings::error::{Error, ErrorResult};
19use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
20use crate::dom::bindings::str::USVString;
21use crate::dom::bindings::structuredclone;
22use crate::dom::bindings::trace::RootedTraceableBox;
23use crate::dom::dissimilaroriginlocation::DissimilarOriginLocation;
24use crate::dom::globalscope::GlobalScope;
25use crate::dom::windowproxy::WindowProxy;
26use crate::script_runtime::CanGc;
27
28/// Represents a dissimilar-origin `Window` that exists in another script thread.
29///
30/// Since the `Window` is in a different script thread, we cannot access it
31/// directly, but some of its accessors (for example `window.parent`)
32/// still need to function.
33///
34/// In `windowproxy.rs`, we create a custom window proxy for these windows,
35/// that throws security exceptions for most accessors. This is not a replacement
36/// for XOWs, but provides belt-and-braces security.
37#[dom_struct]
38pub(crate) struct DissimilarOriginWindow {
39    /// The global for this window.
40    globalscope: GlobalScope,
41
42    /// The window proxy for this window.
43    window_proxy: Dom<WindowProxy>,
44
45    /// The location of this window, initialized lazily.
46    location: MutNullableDom<DissimilarOriginLocation>,
47}
48
49impl DissimilarOriginWindow {
50    pub(crate) fn new(
51        cx: &mut js::context::JSContext,
52        global_to_clone_from: &GlobalScope,
53        window_proxy: &WindowProxy,
54    ) -> DomRoot<Self> {
55        let win = Box::new(Self {
56            globalscope: GlobalScope::new_inherited(
57                PipelineId::new(),
58                global_to_clone_from.devtools_chan().cloned(),
59                global_to_clone_from.mem_profiler_chan().clone(),
60                global_to_clone_from.time_profiler_chan().clone(),
61                global_to_clone_from.script_to_constellation_chan().clone(),
62                global_to_clone_from.script_to_embedder_chan().clone(),
63                global_to_clone_from.resource_threads().clone(),
64                global_to_clone_from.storage_threads().clone(),
65                global_to_clone_from.origin().clone(),
66                global_to_clone_from.creation_url(),
67                global_to_clone_from.top_level_creation_url().clone(),
68                #[cfg(feature = "webgpu")]
69                global_to_clone_from.wgpu_id_hub(),
70                Some(global_to_clone_from.is_secure_context()),
71                false,
72                global_to_clone_from.font_context().cloned(),
73                HttpsState::None,
74            ),
75            window_proxy: Dom::from_ref(window_proxy),
76            location: Default::default(),
77        });
78        DissimilarOriginWindowBinding::Wrap::<crate::DomTypeHolder>(cx, win)
79    }
80
81    pub(crate) fn window_proxy(&self) -> DomRoot<WindowProxy> {
82        DomRoot::from_ref(&*self.window_proxy)
83    }
84}
85
86impl DissimilarOriginWindowMethods<crate::DomTypeHolder> for DissimilarOriginWindow {
87    /// <https://html.spec.whatwg.org/multipage/#dom-window>
88    fn Window(&self) -> DomRoot<WindowProxy> {
89        self.window_proxy()
90    }
91
92    /// <https://html.spec.whatwg.org/multipage/#dom-self>
93    fn Self_(&self) -> DomRoot<WindowProxy> {
94        self.window_proxy()
95    }
96
97    /// <https://html.spec.whatwg.org/multipage/#dom-frames>
98    fn Frames(&self) -> DomRoot<WindowProxy> {
99        self.window_proxy()
100    }
101
102    /// <https://html.spec.whatwg.org/multipage/#dom-parent>
103    fn GetParent(&self) -> Option<DomRoot<WindowProxy>> {
104        // Steps 1-3.
105        if self.window_proxy.is_browsing_context_discarded() {
106            return None;
107        }
108        // Step 4.
109        if let Some(parent) = self.window_proxy.parent() {
110            return Some(DomRoot::from_ref(parent));
111        }
112        // Step 5.
113        Some(DomRoot::from_ref(&*self.window_proxy))
114    }
115
116    /// <https://html.spec.whatwg.org/multipage/#dom-top>
117    fn GetTop(&self) -> Option<DomRoot<WindowProxy>> {
118        // Steps 1-3.
119        if self.window_proxy.is_browsing_context_discarded() {
120            return None;
121        }
122        // Steps 4-5.
123        Some(DomRoot::from_ref(self.window_proxy.top()))
124    }
125
126    /// <https://html.spec.whatwg.org/multipage/#dom-length>
127    fn Length(&self) -> u32 {
128        // TODO: Implement x-origin length
129        0
130    }
131
132    /// <https://html.spec.whatwg.org/multipage/#dom-window-close>
133    fn Close(&self) {
134        // TODO: Implement x-origin close
135    }
136
137    /// <https://html.spec.whatwg.org/multipage/#dom-window-closed>
138    fn Closed(&self) -> bool {
139        // TODO: Implement x-origin close
140        false
141    }
142
143    /// <https://html.spec.whatwg.org/multipage/#dom-window-postmessage>
144    fn PostMessage(
145        &self,
146        cx: &mut JSContext,
147        message: HandleValue,
148        target_origin: USVString,
149        transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
150    ) -> ErrorResult {
151        self.post_message_impl(&target_origin, cx, message, transfer)
152    }
153
154    /// <https://html.spec.whatwg.org/multipage/#dom-window-postmessage-options>
155    fn PostMessage_(
156        &self,
157        cx: &mut JSContext,
158        message: HandleValue,
159        options: RootedTraceableBox<WindowPostMessageOptions>,
160    ) -> ErrorResult {
161        let mut rooted = CustomAutoRooter::new(
162            options
163                .parent
164                .transfer
165                .iter()
166                .map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get())
167                .collect(),
168        );
169        #[expect(unsafe_code)]
170        let transfer = unsafe { CustomAutoRooterGuard::new(cx.raw_cx(), &mut rooted) };
171
172        self.post_message_impl(&options.targetOrigin, cx, message, transfer)
173    }
174
175    /// <https://html.spec.whatwg.org/multipage/#dom-opener>
176    fn Opener(&self, _: &mut JSContext, mut retval: MutableHandleValue) {
177        // TODO: Implement x-origin opener
178        retval.set(UndefinedValue());
179    }
180
181    /// <https://html.spec.whatwg.org/multipage/#dom-opener>
182    fn SetOpener(&self, _: &mut JSContext, _: HandleValue) {
183        // TODO: Implement x-origin opener
184    }
185
186    /// <https://html.spec.whatwg.org/multipage/#dom-window-blur>
187    fn Blur(&self) {
188        // > User agents are encouraged to ignore calls to this `blur()` method
189        // > entirely.
190    }
191
192    /// <https://html.spec.whatwg.org/multipage/#dom-window-focus>
193    fn Focus(&self) {
194        let browsing_context_id = self.window_proxy.browsing_context_id();
195        debug!("Initiating a focus operation for {browsing_context_id:?}");
196        self.globalscope
197            .script_to_constellation_chan()
198            .send(ScriptToConstellationMessage::FocusRemoteBrowsingContext(
199                browsing_context_id,
200            ))
201            .unwrap();
202    }
203
204    /// <https://html.spec.whatwg.org/multipage/#dom-location>
205    fn Location(&self, can_gc: CanGc) -> DomRoot<DissimilarOriginLocation> {
206        self.location
207            .or_init(|| DissimilarOriginLocation::new(self, can_gc))
208    }
209}
210
211impl DissimilarOriginWindow {
212    /// <https://html.spec.whatwg.org/multipage/#window-post-message-steps>
213    fn post_message_impl(
214        &self,
215        target_origin: &USVString,
216        cx: &mut JSContext,
217        message: HandleValue,
218        transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
219    ) -> ErrorResult {
220        // Step 6-7.
221        let data = structuredclone::write(cx.into(), message, Some(transfer))?;
222
223        self.post_message(target_origin, data)
224    }
225
226    /// <https://html.spec.whatwg.org/multipage/#window-post-message-steps>
227    pub(crate) fn post_message(
228        &self,
229        target_origin: &USVString,
230        data: StructuredSerializedData,
231    ) -> ErrorResult {
232        // Step 1.
233        let target = self.window_proxy.browsing_context_id();
234        // Step 2.
235        let incumbent = match GlobalScope::incumbent() {
236            None => panic!("postMessage called with no incumbent global"),
237            Some(incumbent) => incumbent,
238        };
239
240        let source_origin = incumbent.origin().immutable().clone();
241
242        // Step 3-5.
243        let target_origin = match target_origin.0[..].as_ref() {
244            "*" => None,
245            "/" => Some(source_origin.clone()),
246            url => match ServoUrl::parse(url) {
247                Ok(url) => Some(url.origin()),
248                Err(_) => return Err(Error::Syntax(None)),
249            },
250        };
251        let msg = ScriptToConstellationMessage::PostMessage {
252            target,
253            source: incumbent.pipeline_id(),
254            source_origin,
255            target_origin,
256            data,
257        };
258        // Step 8
259        let _ = incumbent.script_to_constellation_chan().send(msg);
260        Ok(())
261    }
262}