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