1use 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 script_bindings::interfaces::HasOrigin;
11use servo_base::id::PipelineId;
12use servo_constellation_traits::{
13 RemoteFocusOperation, ScriptToConstellationMessage, StructuredSerializedData,
14};
15use servo_url::{MutableOrigin, ServoUrl};
16
17use crate::dom::bindings::codegen::Bindings::DissimilarOriginWindowBinding;
18use crate::dom::bindings::codegen::Bindings::DissimilarOriginWindowBinding::DissimilarOriginWindowMethods;
19use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowPostMessageOptions;
20use crate::dom::bindings::error::{Error, ErrorResult};
21use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
22use crate::dom::bindings::str::USVString;
23use crate::dom::bindings::structuredclone;
24use crate::dom::bindings::trace::RootedTraceableBox;
25use crate::dom::dissimilaroriginlocation::DissimilarOriginLocation;
26use crate::dom::globalscope::GlobalScope;
27use crate::dom::windowproxy::WindowProxy;
28
29#[dom_struct]
39pub(crate) struct DissimilarOriginWindow {
40 globalscope: GlobalScope,
42
43 window_proxy: Dom<WindowProxy>,
45
46 location: MutNullableDom<DissimilarOriginLocation>,
48
49 #[no_trace]
50 pipeline_id: PipelineId,
51
52 #[no_trace]
53 origin: MutableOrigin,
54}
55
56impl DissimilarOriginWindow {
57 pub(crate) fn new(
58 cx: &mut js::context::JSContext,
59 global_to_clone_from: &GlobalScope,
60 window_proxy: &WindowProxy,
61 ) -> DomRoot<Self> {
62 let win = Box::new(Self {
63 globalscope: GlobalScope::new_inherited(
64 global_to_clone_from.devtools_chan().cloned(),
65 global_to_clone_from.mem_profiler_chan().clone(),
66 global_to_clone_from.time_profiler_chan().clone(),
67 global_to_clone_from.script_to_constellation_chan().sender,
68 global_to_clone_from.script_to_embedder_chan().clone(),
69 global_to_clone_from.resource_threads().clone(),
70 global_to_clone_from.storage_threads().clone(),
71 global_to_clone_from.creation_url(),
72 global_to_clone_from.top_level_creation_url(),
73 #[cfg(feature = "webgpu")]
74 global_to_clone_from.wgpu_id_hub(),
75 Some(global_to_clone_from.is_secure_context()),
76 false,
77 ),
78 window_proxy: Dom::from_ref(window_proxy),
79 location: Default::default(),
80 pipeline_id: PipelineId::new(),
81 origin: global_to_clone_from.origin(),
82 });
83 DissimilarOriginWindowBinding::Wrap::<crate::DomTypeHolder>(cx, &win.origin(), win)
84 }
85
86 pub(crate) fn origin(&self) -> MutableOrigin {
87 self.origin.clone()
88 }
89
90 pub(crate) fn window_proxy(&self) -> DomRoot<WindowProxy> {
91 DomRoot::from_ref(&*self.window_proxy)
92 }
93
94 pub(crate) fn pipeline_id(&self) -> PipelineId {
95 self.pipeline_id
96 }
97}
98
99impl DissimilarOriginWindowMethods<crate::DomTypeHolder> for DissimilarOriginWindow {
100 fn Window(&self) -> DomRoot<WindowProxy> {
102 self.window_proxy()
103 }
104
105 fn Self_(&self) -> DomRoot<WindowProxy> {
107 self.window_proxy()
108 }
109
110 fn Frames(&self) -> DomRoot<WindowProxy> {
112 self.window_proxy()
113 }
114
115 fn GetParent(&self) -> Option<DomRoot<WindowProxy>> {
117 if self.window_proxy.is_browsing_context_discarded() {
119 return None;
120 }
121 if let Some(parent) = self.window_proxy.parent() {
123 return Some(DomRoot::from_ref(parent));
124 }
125 Some(DomRoot::from_ref(&*self.window_proxy))
127 }
128
129 fn GetTop(&self) -> Option<DomRoot<WindowProxy>> {
131 if self.window_proxy.is_browsing_context_discarded() {
133 return None;
134 }
135 Some(DomRoot::from_ref(self.window_proxy.top()))
137 }
138
139 fn Length(&self) -> u32 {
141 0
143 }
144
145 fn Close(&self) {
147 }
149
150 fn Closed(&self) -> bool {
152 false
154 }
155
156 fn PostMessage(
158 &self,
159 cx: &mut JSContext,
160 message: HandleValue,
161 target_origin: USVString,
162 transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
163 ) -> ErrorResult {
164 self.post_message_impl(&target_origin, cx, message, transfer)
165 }
166
167 fn PostMessage_(
169 &self,
170 cx: &mut JSContext,
171 message: HandleValue,
172 options: RootedTraceableBox<WindowPostMessageOptions>,
173 ) -> ErrorResult {
174 let mut rooted = CustomAutoRooter::new(
175 options
176 .parent
177 .transfer
178 .iter()
179 .map(|js: &RootedTraceableBox<Heap<*mut JSObject>>| js.get())
180 .collect(),
181 );
182 #[expect(unsafe_code)]
183 let transfer = unsafe { CustomAutoRooterGuard::new(cx.raw_cx(), &mut rooted) };
184
185 self.post_message_impl(&options.targetOrigin, cx, message, transfer)
186 }
187
188 fn Opener(&self, _: &mut JSContext, mut retval: MutableHandleValue) {
190 retval.set(UndefinedValue());
192 }
193
194 fn SetOpener(&self, _: &mut JSContext, _: HandleValue) {
196 }
198
199 fn Blur(&self) {
201 }
204
205 fn Focus(&self) {
207 let browsing_context_id = self.window_proxy.browsing_context_id();
208 debug!("Initiating a focus operation for {browsing_context_id:?}");
209 let _ = self.globalscope.script_to_constellation_chan().send(
210 ScriptToConstellationMessage::FocusRemoteBrowsingContext(
211 browsing_context_id,
212 RemoteFocusOperation::Viewport,
213 ),
214 );
215 }
216
217 fn Location(&self, cx: &mut js::context::JSContext) -> DomRoot<DissimilarOriginLocation> {
219 self.location
220 .or_init(|| DissimilarOriginLocation::new(cx, self))
221 }
222}
223
224impl DissimilarOriginWindow {
225 fn post_message_impl(
227 &self,
228 target_origin: &USVString,
229 cx: &mut JSContext,
230 message: HandleValue,
231 transfer: CustomAutoRooterGuard<Vec<*mut JSObject>>,
232 ) -> ErrorResult {
233 let data = structuredclone::write(cx, message, Some(transfer))?;
235
236 self.post_message(target_origin, data)
237 }
238
239 pub(crate) fn post_message(
241 &self,
242 target_origin: &USVString,
243 data: StructuredSerializedData,
244 ) -> ErrorResult {
245 let target = self.window_proxy.browsing_context_id();
247 let incumbent = match GlobalScope::incumbent() {
249 None => panic!("postMessage called with no incumbent global"),
250 Some(incumbent) => incumbent,
251 };
252
253 let source_origin = incumbent.origin().immutable().clone();
254
255 let target_origin = match target_origin.0[..].as_ref() {
257 "*" => None,
258 "/" => Some(source_origin.clone()),
259 url => match ServoUrl::parse(url) {
260 Ok(url) => Some(url.origin()),
261 Err(_) => return Err(Error::Syntax(None)),
262 },
263 };
264 let msg = ScriptToConstellationMessage::PostMessage {
265 target,
266 source: incumbent.pipeline_id(),
267 source_origin,
268 target_origin,
269 data,
270 };
271 let _ = incumbent.script_to_constellation_chan().send(msg);
273 Ok(())
274 }
275}
276
277impl HasOrigin for DissimilarOriginWindow {
278 fn origin(&self) -> MutableOrigin {
279 DissimilarOriginWindow::origin(self)
280 }
281}