1use std::cell::Cell;
6use std::ptr::{self, NonNull};
7use std::rc::Rc;
8
9use content_security_policy::sandboxing_directive::SandboxingFlagSet;
10use dom_struct::dom_struct;
11use html5ever::local_name;
12use indexmap::map::IndexMap;
13use itertools::Either;
14use js::JSCLASS_IS_GLOBAL;
15use js::context::JSContext;
16use js::gc::{HandleId, HandleObject, HandleValue, MutableHandleObject};
17use js::glue::{
18 CreateWrapperProxyHandler, DeleteWrapperProxyHandler, GetProxyPrivate, GetProxyReservedSlot,
19 ProxyTraps, SetProxyReservedSlot,
20};
21use js::jsapi::{
22 GCContext, Handle as RawHandle, HandleId as RawHandleId, HandleObject as RawHandleObject,
23 HandleValue as RawHandleValue, JS_DefinePropertyById, JS_DeletePropertyById,
24 JS_ForwardSetPropertyTo, JSContext as RawJSContext, JSErrNum, JSITER_HIDDEN, JSITER_OWNONLY,
25 JSITER_SYMBOLS, JSObject, JSPROP_ENUMERATE, JSPROP_READONLY, JSTracer,
26 MutableHandle as RawMutableHandle, MutableHandleIdVector as RawMutableHandleIdVector,
27 MutableHandleObject as RawMutableHandleObject, MutableHandleValue as RawMutableHandleValue,
28 ObjectOpResult, PropertyDescriptor, jsid,
29};
30use js::jsval::{NullValue, PrivateValue, UndefinedValue};
31use js::realm::{AutoRealm, CurrentRealm};
32use js::rust::wrappers2::{
33 AppendToIdVector, JS_ForwardGetPropertyTo, JS_HasOwnPropertyById, JS_HasPropertyById,
34 JS_TransplantObject, NewWindowProxy, SetWindowProxy, int_to_jsid,
35};
36use js::rust::{Handle, MutableHandle, MutableHandleValue, get_object_class};
37use js::typedarray::JSObjectStorage;
38use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
39use net_traits::ReferrerPolicy;
40use net_traits::request::Referrer;
41use script_bindings::cell::DomRefCell;
42use script_bindings::codegen::GenericBindings::DissimilarOriginWindowBinding::{
43 self, DissimilarOriginWindowMethods,
44};
45use script_bindings::codegen::GenericBindings::HTMLIFrameElementBinding::HTMLIFrameElementMethods;
46use script_bindings::codegen::GenericBindings::WindowBinding::{
47 self, GetProtoObject, WindowMethods,
48};
49use script_bindings::conversions::jsid_to_string;
50use script_bindings::domstring::TracedDOMString;
51use script_bindings::js::{GetPropertyKeys, JS_GetOwnPropertyDescriptorById};
52use script_bindings::proxyhandler::{
53 self, CROSS_ORIGIN_PROPERTY_HOLDER_WEAK_MAP_SLOT, CrossOriginProperties,
54 cross_origin_get_own_property_helper, cross_origin_own_property_keys,
55 cross_origin_property_fallback, cross_origin_set, is_extensible,
56 is_platform_object_same_origin, maybe_cross_origin_get_prototype,
57 maybe_cross_origin_set_prototype_rawcx, prevent_extensions, report_cross_origin_denial,
58 set_property_descriptor,
59};
60use script_bindings::reflector::{DomObject, MutDomObject, Reflector};
61use script_traits::{NewPipelineInfo, WebViewState};
62use serde::{Deserialize, Serialize};
63use servo_base::generic_channel;
64use servo_base::generic_channel::GenericSend;
65use servo_base::id::{BrowsingContextId, PipelineId, WebViewId};
66use servo_constellation_traits::{
67 AuxiliaryWebViewCreationRequest, LoadData, LoadOrigin, NavigationHistoryBehavior,
68 ScriptToConstellationMessage, TargetSnapshotParams,
69};
70use servo_url::{ImmutableOrigin, OriginSnapshot, ServoUrl};
71use storage_traits::webstorage_thread::WebStorageThreadMsg;
72use style::attr::parse_integer;
73
74use crate::DomTypeHolder;
75use crate::dom::bindings::conversions::{ToJSValConvertible, root_from_handleobject};
76use crate::dom::bindings::error::{Error, Fallible};
77use crate::dom::bindings::inheritance::Castable;
78use crate::dom::bindings::reflector::DomGlobal;
79use crate::dom::bindings::root::{Dom, DomRoot};
80use crate::dom::bindings::settings_stack::maybe_entry_global;
81use crate::dom::bindings::str::{DOMString, USVString};
82use crate::dom::bindings::trace::JSTraceable;
83use crate::dom::bindings::utils::get_array_index_from_id;
84use crate::dom::dissimilaroriginwindow::DissimilarOriginWindow;
85use crate::dom::document::Document;
86use crate::dom::element::Element;
87use crate::dom::globalscope::GlobalScope;
88use crate::dom::node::node::NodeTraits;
89use crate::dom::window::Window;
90use crate::event_loop::script_thread::{ScriptThread, with_script_thread};
91use crate::event_loop::script_window_proxies::ScriptWindowProxies;
92use crate::navigation::navigate;
93
94#[dom_struct]
95pub(crate) struct WindowProxy {
100 reflector: Reflector,
105
106 #[no_trace]
110 browsing_context_id: BrowsingContextId,
111
112 #[no_trace]
114 opener: Option<BrowsingContextId>,
115
116 #[no_trace]
119 webview_id: WebViewId,
120
121 name: DomRefCell<TracedDOMString>,
124 #[no_trace]
130 currently_active: Cell<Option<PipelineId>>,
131
132 discarded: Cell<bool>,
134
135 disowned: Cell<bool>,
137
138 is_closing: Cell<bool>,
140
141 frame_element: Option<Dom<Element>>,
145
146 parent: Option<Dom<WindowProxy>>,
148
149 delaying_load_events_mode: Cell<bool>,
151
152 #[no_trace]
154 creator_url: Option<ServoUrl>,
155
156 #[no_trace]
158 creator_origin: Option<ImmutableOrigin>,
159
160 #[conditional_malloc_size_of]
162 script_window_proxies: Rc<ScriptWindowProxies>,
163}
164
165impl WindowProxy {
166 fn new_inherited(
167 browsing_context_id: BrowsingContextId,
168 webview_id: WebViewId,
169 currently_active: Option<PipelineId>,
170 frame_element: Option<&Element>,
171 parent: Option<&WindowProxy>,
172 opener: Option<BrowsingContextId>,
173 creator: CreatorBrowsingContextInfo,
174 ) -> WindowProxy {
175 let name = frame_element.map_or(DOMString::new(), |e| {
176 e.get_string_attribute(&local_name!("name"))
177 });
178 WindowProxy {
179 reflector: Reflector::new(),
180 browsing_context_id,
181 webview_id,
182 name: DomRefCell::new(name.traced()),
183 currently_active: Cell::new(currently_active),
184 discarded: Cell::new(false),
185 disowned: Cell::new(false),
186 is_closing: Cell::new(false),
187 frame_element: frame_element.map(Dom::from_ref),
188 parent: parent.map(Dom::from_ref),
189 delaying_load_events_mode: Cell::new(false),
190 opener,
191 creator_url: creator.url,
192 creator_origin: creator.origin,
193 script_window_proxies: ScriptThread::window_proxies(),
194 }
195 }
196
197 #[expect(unsafe_code)]
198 #[expect(clippy::too_many_arguments)]
199 pub(crate) fn new(
200 cx: &mut JSContext,
201 window: &Window,
202 browsing_context_id: BrowsingContextId,
203 webview_id: WebViewId,
204 frame_element: Option<&Element>,
205 parent: Option<&WindowProxy>,
206 opener: Option<BrowsingContextId>,
207 creator: CreatorBrowsingContextInfo,
208 ) -> DomRoot<WindowProxy> {
209 unsafe {
210 let handler = window.windowproxy_handler();
211
212 let window_jsobject = window.reflector().get_jsobject();
213 assert!(!window_jsobject.get().is_null());
214 assert_ne!(
215 ((*get_object_class(window_jsobject.get())).flags & JSCLASS_IS_GLOBAL),
216 0
217 );
218
219 let mut realm = AutoRealm::new_from_handle(cx, window_jsobject);
220 let cx = &mut realm;
221
222 rooted!(&in(cx) let js_proxy = handler.new_window_proxy(cx, window_jsobject));
224 assert!(!js_proxy.is_null());
225
226 let current = Some(window.upcast::<GlobalScope>().pipeline_id());
229 let window_proxy = Box::new(WindowProxy::new_inherited(
230 browsing_context_id,
231 webview_id,
232 current,
233 frame_element,
234 parent,
235 opener,
236 creator,
237 ));
238
239 SetProxyReservedSlot(
242 js_proxy.get(),
243 0,
244 &PrivateValue(&raw const (*window_proxy) as *const libc::c_void),
245 );
246
247 SetWindowProxy(cx, window_jsobject, js_proxy.handle());
249
250 debug!(
252 "Initializing reflector of {:p} to {:p}.",
253 window_proxy,
254 js_proxy.get()
255 );
256 window_proxy
257 .reflector
258 .init_reflector::<WindowProxy>(js_proxy.get());
259 DomRoot::from_ref(&*Box::into_raw(window_proxy))
260 }
261 }
262
263 #[expect(unsafe_code)]
264 pub(crate) fn new_dissimilar_origin(
265 cx: &mut JSContext,
266 global_to_clone_from: &GlobalScope,
267 browsing_context_id: BrowsingContextId,
268 webview_id: WebViewId,
269 parent: Option<&WindowProxy>,
270 opener: Option<BrowsingContextId>,
271 creator: CreatorBrowsingContextInfo,
272 ) -> DomRoot<WindowProxy> {
273 unsafe {
274 let window_proxy = Box::new(WindowProxy::new_inherited(
276 browsing_context_id,
277 webview_id,
278 None,
279 None,
280 parent,
281 opener,
282 creator,
283 ));
284
285 let window = DissimilarOriginWindow::new(cx, global_to_clone_from, &window_proxy);
287 let window_jsobject = window.reflector().get_jsobject();
288 assert!(!window_jsobject.get().is_null());
289 assert_ne!(
290 ((*get_object_class(window_jsobject.get())).flags & JSCLASS_IS_GLOBAL),
291 0
292 );
293
294 let mut realm = AutoRealm::new_from_handle(cx, window_jsobject);
295 let cx = &mut realm;
296
297 let handler = WindowProxyHandler::proxy_handler();
299 rooted!(&in(cx) let js_proxy = handler.new_window_proxy(cx, window_jsobject));
300 assert!(!js_proxy.is_null());
301
302 SetProxyReservedSlot(
305 js_proxy.get(),
306 0,
307 &PrivateValue(&raw const (*window_proxy) as *const libc::c_void),
308 );
309
310 SetWindowProxy(cx, window_jsobject, js_proxy.handle());
312
313 debug!(
315 "Initializing reflector of {:p} to {:p}.",
316 window_proxy,
317 js_proxy.get()
318 );
319 window_proxy
320 .reflector
321 .init_reflector::<WindowProxy>(js_proxy.get());
322 DomRoot::from_ref(&*Box::into_raw(window_proxy))
323 }
324 }
325
326 fn create_auxiliary_browsing_context(
328 &self,
329 cx: &mut JSContext,
330 name: DOMString,
331 noopener: bool,
332 ) -> Option<DomRoot<WindowProxy>> {
333 let (response_sender, response_receiver) = generic_channel::channel().unwrap();
334 let window = self
335 .currently_active
336 .get()
337 .and_then(ScriptThread::find_document)
338 .map(|doc| DomRoot::from_ref(doc.window()))
339 .unwrap();
340
341 let document = self
342 .currently_active
343 .get()
344 .and_then(ScriptThread::find_document)
345 .expect("A WindowProxy creating an auxiliary to have an active document");
346
347 let sandboxing_flag_set = document.active_sandboxing_flag_set();
355 let propagate_sandbox = sandboxing_flag_set
356 .contains(SandboxingFlagSet::SANDBOX_PROPOGATES_TO_AUXILIARY_BROWSING_CONTEXTS_FLAG);
357 let sandboxing_flag_set = if propagate_sandbox {
358 sandboxing_flag_set
359 } else {
360 SandboxingFlagSet::empty()
361 };
362
363 let blank_url = ServoUrl::parse("about:blank").ok().unwrap();
364 let mut load_data = LoadData::new(
365 LoadOrigin::Script(document.origin().snapshot()),
366 blank_url,
367 Some(document.base_url()),
368 Some(window.pipeline_id()),
371 document.global().get_referrer(),
372 document.get_referrer_policy(),
373 None, None,
375 false,
376 sandboxing_flag_set,
377 );
378 load_data.is_initial_about_blank = true;
379 let load_info = AuxiliaryWebViewCreationRequest {
380 load_data: load_data.clone(),
381 opener_webview_id: window.webview_id(),
382 opener_pipeline_id: self.currently_active.get().unwrap(),
383 response_sender,
384 };
385 let constellation_msg = ScriptToConstellationMessage::CreateAuxiliaryWebView(load_info);
386 window.send_to_constellation(constellation_msg);
387
388 let response = response_receiver.recv().unwrap()?;
389 let new_browsing_context_id = BrowsingContextId::from(response.new_webview_id);
390 let new_pipeline_info = NewPipelineInfo {
391 webview_state: WebViewState {
392 id: response.new_webview_id,
393 theme: Cell::new(window.webview_theme()),
396 has_system_focus: Cell::new(true),
398 },
399 parent_info: None,
400 new_pipeline_id: response.new_pipeline_id,
401 browsing_context_id: new_browsing_context_id,
402 opener: Some(self.browsing_context_id),
403 load_data,
404 viewport_details: window.viewport_details(),
405 user_content_manager_id: response.user_content_manager_id,
406 target_snapshot_params: TargetSnapshotParams {
407 sandboxing_flags: sandboxing_flag_set,
408 iframe_element_referrer_policy: ReferrerPolicy::EmptyString,
409 },
410 frame_name: None,
411 };
412
413 with_script_thread(|script_thread| {
414 script_thread.spawn_pipeline(cx, new_pipeline_info);
415 });
416
417 let new_window_proxy = ScriptThread::find_document(response.new_pipeline_id)
418 .and_then(|doc| doc.browsing_context())?;
419 if !name.eq_ignore_ascii_case("_blank") {
420 new_window_proxy.set_name(name);
421 }
422 if noopener {
423 new_window_proxy.disown();
424 } else {
425 let (sender, receiver) = generic_channel::channel().unwrap();
430
431 let msg = WebStorageThreadMsg::Clone {
432 sender,
433 src: window.window_proxy().webview_id(),
434 dest: response.new_webview_id,
435 };
436
437 GenericSend::send(document.global().storage_threads(), msg).unwrap();
438 receiver.recv().unwrap();
439 }
440 Some(new_window_proxy)
441 }
442
443 pub(crate) fn start_delaying_load_events_mode(&self) {
445 self.delaying_load_events_mode.set(true);
446 }
447
448 pub(crate) fn stop_delaying_load_events_mode(&self) {
450 self.delaying_load_events_mode.set(false);
451 }
452
453 pub(crate) fn disown(&self) {
455 self.disowned.set(true);
456 }
457
458 pub(crate) fn close(&self) {
461 self.is_closing.set(true);
462 }
463
464 pub(crate) fn is_closing(&self) -> bool {
466 self.is_closing.get()
467 }
468
469 pub(crate) fn opener(&self, cx: &mut CurrentRealm, mut retval: MutableHandleValue) {
471 if self.disowned.get() {
472 return retval.set(NullValue());
473 }
474 let opener_id = match self.opener {
475 Some(opener_browsing_context_id) => opener_browsing_context_id,
476 None => return retval.set(NullValue()),
477 };
478 let parent_browsing_context = self.parent.as_deref();
479 let opener_proxy = match self.script_window_proxies.find_window_proxy(opener_id) {
480 Some(window_proxy) => window_proxy,
481 None => {
482 let sender_pipeline_id = self.currently_active().unwrap();
483 match ScriptThread::get_top_level_for_browsing_context(
484 self.webview_id(),
485 sender_pipeline_id,
486 opener_id,
487 ) {
488 Some(opener_top_id) => {
489 let global_to_clone_from = GlobalScope::from_current_realm(cx);
490 let creator =
491 CreatorBrowsingContextInfo::from(parent_browsing_context, None);
492 WindowProxy::new_dissimilar_origin(
493 cx,
494 &global_to_clone_from,
495 opener_id,
496 opener_top_id,
497 None,
498 None,
499 creator,
500 )
501 },
502 None => return retval.set(NullValue()),
503 }
504 },
505 };
506 if opener_proxy.is_browsing_context_discarded() {
507 return retval.set(NullValue());
508 }
509 opener_proxy.to_jsval(cx, retval);
510 }
511
512 pub(crate) fn open(
514 &self,
515 cx: &mut JSContext,
516 url: USVString,
517 target: DOMString,
518 features: DOMString,
519 ) -> Fallible<Option<DomRoot<WindowProxy>>> {
520 if self.discarded.get() {
526 return Ok(None);
527 }
528 let source_document = maybe_entry_global()
534 .map(|global| global.as_window().Document())
535 .or_else(|| self.document())
536 .expect("Must have an entry global or active document");
537 let url_record = if !url.is_empty() {
539 let Ok(url) = source_document.encoding_parse_a_url(&url) else {
541 return Err(Error::Syntax(Some(format!(
543 "Error parsing URL '{url}' relative to '{}'",
544 source_document.url()
545 ))));
546 };
547 Some(url)
548 } else {
549 None
551 };
552 let non_empty_target = if target.is_empty() {
554 DOMString::from_static("_blank")
555 } else {
556 target
557 };
558 let tokenized_features = tokenize_open_features(features);
560 let noreferrer = parse_open_feature_boolean(&tokenized_features, "noreferrer");
564
565 let noopener = if noreferrer {
568 true
569 } else {
570 parse_open_feature_boolean(&tokenized_features, "noopener")
571 };
572 let (chosen, new) = match self.choose_a_navigable(cx, non_empty_target, noopener) {
582 (Some(chosen), new) => (chosen, new),
583 (None, _) => return Ok(None),
584 };
585 let target_document = match chosen.document() {
588 Some(target_document) => target_document,
589 None => return Ok(None),
590 };
591 let has_trustworthy_ancestor_origin = if new {
592 target_document.has_trustworthy_ancestor_or_current_origin()
593 } else {
594 false
595 };
596 let target_window = target_document.window();
597 let url_record = url_record.unwrap_or(ServoUrl::parse("about:blank").unwrap());
599 if !url_record.matches_about_blank() {
603 let referrer = if noreferrer {
604 Referrer::NoReferrer
605 } else {
606 target_window.as_global_scope().get_referrer()
607 };
608 let csp_list = source_document.get_csp_list().clone();
610 target_document.set_csp_list(csp_list);
611
612 let mut load_data = LoadData::new(
616 LoadOrigin::Script(source_document.origin().snapshot()),
617 url_record,
618 target_document.about_base_url(),
619 Some(target_window.pipeline_id()),
620 referrer,
621 target_document.get_referrer_policy(),
622 Some(target_window.as_global_scope().is_secure_context()),
623 Some(target_document.insecure_requests_policy()),
624 has_trustworthy_ancestor_origin,
625 target_document.creation_sandboxing_flag_set_considering_parent_iframe(),
626 );
627
628 if load_data.url.scheme() == "javascript" {
631 let existing_global = source_document.global();
632
633 if !ScriptThread::can_navigate_to_javascript_url(
635 cx,
636 &existing_global,
637 target_window.as_global_scope(),
638 &mut load_data,
639 None,
640 ) {
641 return Ok(target_document.browsing_context());
643 }
644 }
645
646 let history_handling = if new {
647 NavigationHistoryBehavior::Replace
648 } else {
649 NavigationHistoryBehavior::Push
650 };
651 navigate(cx, target_window, history_handling, false, load_data);
652 }
653 if noopener {
655 return Ok(None);
656 }
657 Ok(target_document.browsing_context())
659 }
660
661 pub(crate) fn choose_a_navigable(
663 &self,
664 cx: &mut JSContext,
665 name: DOMString,
666 noopener: bool,
667 ) -> (Option<DomRoot<WindowProxy>>, bool) {
668 let sandboxing_flag_set = self
674 .document()
675 .map(|document| document.active_sandboxing_flag_set())
676 .unwrap_or_default();
677
678 let chosen = if name.is_empty() || name.eq_ignore_ascii_case("_self") {
679 Some((Some(DomRoot::from_ref(self)), false))
682 } else if name.eq_ignore_ascii_case("_parent") {
683 Some(
687 self.parent()
688 .map(|parent| (Some(DomRoot::from_ref(parent)), false))
689 .unwrap_or_else(|| (Some(DomRoot::from_ref(self)), false)),
690 )
691 } else if name.eq_ignore_ascii_case("_top") {
692 Some((Some(DomRoot::from_ref(self.top())), false))
695 } else if !name.eq_ignore_ascii_case("_blank") {
696 self.find_navigable_by_target_name(&name)
704 .map(|proxy| (Some(proxy), false))
705 } else {
706 None
707 };
708
709 if let Some(chosen) = chosen {
710 return chosen;
711 }
712
713 if sandboxing_flag_set
727 .contains(SandboxingFlagSet::SANDBOXED_AUXILIARY_NAVIGATION_BROWSING_CONTEXT_FLAG)
728 {
729 (None, false)
730 }
731 else {
735 (
736 self.create_auxiliary_browsing_context(cx, name, noopener),
737 true,
738 )
739 }
740 }
741
742 fn find_navigable_by_target_name(&self, name: &DOMString) -> Option<DomRoot<WindowProxy>> {
744 let top = self.top();
764 let subtrees_to_search = if top != self {
765 Either::Left([self, top])
766 } else {
767 Either::Right([self])
768 };
769
770 for subtree_to_search in subtrees_to_search.into_iter() {
772 if let Some(result) =
776 subtree_to_search.find_navigable_by_target_name_in_descendants(name)
777 {
778 return Some(result);
779 }
780 }
781
782 let mut top_level_window_proxies = self.script_window_proxies.top_level_window_proxies();
789
790 top_level_window_proxies
798 .sort_by_key(|proxy| std::cmp::Reverse(proxy.browsing_context_id()));
799
800 for window_proxy in top_level_window_proxies {
801 if &*window_proxy == top {
804 continue;
805 }
806 if let Some(result) = window_proxy.find_navigable_by_target_name_in_descendants(name) {
824 return Some(result);
825 }
826 }
827
828 None
830 }
831
832 fn find_navigable_by_target_name_in_descendants(
834 &self,
835 name: &DOMString,
836 ) -> Option<DomRoot<WindowProxy>> {
837 if self.is_browsing_context_discarded() {
839 return None;
840 }
841
842 if self.get_name() == *name {
848 return Some(DomRoot::from_ref(self));
849 }
850
851 let document = self.document()?;
852 let iframes: Vec<_> = document.iframes().iter().collect();
853 iframes.iter().find_map(|iframe| {
854 iframe
855 .browsing_context_id()
856 .and_then(|browsing_context_id| {
857 self.script_window_proxies
858 .find_window_proxy(browsing_context_id)?
859 .find_navigable_by_target_name_in_descendants(name)
860 })
861 })
862 }
863
864 pub(crate) fn is_auxiliary(&self) -> bool {
865 self.opener.is_some()
866 }
867
868 pub(crate) fn discard_browsing_context(&self) {
869 self.discarded.set(true);
870 }
871
872 pub(crate) fn is_browsing_context_discarded(&self) -> bool {
873 self.discarded.get()
874 }
875
876 pub(crate) fn browsing_context_id(&self) -> BrowsingContextId {
877 self.browsing_context_id
878 }
879
880 pub(crate) fn webview_id(&self) -> WebViewId {
881 self.webview_id
882 }
883
884 pub(crate) fn frame_element(&self) -> Option<&Element> {
891 if self.is_browsing_context_discarded() {
892 return None;
893 }
894 self.frame_element.as_deref()
895 }
896
897 pub(crate) fn document(&self) -> Option<DomRoot<Document>> {
898 self.currently_active
899 .get()
900 .and_then(ScriptThread::find_document)
901 }
902
903 pub(crate) fn parent(&self) -> Option<&WindowProxy> {
904 self.parent.as_deref()
905 }
906
907 pub(crate) fn top(&self) -> &WindowProxy {
908 let mut result = self;
909 while let Some(parent) = result.parent() {
910 result = parent;
911 }
912 result
913 }
914
915 pub(crate) fn document_origin_and_internal_ancestor_origin_objects_list(
916 &self,
917 ) -> Option<(OriginSnapshot, Vec<ImmutableOrigin>)> {
918 let pipeline_id = self.currently_active()?;
919 let (result_sender, result_receiver) = generic_channel::channel().unwrap();
920 self.global()
921 .script_to_constellation_chan()
922 .send(ScriptToConstellationMessage::GetDocumentOriginDetails(
923 pipeline_id,
924 result_sender,
925 ))
926 .ok()?;
927 result_receiver.recv().ok()?
928 }
929
930 pub(crate) fn parent_origin_and_internal_ancestor_origin_objects_list(
932 &self,
933 ) -> Option<(OriginSnapshot, Vec<ImmutableOrigin>)> {
934 if let Some(frame_element) = self.frame_element() {
935 let parent_document = frame_element.owner_document();
936 if !parent_document.is_fully_active() {
939 return None;
940 }
941 Some((
942 parent_document.origin().snapshot(),
943 parent_document
944 .internal_ancestor_origin_objects_list()
945 .clone()
946 .expect("Must always be fully active"),
947 ))
948 } else if let Some(parent_proxy) = self.parent() {
949 assert!(parent_proxy.currently_active().is_some());
951 parent_proxy.document_origin_and_internal_ancestor_origin_objects_list()
952 } else {
953 None
954 }
955 }
956
957 #[expect(unsafe_code)]
958 fn set_window(&self, cx: &mut JSContext, window: &GlobalScope) {
962 unsafe {
963 debug!("Setting window of {:p}.", self);
964
965 let window_jsobject = window.reflector().get_jsobject();
966 let old_js_proxy = self.reflector.get_jsobject();
967 assert!(!window_jsobject.get().is_null());
968 assert_ne!(
969 ((*get_object_class(window_jsobject.get())).flags & JSCLASS_IS_GLOBAL),
970 0
971 );
972
973 let mut realm = AutoRealm::new_from_handle(cx, window_jsobject);
974 let cx = &mut realm;
975
976 SetProxyReservedSlot(old_js_proxy.get(), 0, &PrivateValue(ptr::null_mut()));
978 SetProxyReservedSlot(
980 old_js_proxy.get(),
981 CROSS_ORIGIN_PROPERTY_HOLDER_WEAK_MAP_SLOT,
982 &UndefinedValue(),
983 );
984
985 rooted!(&in(cx) let new_js_proxy = WindowProxyHandler::proxy_handler().new_window_proxy(cx, window_jsobject));
990 SetProxyReservedSlot(new_js_proxy.get(), 0, &PrivateValue(ptr::null_mut()));
993 debug!(
994 "Transplanting proxy from {:p} to {:p}.",
995 old_js_proxy.get(),
996 new_js_proxy.get()
997 );
998 rooted!(&in(cx) let new_js_proxy = JS_TransplantObject(cx, old_js_proxy, new_js_proxy.handle()));
999 debug!("Transplanted proxy is {:p}.", new_js_proxy.get());
1000
1001 SetProxyReservedSlot(
1003 new_js_proxy.get(),
1004 0,
1005 &PrivateValue(self as *const _ as *const libc::c_void),
1006 );
1007
1008 SetWindowProxy(cx, window_jsobject, new_js_proxy.handle());
1010
1011 debug!(
1013 "Setting reflector of {:p} to {:p}.",
1014 self,
1015 new_js_proxy.get()
1016 );
1017 self.reflector.rootable().set(new_js_proxy.get());
1018 }
1019 }
1020
1021 pub(crate) fn set_pipeline_id(&self, pipeline_id: PipelineId) {
1022 self.currently_active.set(Some(pipeline_id));
1023 }
1024
1025 pub(crate) fn set_currently_active(&self, cx: &mut JSContext, window: &Window) {
1026 if let Some(pipeline_id) = self.currently_active() &&
1027 pipeline_id == window.pipeline_id()
1028 {
1029 return debug!(
1030 "Attempt to set the currently active window to the currently active window."
1031 );
1032 }
1033
1034 let global_scope = window.as_global_scope();
1035 self.set_window(cx, global_scope);
1036 self.currently_active.set(Some(global_scope.pipeline_id()));
1037 }
1038
1039 pub(crate) fn unset_currently_active(&self, cx: &mut JSContext) {
1040 if self.currently_active().is_none() {
1041 return debug!(
1042 "Attempt to unset the currently active window on a windowproxy that does not have one."
1043 );
1044 }
1045 let globalscope = self.global();
1046 let window = DissimilarOriginWindow::new(cx, &globalscope, self);
1047 self.set_window(cx, window.upcast());
1048 self.currently_active.set(None);
1049 }
1050
1051 pub(crate) fn currently_active(&self) -> Option<PipelineId> {
1052 self.currently_active.get()
1053 }
1054
1055 pub(crate) fn get_name(&self) -> DOMString {
1056 self.name.borrow().clone().root()
1057 }
1058
1059 pub(crate) fn set_name(&self, name: DOMString) {
1060 *self.name.borrow_mut() = name.traced();
1061 }
1062}
1063
1064#[derive(Debug, Deserialize, Serialize)]
1076pub(crate) struct CreatorBrowsingContextInfo {
1077 url: Option<ServoUrl>,
1079
1080 origin: Option<ImmutableOrigin>,
1082}
1083
1084impl CreatorBrowsingContextInfo {
1085 pub(crate) fn from(
1086 parent: Option<&WindowProxy>,
1087 opener: Option<&WindowProxy>,
1088 ) -> CreatorBrowsingContextInfo {
1089 let creator = match (parent, opener) {
1090 (Some(parent), _) => parent.document(),
1091 (None, Some(opener)) => opener.document(),
1092 (None, None) => None,
1093 };
1094
1095 let url = creator.as_deref().map(|document| document.url());
1096 let origin = creator
1097 .as_deref()
1098 .map(|document| document.origin().immutable().clone());
1099
1100 CreatorBrowsingContextInfo { url, origin }
1101 }
1102}
1103
1104fn tokenize_open_features(features: DOMString) -> IndexMap<String, String> {
1106 let is_feature_sep = |c: char| c.is_ascii_whitespace() || ['=', ','].contains(&c);
1107 let mut tokenized_features = IndexMap::new();
1109 let features = features.str();
1111 let mut iter = features.chars();
1112 let mut cur = iter.next();
1113
1114 while cur.is_some() {
1116 let mut name = String::new();
1118 let mut value = String::new();
1119 while let Some(cur_char) = cur {
1121 if !is_feature_sep(cur_char) {
1122 break;
1123 }
1124 cur = iter.next();
1125 }
1126 while let Some(cur_char) = cur {
1128 if is_feature_sep(cur_char) {
1129 break;
1130 }
1131 name.push(cur_char.to_ascii_lowercase());
1132 cur = iter.next();
1133 }
1134 let normalized_name = String::from(match name.as_ref() {
1136 "screenx" => "left",
1137 "screeny" => "top",
1138 "innerwidth" => "width",
1139 "innerheight" => "height",
1140 _ => name.as_ref(),
1141 });
1142 while let Some(cur_char) = cur {
1144 if cur_char == '=' || cur_char == ',' || !is_feature_sep(cur_char) {
1145 break;
1146 }
1147 cur = iter.next();
1148 }
1149 if cur.is_some() && is_feature_sep(cur.unwrap()) {
1151 while let Some(cur_char) = cur {
1153 if !is_feature_sep(cur_char) || cur_char == ',' {
1154 break;
1155 }
1156 cur = iter.next();
1157 }
1158 while let Some(cur_char) = cur {
1160 if is_feature_sep(cur_char) {
1161 break;
1162 }
1163 value.push(cur_char.to_ascii_lowercase());
1164 cur = iter.next();
1165 }
1166 }
1167 if !name.is_empty() {
1169 tokenized_features.insert(normalized_name, value);
1170 }
1171 }
1172 tokenized_features
1174}
1175
1176fn parse_open_feature_boolean(tokenized_features: &IndexMap<String, String>, name: &str) -> bool {
1178 if let Some(value) = tokenized_features.get(name) {
1179 if value.is_empty() || value == "yes" {
1181 return true;
1182 }
1183 if let Ok(int) = parse_integer(value.chars()) {
1185 return int != 0;
1186 }
1187 }
1188 false
1190}
1191
1192#[expect(unsafe_code)]
1193fn window_proxy_target(proxy: HandleObject) -> *mut JSObject {
1194 let mut slot = UndefinedValue();
1195 unsafe { GetProxyPrivate(proxy.as_raw(), &mut slot) };
1196 slot.to_object()
1197}
1198
1199#[expect(unsafe_code)]
1201unsafe extern "C" fn get_own_property_descriptor(
1202 cx: *mut RawJSContext,
1203 proxy: RawHandleObject,
1204 id: RawHandleId,
1205 property_descriptor: RawMutableHandle<PropertyDescriptor>,
1206 is_none: *mut bool,
1207) -> bool {
1208 let mut cx = unsafe {
1209 JSContext::from_ptr(NonNull::new(cx).expect("JSContext should not be null in SM hook"))
1212 };
1213 let mut cx = CurrentRealm::assert(&mut cx);
1214 let cx = &mut cx;
1215 let proxy = unsafe { Handle::from_raw(proxy) };
1216 let id = unsafe { Handle::from_raw(id) };
1217 let mut property_descriptor = unsafe { MutableHandle::from_raw(property_descriptor) };
1218 let is_none = unsafe { &mut *is_none };
1219
1220 rooted!(&in(cx) let target = window_proxy_target(proxy));
1222 let window = WindowOrDissimilarOriginWindow::new(cx, target.handle());
1223
1224 if let Some(index) = get_array_index_from_id(id) {
1227 if let Some(window_proxy) = window.window_proxy_for_child_navigable_at_index(index) {
1231 rooted!(&in(cx) let mut window_proxy_jsval = UndefinedValue());
1238 window_proxy.to_jsval(cx, window_proxy_jsval.handle_mut());
1239
1240 set_property_descriptor(
1243 property_descriptor,
1244 window_proxy_jsval.handle(),
1245 (JSPROP_ENUMERATE | JSPROP_READONLY) as u32,
1246 is_none,
1247 );
1248 return true;
1249 } else {
1250 *is_none = true;
1252 if is_platform_object_same_origin(cx, proxy) {
1255 return true;
1256 }
1257 return report_cross_origin_denial::<DomTypeHolder>(cx, id, "get");
1259 }
1260 }
1261
1262 if is_platform_object_same_origin(cx, proxy) {
1264 return JS_GetOwnPropertyDescriptorById(
1265 cx,
1266 target.handle(),
1267 id,
1268 property_descriptor,
1269 is_none,
1270 );
1271 }
1272
1273 if !cross_origin_get_own_property_helper(
1275 cx,
1276 proxy,
1277 window.cross_origin_properties(),
1278 id,
1279 property_descriptor.reborrow(),
1280 is_none,
1281 ) {
1282 return false;
1283 }
1284
1285 if !*is_none {
1287 return true;
1288 }
1289
1290 if let Some(named_child_navigable) = window.named_child_navigable(cx, id) {
1293 rooted!(&in(cx) let mut window_proxy_value = UndefinedValue());
1295 named_child_navigable.to_jsval(cx, window_proxy_value.handle_mut());
1296 set_property_descriptor(
1299 property_descriptor.reborrow(),
1300 window_proxy_value.handle(),
1301 JSPROP_READONLY as u32,
1302 is_none,
1303 );
1304 return true;
1305 }
1306
1307 cross_origin_property_fallback::<DomTypeHolder>(
1309 cx,
1310 proxy,
1311 id,
1312 property_descriptor.reborrow(),
1313 is_none,
1314 )
1315}
1316
1317#[expect(unsafe_code)]
1319unsafe extern "C" fn define_property(
1320 cx: *mut RawJSContext,
1321 proxy: RawHandleObject,
1322 id: RawHandleId,
1323 desc: RawHandle<PropertyDescriptor>,
1324 res: *mut ObjectOpResult,
1325) -> bool {
1326 let mut cx = unsafe {
1327 JSContext::from_ptr(NonNull::new(cx).expect("JSContext should not be null in SM hook"))
1330 };
1331 let mut cx = CurrentRealm::assert(&mut cx);
1332 let cx = &mut cx;
1333 let id = unsafe { Handle::from_raw(id) };
1334 let proxy = unsafe { Handle::from_raw(proxy) };
1335
1336 if is_platform_object_same_origin(cx, proxy) {
1341 if get_array_index_from_id(id).is_some() {
1343 unsafe {
1348 (*res).code_ = JSErrNum::JSMSG_CANT_DEFINE_WINDOW_ELEMENT as usize;
1349 }
1350 return true;
1351 }
1352
1353 rooted!(&in(cx) let target = window_proxy_target(proxy));
1355 return unsafe {
1356 JS_DefinePropertyById(cx.raw_cx(), target.handle().into(), id.into(), desc, res)
1357 };
1358 }
1359
1360 report_cross_origin_denial::<DomTypeHolder>(cx, id, "define")
1362}
1363
1364#[expect(unsafe_code)]
1365unsafe extern "C" fn has(
1366 cx: *mut RawJSContext,
1367 proxy: RawHandleObject,
1368 id: RawHandleId,
1369 bp: *mut bool,
1370) -> bool {
1371 unsafe { has_or_has_own(cx, proxy, id, bp, IncludePrototypes::Yes) }
1372}
1373
1374#[expect(unsafe_code)]
1375unsafe extern "C" fn has_own(
1376 cx: *mut RawJSContext,
1377 proxy: RawHandleObject,
1378 id: RawHandleId,
1379 bp: *mut bool,
1380) -> bool {
1381 unsafe { has_or_has_own(cx, proxy, id, bp, IncludePrototypes::No) }
1382}
1383
1384enum IncludePrototypes {
1385 Yes,
1386 No,
1387}
1388
1389#[expect(unsafe_code)]
1390unsafe fn has_or_has_own(
1391 cx: *mut RawJSContext,
1392 proxy: RawHandleObject,
1393 id: RawHandleId,
1394 bp: *mut bool,
1395 include_prototypes: IncludePrototypes,
1396) -> bool {
1397 let mut cx = unsafe { JSContext::from_ptr(ptr::NonNull::new(cx).unwrap()) };
1398 let mut cx = CurrentRealm::assert(&mut cx);
1399 let cx = &mut cx;
1400 let proxy = unsafe { Handle::from_raw(proxy) };
1401 let id = unsafe { Handle::from_raw(id) };
1402
1403 rooted!(&in(cx) let target = window_proxy_target(proxy));
1404 let window = WindowOrDissimilarOriginWindow::new(cx, target.handle());
1405
1406 let (success, found) = if is_platform_object_same_origin(cx, proxy) {
1407 if let Some(array_index) = get_array_index_from_id(id) &&
1408 window
1409 .window_proxy_for_child_navigable_at_index(array_index)
1410 .is_some()
1411 {
1412 unsafe { *bp = true };
1413 return true;
1414 }
1415
1416 let mut found = false;
1417 let success = match include_prototypes {
1418 IncludePrototypes::Yes => unsafe {
1419 JS_HasPropertyById(cx, target.handle(), id, &mut found)
1420 },
1421 IncludePrototypes::No => unsafe {
1422 JS_HasOwnPropertyById(cx, target.handle(), id, &mut found)
1423 },
1424 };
1425 (success, found)
1426 } else {
1427 rooted!(&in(cx) let mut property_descriptor = PropertyDescriptor::default());
1428 let mut is_none = false;
1429 let success = unsafe {
1430 get_own_property_descriptor(
1431 cx.raw_cx(),
1432 proxy.into(),
1433 id.into(),
1434 property_descriptor.handle_mut().into(),
1435 &mut is_none,
1436 )
1437 };
1438 (success, !is_none)
1439 };
1440
1441 if !success {
1442 return false;
1443 }
1444 unsafe { *bp = found };
1445 true
1446}
1447
1448#[expect(unsafe_code)]
1450unsafe extern "C" fn get(
1451 cx: *mut RawJSContext,
1452 proxy: RawHandleObject,
1453 receiver: RawHandleValue,
1454 id: RawHandleId,
1455 return_value: RawMutableHandleValue,
1456) -> bool {
1457 let mut cx = unsafe {
1458 JSContext::from_ptr(NonNull::new(cx).expect("JSContext should not be null in SM hook"))
1460 };
1461 let mut cx = CurrentRealm::assert(&mut cx);
1462 let cx = &mut cx;
1463 let proxy = unsafe { Handle::from_raw(proxy) };
1464 let receiver = unsafe { Handle::from_raw(receiver) };
1465 let id = unsafe { Handle::from_raw(id) };
1466 let return_value = unsafe { MutableHandle::from_raw(return_value) };
1467
1468 rooted!(&in(cx) let target = window_proxy_target(proxy));
1470 let window = WindowOrDissimilarOriginWindow::new(cx, target.handle());
1471
1472 if is_platform_object_same_origin(cx, proxy) {
1479 if let Some(index) = get_array_index_from_id(id) &&
1480 let Some(window_proxy) = window.window_proxy_for_child_navigable_at_index(index)
1481 {
1482 window_proxy.to_jsval(cx, return_value);
1483 return true;
1484 }
1485
1486 return unsafe { JS_ForwardGetPropertyTo(cx, target.handle(), id, receiver, return_value) };
1487 }
1488
1489 proxyhandler::cross_origin_get::<DomTypeHolder>(cx, proxy, receiver, id, return_value)
1491}
1492
1493#[expect(unsafe_code)]
1495unsafe extern "C" fn set(
1496 cx: *mut RawJSContext,
1497 proxy: RawHandleObject,
1498 id: RawHandleId,
1499 v: RawHandleValue,
1500 receiver: RawHandleValue,
1501 res: *mut ObjectOpResult,
1502) -> bool {
1503 let mut cx = unsafe {
1504 JSContext::from_ptr(NonNull::new(cx).expect("JSContext should not be null in SM hook"))
1506 };
1507 let mut cx = CurrentRealm::assert(&mut cx);
1508 let cx = &mut cx;
1509
1510 let proxy = unsafe { Handle::from_raw(proxy) };
1519 let id = unsafe { Handle::from_raw(id) };
1520
1521 if is_platform_object_same_origin(cx, proxy) {
1523 if get_array_index_from_id(id).is_some() {
1525 unsafe { (*res).code_ = JSErrNum::JSMSG_READ_ONLY as usize };
1527 return true;
1528 }
1529
1530 rooted!(&in(cx) let target = window_proxy_target(proxy));
1532 return unsafe {
1533 JS_ForwardSetPropertyTo(
1534 cx.raw_cx(),
1535 target.handle().into(),
1536 id.into(),
1537 v,
1538 receiver,
1539 res,
1540 )
1541 };
1542 }
1543
1544 let receiver = unsafe { HandleValue::from_raw(receiver) };
1546 unsafe { cross_origin_set::<DomTypeHolder>(cx, proxy, id, v, receiver, res) }
1547}
1548
1549#[expect(unsafe_code)]
1550unsafe extern "C" fn get_prototype_if_ordinary(
1551 _: *mut RawJSContext,
1552 _: RawHandleObject,
1553 is_ordinary: *mut bool,
1554 _: RawMutableHandleObject,
1555) -> bool {
1556 unsafe { *is_ordinary = false };
1569 true
1570}
1571
1572#[expect(unsafe_code)]
1574unsafe extern "C" fn get_prototype(
1575 cx: *mut RawJSContext,
1576 proxy: RawHandleObject,
1577 result: RawMutableHandleObject,
1578) -> bool {
1579 let mut cx = unsafe { JSContext::from_ptr(ptr::NonNull::new(cx).unwrap()) };
1580 let mut realm = CurrentRealm::assert(&mut cx);
1581 let proxy = unsafe { Handle::from_raw(proxy) };
1582 let result = unsafe { MutableHandleObject::from_raw(result) };
1583 maybe_cross_origin_get_prototype::<DomTypeHolder>(
1584 &mut realm,
1585 proxy,
1586 GetProtoObject::<DomTypeHolder>,
1587 result,
1588 )
1589}
1590
1591#[expect(unsafe_code)]
1593unsafe extern "C" fn delete(
1594 cx: *mut RawJSContext,
1595 proxy: RawHandleObject,
1596 id: RawHandleId,
1597 result: *mut ObjectOpResult,
1598) -> bool {
1599 let mut cx = unsafe { JSContext::from_ptr(ptr::NonNull::new(cx).unwrap()) };
1600 let mut cx = CurrentRealm::assert(&mut cx);
1601 let cx = &mut cx;
1602 let proxy = unsafe { Handle::from_raw(proxy) };
1603 let id = unsafe { Handle::from_raw(id) };
1604
1605 rooted!(&in(cx) let target = window_proxy_target(proxy));
1607 let window = WindowOrDissimilarOriginWindow::new(cx, target.handle());
1608
1609 if is_platform_object_same_origin(cx, proxy) {
1611 if let Some(array_index) = get_array_index_from_id(id) {
1613 let code = if window
1615 .window_proxy_for_child_navigable_at_index(array_index)
1616 .is_none()
1617 {
1618 0 } else {
1621 JSErrNum::JSMSG_CANT_DELETE_WINDOW_ELEMENT as usize
1623 };
1624 unsafe { (*result).code_ = code };
1625 return true;
1626 }
1627 rooted!(&in(cx) let target = window_proxy_target(proxy));
1629 return unsafe {
1630 JS_DeletePropertyById(cx.raw_cx(), target.handle().into(), id.into(), result)
1631 };
1632 }
1633
1634 report_cross_origin_denial::<DomTypeHolder>(cx, id, "delete")
1636}
1637
1638#[expect(unsafe_code)]
1640unsafe extern "C" fn own_property_keys(
1641 cx: *mut RawJSContext,
1642 proxy: RawHandleObject,
1643 property_keys: RawMutableHandleIdVector,
1644) -> bool {
1645 let mut cx = unsafe { JSContext::from_ptr(ptr::NonNull::new(cx).unwrap()) };
1646 let mut cx = CurrentRealm::assert(&mut cx);
1647 let cx = &mut cx;
1648 let proxy = unsafe { Handle::from_raw(proxy) };
1649
1650 rooted!(&in(cx) let target = window_proxy_target(proxy));
1652 let window = WindowOrDissimilarOriginWindow::new(cx, target.handle());
1653
1654 let max_properties = window.iframe_count();
1656
1657 rooted!(&in(cx) let mut rooted_index_jsid: jsid);
1659 for index in 0..max_properties {
1660 unsafe { int_to_jsid(index as i32, rooted_index_jsid.handle_mut()) };
1661 unsafe { AppendToIdVector(property_keys, rooted_index_jsid.handle()) };
1662 }
1663
1664 if is_platform_object_same_origin(cx, proxy) {
1665 return GetPropertyKeys(
1668 cx,
1669 target.handle(),
1670 JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS,
1671 property_keys,
1672 );
1673 }
1674
1675 cross_origin_own_property_keys(cx, proxy, window.cross_origin_properties(), property_keys)
1677}
1678
1679#[expect(unsafe_code)]
1684unsafe extern "C" fn get_own_enumerable_property_keys(
1685 cx: *mut RawJSContext,
1686 proxy: RawHandleObject,
1687 property_keys: RawMutableHandleIdVector,
1688) -> bool {
1689 let mut cx = unsafe { JSContext::from_ptr(ptr::NonNull::new(cx).unwrap()) };
1690 let mut cx = CurrentRealm::assert(&mut cx);
1691 let cx = &mut cx;
1692 let proxy = unsafe { Handle::from_raw(proxy) };
1693
1694 rooted!(&in(cx) let target = window_proxy_target(proxy));
1696 let window = WindowOrDissimilarOriginWindow::new(cx, target.handle());
1697
1698 let max_properties = window.iframe_count();
1700
1701 rooted!(&in(cx) let mut rooted_index_jsid: jsid);
1703 for index in 0..max_properties {
1704 unsafe { int_to_jsid(index as i32, rooted_index_jsid.handle_mut()) };
1705 unsafe { AppendToIdVector(property_keys, rooted_index_jsid.handle()) };
1706 }
1707
1708 if is_platform_object_same_origin(cx, proxy) {
1709 return GetPropertyKeys(cx, target.handle(), JSITER_OWNONLY, property_keys);
1712 }
1713
1714 true
1717}
1718
1719#[expect(unsafe_code)]
1720unsafe extern "C" fn enumerate(
1721 cx: *mut RawJSContext,
1722 proxy: RawHandleObject,
1723 property_keys: RawMutableHandleIdVector,
1724) -> bool {
1725 let mut cx = unsafe { JSContext::from_ptr(ptr::NonNull::new(cx).unwrap()) };
1729 let cx = &mut cx;
1730 let proxy = unsafe { Handle::from_raw(proxy) };
1731
1732 GetPropertyKeys(cx, proxy, 0, property_keys)
1733}
1734
1735static PROXY_TRAPS: ProxyTraps = ProxyTraps {
1736 enter: None,
1737 getOwnPropertyDescriptor: Some(get_own_property_descriptor),
1738 defineProperty: Some(define_property),
1739 ownPropertyKeys: Some(own_property_keys),
1740 delete_: Some(delete),
1741 enumerate: Some(enumerate),
1742 getPrototypeIfOrdinary: Some(get_prototype_if_ordinary),
1743 getPrototype: Some(get_prototype),
1744 setPrototype: Some(maybe_cross_origin_set_prototype_rawcx),
1745 setImmutablePrototype: None,
1746 preventExtensions: Some(prevent_extensions),
1747 isExtensible: Some(is_extensible),
1748 has: Some(has),
1749 get: Some(get),
1750 set: Some(set),
1751 call: None,
1752 construct: None,
1753 hasOwn: Some(has_own),
1754 getOwnEnumerablePropertyKeys: Some(get_own_enumerable_property_keys),
1755 nativeCall: None,
1756 objectClassIs: None,
1757 className: None,
1758 fun_toString: None,
1759 boxedValue_unbox: None,
1760 defaultValue: None,
1761 trace: Some(trace),
1762 finalize: Some(finalize),
1763 objectMoved: None,
1764 isCallable: None,
1765 isConstructor: None,
1766};
1767
1768pub(crate) struct WindowProxyHandler(*const libc::c_void);
1771
1772impl MallocSizeOf for WindowProxyHandler {
1773 fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
1774 0
1776 }
1777}
1778
1779#[expect(unsafe_code)]
1781unsafe impl Send for WindowProxyHandler {}
1782#[expect(unsafe_code)]
1784unsafe impl Sync for WindowProxyHandler {}
1785
1786#[expect(unsafe_code)]
1787impl WindowProxyHandler {
1788 fn new(traps: &ProxyTraps) -> Self {
1789 let ptr = unsafe { CreateWrapperProxyHandler(traps) };
1791 assert!(!ptr.is_null());
1792 Self(ptr)
1793 }
1794
1795 pub(crate) fn proxy_handler() -> &'static Self {
1797 use std::sync::OnceLock;
1798 static SINGLETON: OnceLock<WindowProxyHandler> = OnceLock::new();
1804 SINGLETON.get_or_init(|| Self::new(&PROXY_TRAPS))
1805 }
1806
1807 fn new_window_proxy(
1810 &self,
1811 cx: &mut JSContext,
1812 window_jsobject: js::gc::HandleObject,
1813 ) -> *mut JSObject {
1814 let obj = unsafe { NewWindowProxy(cx, window_jsobject, self.0) };
1815 assert!(!obj.is_null());
1816 obj
1817 }
1818}
1819
1820#[expect(unsafe_code)]
1821impl Drop for WindowProxyHandler {
1822 fn drop(&mut self) {
1823 unsafe {
1826 DeleteWrapperProxyHandler(self.0);
1827 }
1828 }
1829}
1830
1831#[expect(unsafe_code)]
1834unsafe extern "C" fn finalize(_fop: *mut GCContext, obj: *mut JSObject) {
1835 let mut slot = UndefinedValue();
1836 unsafe { GetProxyReservedSlot(obj, 0, &mut slot) };
1837 let this = slot.to_private() as *mut WindowProxy;
1838 if this.is_null() {
1839 return;
1841 }
1842 unsafe {
1843 (*this).reflector.drop_memory(&*this);
1844 let jsobject = (*this).reflector.get_jsobject().get();
1845 debug!(
1846 "WindowProxy finalize: {:p}, with reflector {:p} from {:p}.",
1847 this, jsobject, obj
1848 );
1849 let _ = Box::from_raw(this);
1850 }
1851}
1852
1853#[expect(unsafe_code)]
1854unsafe extern "C" fn trace(trc: *mut JSTracer, obj: *mut JSObject) {
1855 let mut slot = UndefinedValue();
1856 unsafe { GetProxyReservedSlot(obj, 0, &mut slot) };
1857 let this = slot.to_private() as *const WindowProxy;
1858 if this.is_null() {
1859 return;
1861 }
1862 unsafe { (*this).trace(trc) };
1863}
1864
1865enum WindowOrDissimilarOriginWindow {
1868 Window(DomRoot<Window>),
1869 DissimilarOriginWindow(DomRoot<DissimilarOriginWindow>),
1870}
1871
1872impl WindowOrDissimilarOriginWindow {
1873 fn new(cx: &mut JSContext, handle_object: HandleObject) -> Self {
1874 if let Ok(window) = root_from_handleobject::<Window>(cx, handle_object) {
1875 Self::Window(window)
1876 } else if let Ok(window) =
1877 root_from_handleobject::<DissimilarOriginWindow>(cx, handle_object)
1878 {
1879 Self::DissimilarOriginWindow(window)
1880 } else {
1881 unreachable!("WindowProxy should always be backed by some kind of window");
1882 }
1883 }
1884
1885 fn global_scope(&self) -> DomRoot<GlobalScope> {
1886 match self {
1887 WindowOrDissimilarOriginWindow::Window(window) => window.global(),
1888 WindowOrDissimilarOriginWindow::DissimilarOriginWindow(window) => window.global(),
1889 }
1890 }
1891
1892 fn window_proxy(&self) -> DomRoot<WindowProxy> {
1893 match self {
1894 WindowOrDissimilarOriginWindow::Window(window) => window.window_proxy(),
1895 WindowOrDissimilarOriginWindow::DissimilarOriginWindow(window) => window.window_proxy(),
1896 }
1897 }
1898
1899 #[expect(unsafe_code)]
1900 fn cross_origin_properties(&self) -> &'static CrossOriginProperties {
1901 match self {
1902 WindowOrDissimilarOriginWindow::Window(..) => unsafe {
1903 WindowBinding::CROSS_ORIGIN_PROPERTIES.get()
1904 },
1905 WindowOrDissimilarOriginWindow::DissimilarOriginWindow(..) => unsafe {
1906 DissimilarOriginWindowBinding::CROSS_ORIGIN_PROPERTIES.get()
1907 },
1908 }
1909 }
1910
1911 fn iframe_count(&self) -> u32 {
1912 match self {
1913 WindowOrDissimilarOriginWindow::Window(window) => window.Length(),
1914 WindowOrDissimilarOriginWindow::DissimilarOriginWindow(window) => window.Length(),
1915 }
1916 }
1917
1918 fn window_proxy_for_child_navigable_at_index(
1919 &self,
1920 index: u32,
1921 ) -> Option<DomRoot<WindowProxy>> {
1922 let window_proxy = self.window_proxy();
1923 let browsing_context_id = if let Some(document) = window_proxy.document() {
1924 document.iframes().at_insertion_index(index as usize)?
1925 } else {
1926 let parent_browsing_context_id = window_proxy.browsing_context_id();
1927 let (result_sender, result_receiver) = generic_channel::channel().unwrap();
1928 let _ = self.global_scope().script_to_constellation_chan().send(
1929 ScriptToConstellationMessage::GetChildBrowsingContextId(
1930 parent_browsing_context_id,
1931 index as usize,
1932 result_sender,
1933 ),
1934 );
1935 result_receiver.recv().ok().flatten()?
1936 };
1937 ScriptThread::window_proxies().find_window_proxy(browsing_context_id)
1938 }
1939
1940 fn named_child_navigable(
1961 &self,
1962 cx: &mut CurrentRealm,
1963 id: HandleId,
1964 ) -> Option<DomRoot<WindowProxy>> {
1965 let Self::Window(window) = &self else {
1967 return None;
1968 };
1969
1970 let name = jsid_to_string(cx, id)?;
1971 if name.is_empty() {
1972 return None;
1973 }
1974 window
1975 .Document()
1976 .iframes()
1977 .iter()
1978 .filter_map(|iframe| iframe.GetContentWindow())
1979 .find(|window_proxy| window_proxy.get_name() == name)
1980 }
1981}