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::jsapi::{Heap, JSObject};
9use js::jsval::UndefinedValue;
10use js::rust::{CustomAutoRooter, CustomAutoRooterGuard, HandleValue, MutableHandleValue};
11use script_bindings::script_runtime::temp_cx;
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, JSContext};
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    #[expect(unsafe_code)]
50    pub(crate) fn new(
51        global_to_clone_from: &GlobalScope,
52        window_proxy: &WindowProxy,
53    ) -> DomRoot<Self> {
54        let mut cx = unsafe { temp_cx() };
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().clone(),
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            ),
74            window_proxy: Dom::from_ref(window_proxy),
75            location: Default::default(),
76        });
77        DissimilarOriginWindowBinding::Wrap::<crate::DomTypeHolder>(&mut cx, win)
78    }
79
80    pub(crate) fn window_proxy(&self) -> DomRoot<WindowProxy> {
81        DomRoot::from_ref(&*self.window_proxy)
82    }
83}
84
85impl DissimilarOriginWindowMethods<crate::DomTypeHolder> for DissimilarOriginWindow {
86    /// <https://html.spec.whatwg.org/multipage/#dom-window>
87    fn Window(&self) -> DomRoot<WindowProxy> {
88        self.window_proxy()
89    }
90
91    /// <https://html.spec.whatwg.org/multipage/#dom-self>
92    fn Self_(&self) -> DomRoot<WindowProxy> {
93        self.window_proxy()
94    }
95
96    /// <https://html.spec.whatwg.org/multipage/#dom-frames>
97    fn Frames(&self) -> DomRoot<WindowProxy> {
98        self.window_proxy()
99    }
100
101    /// <https://html.spec.whatwg.org/multipage/#dom-parent>
102    fn GetParent(&self) -> Option<DomRoot<WindowProxy>> {
103        // Steps 1-3.
104        if self.window_proxy.is_browsing_context_discarded() {
105            return None;
106        }
107        // Step 4.
108        if let Some(parent) = self.window_proxy.parent() {
109            return Some(DomRoot::from_ref(parent));
110        }
111        // Step 5.
112        Some(DomRoot::from_ref(&*self.window_proxy))
113    }
114
115    /// <https://html.spec.whatwg.org/multipage/#dom-top>
116    fn GetTop(&self) -> Option<DomRoot<WindowProxy>> {
117        // Steps 1-3.
118        if self.window_proxy.is_browsing_context_discarded() {
119            return None;
120        }
121        // Steps 4-5.
122        Some(DomRoot::from_ref(self.window_proxy.top()))
123    }
124
125    /// <https://html.spec.whatwg.org/multipage/#dom-length>
126    fn Length(&self) -> u32 {
127        // TODO: Implement x-origin length
128        0
129    }
130
131    /// <https://html.spec.whatwg.org/multipage/#dom-window-close>
132    fn Close(&self) {
133        // TODO: Implement x-origin close
134    }
135
136    /// <https://html.spec.whatwg.org/multipage/#dom-window-closed>
137    fn Closed(&self) -> bool {
138        // TODO: Implement x-origin close
139        false
140    }
141
142    /// <https://html.spec.whatwg.org/multipage/#dom-window-postmessage>
143    fn PostMessage(
144        &self,
145        cx: JSContext,
146        message: HandleValue,
147        target_origin: USVString,
148        transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
149    ) -> ErrorResult {
150        self.post_message_impl(&target_origin, cx, message, transfer)
151    }
152
153    /// <https://html.spec.whatwg.org/multipage/#dom-window-postmessage-options>
154    fn PostMessage_(
155        &self,
156        cx: JSContext,
157        message: HandleValue,
158        options: RootedTraceableBox<WindowPostMessageOptions>,
159    ) -> ErrorResult {
160        let mut rooted = CustomAutoRooter::new(
161            options
162                .parent
163                .transfer
164                .iter()
165                .map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get())
166                .collect(),
167        );
168        let transfer = CustomAutoRooterGuard::new(*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, _: 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, _: 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: JSContext,
208        message: HandleValue,
209        transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
210    ) -> ErrorResult {
211        // Step 6-7.
212        let data = structuredclone::write(cx, 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().clone()),
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}