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