Skip to main content

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