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, GetPropertyKeys, JS_ForwardGetPropertyTo, JS_GetOwnPropertyDescriptorById,
34 JS_HasOwnPropertyById, JS_HasPropertyById, JS_TransplantObject, NewWindowProxy, SetWindowProxy,
35 int_to_jsid,
36};
37use js::rust::{Handle, MutableHandle, MutableHandleValue, get_object_class};
38use js::typedarray::JSObjectStorage;
39use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
40use net_traits::ReferrerPolicy;
41use net_traits::request::Referrer;
42use script_bindings::cell::DomRefCell;
43use script_bindings::codegen::GenericBindings::DissimilarOriginWindowBinding::{
44 self, DissimilarOriginWindowMethods,
45};
46use script_bindings::codegen::GenericBindings::HTMLIFrameElementBinding::HTMLIFrameElementMethods;
47use script_bindings::codegen::GenericBindings::WindowBinding::{
48 self, GetProtoObject, WindowMethods,
49};
50use script_bindings::conversions::jsid_to_string;
51use script_bindings::proxyhandler::{
52 self, CrossOriginProperties, cross_origin_get_own_property_helper,
53 cross_origin_own_property_keys, cross_origin_property_fallback, cross_origin_set,
54 is_extensible, is_platform_object_same_origin, maybe_cross_origin_get_prototype,
55 maybe_cross_origin_set_prototype_rawcx, prevent_extensions, report_cross_origin_denial,
56 set_property_descriptor,
57};
58use script_bindings::reflector::{DomObject, MutDomObject, Reflector};
59use script_traits::NewPipelineInfo;
60use serde::{Deserialize, Serialize};
61use servo_base::generic_channel;
62use servo_base::generic_channel::GenericSend;
63use servo_base::id::{BrowsingContextId, PipelineId, WebViewId};
64use servo_constellation_traits::{
65 AuxiliaryWebViewCreationRequest, LoadData, LoadOrigin, NavigationHistoryBehavior,
66 ScriptToConstellationMessage, TargetSnapshotParams,
67};
68use servo_url::{ImmutableOrigin, OriginSnapshot, ServoUrl};
69use storage_traits::webstorage_thread::WebStorageThreadMsg;
70use style::attr::parse_integer;
71
72use crate::DomTypeHolder;
73use crate::dom::bindings::conversions::{ToJSValConvertible, root_from_handleobject};
74use crate::dom::bindings::error::{Error, Fallible};
75use crate::dom::bindings::inheritance::Castable;
76use crate::dom::bindings::reflector::DomGlobal;
77use crate::dom::bindings::root::{Dom, DomRoot};
78use crate::dom::bindings::settings_stack::maybe_entry_global;
79use crate::dom::bindings::str::{DOMString, USVString};
80use crate::dom::bindings::trace::JSTraceable;
81use crate::dom::bindings::utils::get_array_index_from_id;
82use crate::dom::dissimilaroriginwindow::DissimilarOriginWindow;
83use crate::dom::document::Document;
84use crate::dom::element::Element;
85use crate::dom::globalscope::GlobalScope;
86use crate::dom::node::node::NodeTraits;
87use crate::dom::window::Window;
88use crate::event_loop::script_thread::{ScriptThread, with_script_thread};
89use crate::event_loop::script_window_proxies::ScriptWindowProxies;
90use crate::navigation::navigate;
91
92#[dom_struct]
93pub(crate) struct WindowProxy {
98 reflector: Reflector,
103
104 #[no_trace]
108 browsing_context_id: BrowsingContextId,
109
110 #[no_trace]
112 opener: Option<BrowsingContextId>,
113
114 #[no_trace]
117 webview_id: WebViewId,
118
119 name: DomRefCell<DOMString>,
122 #[no_trace]
128 currently_active: Cell<Option<PipelineId>>,
129
130 discarded: Cell<bool>,
132
133 disowned: Cell<bool>,
135
136 is_closing: Cell<bool>,
138
139 frame_element: Option<Dom<Element>>,
143
144 parent: Option<Dom<WindowProxy>>,
146
147 delaying_load_events_mode: Cell<bool>,
149
150 #[no_trace]
152 creator_url: Option<ServoUrl>,
153
154 #[no_trace]
156 creator_origin: Option<ImmutableOrigin>,
157
158 #[conditional_malloc_size_of]
160 script_window_proxies: Rc<ScriptWindowProxies>,
161}
162
163impl WindowProxy {
164 fn new_inherited(
165 browsing_context_id: BrowsingContextId,
166 webview_id: WebViewId,
167 currently_active: Option<PipelineId>,
168 frame_element: Option<&Element>,
169 parent: Option<&WindowProxy>,
170 opener: Option<BrowsingContextId>,
171 creator: CreatorBrowsingContextInfo,
172 ) -> WindowProxy {
173 let name = frame_element.map_or(DOMString::new(), |e| {
174 e.get_string_attribute(&local_name!("name"))
175 });
176 WindowProxy {
177 reflector: Reflector::new(),
178 browsing_context_id,
179 webview_id,
180 name: DomRefCell::new(name),
181 currently_active: Cell::new(currently_active),
182 discarded: Cell::new(false),
183 disowned: Cell::new(false),
184 is_closing: Cell::new(false),
185 frame_element: frame_element.map(Dom::from_ref),
186 parent: parent.map(Dom::from_ref),
187 delaying_load_events_mode: Cell::new(false),
188 opener,
189 creator_url: creator.url,
190 creator_origin: creator.origin,
191 script_window_proxies: ScriptThread::window_proxies(),
192 }
193 }
194
195 #[expect(unsafe_code)]
196 #[expect(clippy::too_many_arguments)]
197 pub(crate) fn new(
198 cx: &mut JSContext,
199 window: &Window,
200 browsing_context_id: BrowsingContextId,
201 webview_id: WebViewId,
202 frame_element: Option<&Element>,
203 parent: Option<&WindowProxy>,
204 opener: Option<BrowsingContextId>,
205 creator: CreatorBrowsingContextInfo,
206 ) -> DomRoot<WindowProxy> {
207 unsafe {
208 let handler = window.windowproxy_handler();
209
210 let window_jsobject = window.reflector().get_jsobject();
211 assert!(!window_jsobject.get().is_null());
212 assert_ne!(
213 ((*get_object_class(window_jsobject.get())).flags & JSCLASS_IS_GLOBAL),
214 0
215 );
216
217 let mut realm = AutoRealm::new_from_handle(cx, window_jsobject);
218 let cx = &mut realm;
219
220 rooted!(&in(cx) let js_proxy = handler.new_window_proxy(cx, window_jsobject));
222 assert!(!js_proxy.is_null());
223
224 let current = Some(window.upcast::<GlobalScope>().pipeline_id());
227 let window_proxy = Box::new(WindowProxy::new_inherited(
228 browsing_context_id,
229 webview_id,
230 current,
231 frame_element,
232 parent,
233 opener,
234 creator,
235 ));
236
237 SetProxyReservedSlot(
240 js_proxy.get(),
241 0,
242 &PrivateValue(&raw const (*window_proxy) as *const libc::c_void),
243 );
244
245 SetWindowProxy(cx, window_jsobject, js_proxy.handle());
247
248 debug!(
250 "Initializing reflector of {:p} to {:p}.",
251 window_proxy,
252 js_proxy.get()
253 );
254 window_proxy
255 .reflector
256 .init_reflector::<WindowProxy>(js_proxy.get());
257 DomRoot::from_ref(&*Box::into_raw(window_proxy))
258 }
259 }
260
261 #[expect(unsafe_code)]
262 pub(crate) fn new_dissimilar_origin(
263 cx: &mut JSContext,
264 global_to_clone_from: &GlobalScope,
265 browsing_context_id: BrowsingContextId,
266 webview_id: WebViewId,
267 parent: Option<&WindowProxy>,
268 opener: Option<BrowsingContextId>,
269 creator: CreatorBrowsingContextInfo,
270 ) -> DomRoot<WindowProxy> {
271 unsafe {
272 let window_proxy = Box::new(WindowProxy::new_inherited(
274 browsing_context_id,
275 webview_id,
276 None,
277 None,
278 parent,
279 opener,
280 creator,
281 ));
282
283 let window = DissimilarOriginWindow::new(cx, global_to_clone_from, &window_proxy);
285 let window_jsobject = window.reflector().get_jsobject();
286 assert!(!window_jsobject.get().is_null());
287 assert_ne!(
288 ((*get_object_class(window_jsobject.get())).flags & JSCLASS_IS_GLOBAL),
289 0
290 );
291
292 let mut realm = AutoRealm::new_from_handle(cx, window_jsobject);
293 let cx = &mut realm;
294
295 let handler = WindowProxyHandler::proxy_handler();
297 rooted!(&in(cx) let js_proxy = handler.new_window_proxy(cx, window_jsobject));
298 assert!(!js_proxy.is_null());
299
300 SetProxyReservedSlot(
303 js_proxy.get(),
304 0,
305 &PrivateValue(&raw const (*window_proxy) as *const libc::c_void),
306 );
307
308 SetWindowProxy(cx, window_jsobject, js_proxy.handle());
310
311 debug!(
313 "Initializing reflector of {:p} to {:p}.",
314 window_proxy,
315 js_proxy.get()
316 );
317 window_proxy
318 .reflector
319 .init_reflector::<WindowProxy>(js_proxy.get());
320 DomRoot::from_ref(&*Box::into_raw(window_proxy))
321 }
322 }
323
324 fn create_auxiliary_browsing_context(
326 &self,
327 cx: &mut JSContext,
328 name: DOMString,
329 noopener: bool,
330 ) -> Option<DomRoot<WindowProxy>> {
331 let (response_sender, response_receiver) = generic_channel::channel().unwrap();
332 let window = self
333 .currently_active
334 .get()
335 .and_then(ScriptThread::find_document)
336 .map(|doc| DomRoot::from_ref(doc.window()))
337 .unwrap();
338
339 let document = self
340 .currently_active
341 .get()
342 .and_then(ScriptThread::find_document)
343 .expect("A WindowProxy creating an auxiliary to have an active document");
344
345 let sandboxing_flag_set = document.active_sandboxing_flag_set();
353 let propagate_sandbox = sandboxing_flag_set
354 .contains(SandboxingFlagSet::SANDBOX_PROPOGATES_TO_AUXILIARY_BROWSING_CONTEXTS_FLAG);
355 let sandboxing_flag_set = if propagate_sandbox {
356 sandboxing_flag_set
357 } else {
358 SandboxingFlagSet::empty()
359 };
360
361 let blank_url = ServoUrl::parse("about:blank").ok().unwrap();
362 let mut load_data = LoadData::new(
363 LoadOrigin::Script(document.origin().snapshot()),
364 blank_url,
365 Some(document.base_url()),
366 Some(window.pipeline_id()),
369 document.global().get_referrer(),
370 document.get_referrer_policy(),
371 None, None,
373 false,
374 sandboxing_flag_set,
375 );
376 load_data.is_initial_about_blank = true;
377 let load_info = AuxiliaryWebViewCreationRequest {
378 load_data: load_data.clone(),
379 opener_webview_id: window.webview_id(),
380 opener_pipeline_id: self.currently_active.get().unwrap(),
381 response_sender,
382 };
383 let constellation_msg = ScriptToConstellationMessage::CreateAuxiliaryWebView(load_info);
384 window.send_to_constellation(constellation_msg);
385
386 let response = response_receiver.recv().unwrap()?;
387 let new_browsing_context_id = BrowsingContextId::from(response.new_webview_id);
388 let new_pipeline_info = NewPipelineInfo {
389 parent_info: None,
390 new_pipeline_id: response.new_pipeline_id,
391 browsing_context_id: new_browsing_context_id,
392 webview_id: response.new_webview_id,
393 opener: Some(self.browsing_context_id),
394 load_data,
395 viewport_details: window.viewport_details(),
396 user_content_manager_id: response.user_content_manager_id,
397 embedder_theme: window.embedder_theme(),
400 target_snapshot_params: TargetSnapshotParams {
401 sandboxing_flags: sandboxing_flag_set,
402 iframe_element_referrer_policy: ReferrerPolicy::EmptyString,
403 },
404 frame_name: None,
405 };
406
407 with_script_thread(|script_thread| {
408 script_thread.spawn_pipeline(cx, new_pipeline_info);
409 });
410
411 let new_window_proxy = ScriptThread::find_document(response.new_pipeline_id)
412 .and_then(|doc| doc.browsing_context())?;
413 if !name.eq_ignore_ascii_case("_blank") {
414 new_window_proxy.set_name(name);
415 }
416 if noopener {
417 new_window_proxy.disown();
418 } else {
419 let (sender, receiver) = generic_channel::channel().unwrap();
424
425 let msg = WebStorageThreadMsg::Clone {
426 sender,
427 src: window.window_proxy().webview_id(),
428 dest: response.new_webview_id,
429 };
430
431 GenericSend::send(document.global().storage_threads(), msg).unwrap();
432 receiver.recv().unwrap();
433 }
434 Some(new_window_proxy)
435 }
436
437 pub(crate) fn start_delaying_load_events_mode(&self) {
439 self.delaying_load_events_mode.set(true);
440 }
441
442 pub(crate) fn stop_delaying_load_events_mode(&self) {
444 self.delaying_load_events_mode.set(false);
445 }
446
447 pub(crate) fn disown(&self) {
449 self.disowned.set(true);
450 }
451
452 pub(crate) fn close(&self) {
455 self.is_closing.set(true);
456 }
457
458 pub(crate) fn is_closing(&self) -> bool {
460 self.is_closing.get()
461 }
462
463 pub(crate) fn opener(&self, cx: &mut CurrentRealm, mut retval: MutableHandleValue) {
465 if self.disowned.get() {
466 return retval.set(NullValue());
467 }
468 let opener_id = match self.opener {
469 Some(opener_browsing_context_id) => opener_browsing_context_id,
470 None => return retval.set(NullValue()),
471 };
472 let parent_browsing_context = self.parent.as_deref();
473 let opener_proxy = match self.script_window_proxies.find_window_proxy(opener_id) {
474 Some(window_proxy) => window_proxy,
475 None => {
476 let sender_pipeline_id = self.currently_active().unwrap();
477 match ScriptThread::get_top_level_for_browsing_context(
478 self.webview_id(),
479 sender_pipeline_id,
480 opener_id,
481 ) {
482 Some(opener_top_id) => {
483 let global_to_clone_from = GlobalScope::from_current_realm(cx);
484 let creator =
485 CreatorBrowsingContextInfo::from(parent_browsing_context, None);
486 WindowProxy::new_dissimilar_origin(
487 cx,
488 &global_to_clone_from,
489 opener_id,
490 opener_top_id,
491 None,
492 None,
493 creator,
494 )
495 },
496 None => return retval.set(NullValue()),
497 }
498 },
499 };
500 if opener_proxy.is_browsing_context_discarded() {
501 return retval.set(NullValue());
502 }
503 opener_proxy.to_jsval(cx, retval);
504 }
505
506 pub(crate) fn open(
508 &self,
509 cx: &mut JSContext,
510 url: USVString,
511 target: DOMString,
512 features: DOMString,
513 ) -> Fallible<Option<DomRoot<WindowProxy>>> {
514 if self.discarded.get() {
520 return Ok(None);
521 }
522 let source_document = maybe_entry_global()
528 .map(|global| global.as_window().Document())
529 .or_else(|| self.document())
530 .expect("Must have an entry global or active document");
531 let url_record = if !url.is_empty() {
533 let Ok(url) = source_document.encoding_parse_a_url(&url) else {
535 return Err(Error::Syntax(Some(format!(
537 "Error parsing URL '{url}' relative to '{}'",
538 source_document.url()
539 ))));
540 };
541 Some(url)
542 } else {
543 None
545 };
546 let non_empty_target = if target.is_empty() {
548 DOMString::from_static("_blank")
549 } else {
550 target
551 };
552 let tokenized_features = tokenize_open_features(features);
554 let noreferrer = parse_open_feature_boolean(&tokenized_features, "noreferrer");
558
559 let noopener = if noreferrer {
562 true
563 } else {
564 parse_open_feature_boolean(&tokenized_features, "noopener")
565 };
566 let (chosen, new) = match self.choose_a_navigable(cx, non_empty_target, noopener) {
576 (Some(chosen), new) => (chosen, new),
577 (None, _) => return Ok(None),
578 };
579 let target_document = match chosen.document() {
582 Some(target_document) => target_document,
583 None => return Ok(None),
584 };
585 let has_trustworthy_ancestor_origin = if new {
586 target_document.has_trustworthy_ancestor_or_current_origin()
587 } else {
588 false
589 };
590 let target_window = target_document.window();
591 let url_record = url_record.unwrap_or(ServoUrl::parse("about:blank").unwrap());
593 if !url_record.matches_about_blank() {
597 let referrer = if noreferrer {
598 Referrer::NoReferrer
599 } else {
600 target_window.as_global_scope().get_referrer()
601 };
602 let csp_list = source_document.get_csp_list().clone();
604 target_document.set_csp_list(csp_list);
605
606 let mut load_data = LoadData::new(
610 LoadOrigin::Script(source_document.origin().snapshot()),
611 url_record,
612 target_document.about_base_url(),
613 Some(target_window.pipeline_id()),
614 referrer,
615 target_document.get_referrer_policy(),
616 Some(target_window.as_global_scope().is_secure_context()),
617 Some(target_document.insecure_requests_policy()),
618 has_trustworthy_ancestor_origin,
619 target_document.creation_sandboxing_flag_set_considering_parent_iframe(),
620 );
621
622 if load_data.url.scheme() == "javascript" {
625 let existing_global = source_document.global();
626
627 if !ScriptThread::can_navigate_to_javascript_url(
629 cx,
630 &existing_global,
631 target_window.as_global_scope(),
632 &mut load_data,
633 None,
634 ) {
635 return Ok(target_document.browsing_context());
637 }
638 }
639
640 let history_handling = if new {
641 NavigationHistoryBehavior::Replace
642 } else {
643 NavigationHistoryBehavior::Push
644 };
645 navigate(cx, target_window, history_handling, false, load_data);
646 }
647 if noopener {
649 return Ok(None);
650 }
651 Ok(target_document.browsing_context())
653 }
654
655 pub(crate) fn choose_a_navigable(
657 &self,
658 cx: &mut JSContext,
659 name: DOMString,
660 noopener: bool,
661 ) -> (Option<DomRoot<WindowProxy>>, bool) {
662 let sandboxing_flag_set = self
668 .document()
669 .map(|document| document.active_sandboxing_flag_set())
670 .unwrap_or_default();
671
672 let chosen = if name.is_empty() || name.eq_ignore_ascii_case("_self") {
673 Some((Some(DomRoot::from_ref(self)), false))
676 } else if name.eq_ignore_ascii_case("_parent") {
677 Some(
681 self.parent()
682 .map(|parent| (Some(DomRoot::from_ref(parent)), false))
683 .unwrap_or_else(|| (Some(DomRoot::from_ref(self)), false)),
684 )
685 } else if name.eq_ignore_ascii_case("_top") {
686 Some((Some(DomRoot::from_ref(self.top())), false))
689 } else if !name.eq_ignore_ascii_case("_blank") {
690 self.find_navigable_by_target_name(&name)
698 .map(|proxy| (Some(proxy), false))
699 } else {
700 None
701 };
702
703 if let Some(chosen) = chosen {
704 return chosen;
705 }
706
707 if sandboxing_flag_set
721 .contains(SandboxingFlagSet::SANDBOXED_AUXILIARY_NAVIGATION_BROWSING_CONTEXT_FLAG)
722 {
723 (None, false)
724 }
725 else {
729 (
730 self.create_auxiliary_browsing_context(cx, name, noopener),
731 true,
732 )
733 }
734 }
735
736 fn find_navigable_by_target_name(&self, name: &DOMString) -> Option<DomRoot<WindowProxy>> {
738 let top = self.top();
758 let subtrees_to_search = if top != self {
759 Either::Left([self, top])
760 } else {
761 Either::Right([self])
762 };
763
764 for subtree_to_search in subtrees_to_search.into_iter() {
766 if let Some(result) =
770 subtree_to_search.find_navigable_by_target_name_in_descendants(name)
771 {
772 return Some(result);
773 }
774 }
775
776 let mut top_level_window_proxies = self.script_window_proxies.top_level_window_proxies();
783
784 top_level_window_proxies
792 .sort_by_key(|proxy| std::cmp::Reverse(proxy.browsing_context_id()));
793
794 for window_proxy in top_level_window_proxies {
795 if &*window_proxy == top {
798 continue;
799 }
800 if let Some(result) = window_proxy.find_navigable_by_target_name_in_descendants(name) {
818 return Some(result);
819 }
820 }
821
822 None
824 }
825
826 fn find_navigable_by_target_name_in_descendants(
828 &self,
829 name: &DOMString,
830 ) -> Option<DomRoot<WindowProxy>> {
831 if self.is_browsing_context_discarded() {
833 return None;
834 }
835
836 if self.get_name() == *name {
842 return Some(DomRoot::from_ref(self));
843 }
844
845 let document = self.document()?;
846 let iframes: Vec<_> = document.iframes().iter().collect();
847 iframes.iter().find_map(|iframe| {
848 iframe
849 .browsing_context_id()
850 .and_then(|browsing_context_id| {
851 self.script_window_proxies
852 .find_window_proxy(browsing_context_id)?
853 .find_navigable_by_target_name_in_descendants(name)
854 })
855 })
856 }
857
858 pub(crate) fn is_auxiliary(&self) -> bool {
859 self.opener.is_some()
860 }
861
862 pub(crate) fn discard_browsing_context(&self) {
863 self.discarded.set(true);
864 }
865
866 pub(crate) fn is_browsing_context_discarded(&self) -> bool {
867 self.discarded.get()
868 }
869
870 pub(crate) fn browsing_context_id(&self) -> BrowsingContextId {
871 self.browsing_context_id
872 }
873
874 pub(crate) fn webview_id(&self) -> WebViewId {
875 self.webview_id
876 }
877
878 pub(crate) fn frame_element(&self) -> Option<&Element> {
882 self.frame_element.as_deref()
883 }
884
885 pub(crate) fn document(&self) -> Option<DomRoot<Document>> {
886 self.currently_active
887 .get()
888 .and_then(ScriptThread::find_document)
889 }
890
891 pub(crate) fn parent(&self) -> Option<&WindowProxy> {
892 self.parent.as_deref()
893 }
894
895 pub(crate) fn top(&self) -> &WindowProxy {
896 let mut result = self;
897 while let Some(parent) = result.parent() {
898 result = parent;
899 }
900 result
901 }
902
903 pub(crate) fn document_origin_and_internal_ancestor_origin_objects_list(
904 &self,
905 ) -> Option<(OriginSnapshot, Vec<ImmutableOrigin>)> {
906 let pipeline_id = self.currently_active()?;
907 let (result_sender, result_receiver) = generic_channel::channel().unwrap();
908 self.global()
909 .script_to_constellation_chan()
910 .send(ScriptToConstellationMessage::GetDocumentOriginDetails(
911 pipeline_id,
912 result_sender,
913 ))
914 .ok()?;
915 result_receiver.recv().ok()?
916 }
917
918 pub(crate) fn parent_origin_and_internal_ancestor_origin_objects_list(
920 &self,
921 ) -> Option<(OriginSnapshot, Vec<ImmutableOrigin>)> {
922 if let Some(frame_element) = self.frame_element() {
923 let parent_document = frame_element.owner_document();
924 if !parent_document.is_fully_active() {
927 return None;
928 }
929 Some((
930 parent_document.origin().snapshot(),
931 parent_document
932 .internal_ancestor_origin_objects_list()
933 .clone()
934 .expect("Must always be fully active"),
935 ))
936 } else if let Some(parent_proxy) = self.parent() {
937 assert!(parent_proxy.currently_active().is_some());
939 parent_proxy.document_origin_and_internal_ancestor_origin_objects_list()
940 } else {
941 None
942 }
943 }
944
945 #[expect(unsafe_code)]
946 fn set_window(&self, cx: &mut JSContext, window: &GlobalScope) {
950 unsafe {
951 debug!("Setting window of {:p}.", self);
952
953 let window_jsobject = window.reflector().get_jsobject();
954 let old_js_proxy = self.reflector.get_jsobject();
955 assert!(!window_jsobject.get().is_null());
956 assert_ne!(
957 ((*get_object_class(window_jsobject.get())).flags & JSCLASS_IS_GLOBAL),
958 0
959 );
960
961 let mut realm = AutoRealm::new_from_handle(cx, window_jsobject);
962 let cx = &mut realm;
963
964 SetProxyReservedSlot(old_js_proxy.get(), 0, &PrivateValue(ptr::null_mut()));
966
967 rooted!(&in(cx) let new_js_proxy = WindowProxyHandler::proxy_handler().new_window_proxy(cx, window_jsobject));
972 SetProxyReservedSlot(new_js_proxy.get(), 0, &PrivateValue(ptr::null_mut()));
975 debug!(
976 "Transplanting proxy from {:p} to {:p}.",
977 old_js_proxy.get(),
978 new_js_proxy.get()
979 );
980 rooted!(&in(cx) let new_js_proxy = JS_TransplantObject(cx, old_js_proxy, new_js_proxy.handle()));
981 debug!("Transplanted proxy is {:p}.", new_js_proxy.get());
982
983 SetProxyReservedSlot(
985 new_js_proxy.get(),
986 0,
987 &PrivateValue(self as *const _ as *const libc::c_void),
988 );
989
990 SetWindowProxy(cx, window_jsobject, new_js_proxy.handle());
992
993 debug!(
995 "Setting reflector of {:p} to {:p}.",
996 self,
997 new_js_proxy.get()
998 );
999 self.reflector.rootable().set(new_js_proxy.get());
1000 }
1001 }
1002
1003 pub(crate) fn set_pipeline_id(&self, pipeline_id: PipelineId) {
1004 self.currently_active.set(Some(pipeline_id));
1005 }
1006
1007 pub(crate) fn set_currently_active(&self, cx: &mut JSContext, window: &Window) {
1008 if let Some(pipeline_id) = self.currently_active() &&
1009 pipeline_id == window.pipeline_id()
1010 {
1011 return debug!(
1012 "Attempt to set the currently active window to the currently active window."
1013 );
1014 }
1015
1016 let global_scope = window.as_global_scope();
1017 self.set_window(cx, global_scope);
1018 self.currently_active.set(Some(global_scope.pipeline_id()));
1019 }
1020
1021 pub(crate) fn unset_currently_active(&self, cx: &mut JSContext) {
1022 if self.currently_active().is_none() {
1023 return debug!(
1024 "Attempt to unset the currently active window on a windowproxy that does not have one."
1025 );
1026 }
1027 let globalscope = self.global();
1028 let window = DissimilarOriginWindow::new(cx, &globalscope, self);
1029 self.set_window(cx, window.upcast());
1030 self.currently_active.set(None);
1031 }
1032
1033 pub(crate) fn currently_active(&self) -> Option<PipelineId> {
1034 self.currently_active.get()
1035 }
1036
1037 pub(crate) fn get_name(&self) -> DOMString {
1038 self.name.borrow().clone()
1039 }
1040
1041 pub(crate) fn set_name(&self, name: DOMString) {
1042 *self.name.borrow_mut() = name;
1043 }
1044}
1045
1046#[derive(Debug, Deserialize, Serialize)]
1058pub(crate) struct CreatorBrowsingContextInfo {
1059 url: Option<ServoUrl>,
1061
1062 origin: Option<ImmutableOrigin>,
1064}
1065
1066impl CreatorBrowsingContextInfo {
1067 pub(crate) fn from(
1068 parent: Option<&WindowProxy>,
1069 opener: Option<&WindowProxy>,
1070 ) -> CreatorBrowsingContextInfo {
1071 let creator = match (parent, opener) {
1072 (Some(parent), _) => parent.document(),
1073 (None, Some(opener)) => opener.document(),
1074 (None, None) => None,
1075 };
1076
1077 let url = creator.as_deref().map(|document| document.url());
1078 let origin = creator
1079 .as_deref()
1080 .map(|document| document.origin().immutable().clone());
1081
1082 CreatorBrowsingContextInfo { url, origin }
1083 }
1084}
1085
1086fn tokenize_open_features(features: DOMString) -> IndexMap<String, String> {
1088 let is_feature_sep = |c: char| c.is_ascii_whitespace() || ['=', ','].contains(&c);
1089 let mut tokenized_features = IndexMap::new();
1091 let features = features.str();
1093 let mut iter = features.chars();
1094 let mut cur = iter.next();
1095
1096 while cur.is_some() {
1098 let mut name = String::new();
1100 let mut value = String::new();
1101 while let Some(cur_char) = cur {
1103 if !is_feature_sep(cur_char) {
1104 break;
1105 }
1106 cur = iter.next();
1107 }
1108 while let Some(cur_char) = cur {
1110 if is_feature_sep(cur_char) {
1111 break;
1112 }
1113 name.push(cur_char.to_ascii_lowercase());
1114 cur = iter.next();
1115 }
1116 let normalized_name = String::from(match name.as_ref() {
1118 "screenx" => "left",
1119 "screeny" => "top",
1120 "innerwidth" => "width",
1121 "innerheight" => "height",
1122 _ => name.as_ref(),
1123 });
1124 while let Some(cur_char) = cur {
1126 if cur_char == '=' || cur_char == ',' || !is_feature_sep(cur_char) {
1127 break;
1128 }
1129 cur = iter.next();
1130 }
1131 if cur.is_some() && is_feature_sep(cur.unwrap()) {
1133 while let Some(cur_char) = cur {
1135 if !is_feature_sep(cur_char) || cur_char == ',' {
1136 break;
1137 }
1138 cur = iter.next();
1139 }
1140 while let Some(cur_char) = cur {
1142 if is_feature_sep(cur_char) {
1143 break;
1144 }
1145 value.push(cur_char.to_ascii_lowercase());
1146 cur = iter.next();
1147 }
1148 }
1149 if !name.is_empty() {
1151 tokenized_features.insert(normalized_name, value);
1152 }
1153 }
1154 tokenized_features
1156}
1157
1158fn parse_open_feature_boolean(tokenized_features: &IndexMap<String, String>, name: &str) -> bool {
1160 if let Some(value) = tokenized_features.get(name) {
1161 if value.is_empty() || value == "yes" {
1163 return true;
1164 }
1165 if let Ok(int) = parse_integer(value.chars()) {
1167 return int != 0;
1168 }
1169 }
1170 false
1172}
1173
1174#[expect(unsafe_code)]
1175fn window_proxy_target(proxy: HandleObject) -> *mut JSObject {
1176 let mut slot = UndefinedValue();
1177 unsafe { GetProxyPrivate(proxy.as_raw(), &mut slot) };
1178 slot.to_object()
1179}
1180
1181#[expect(unsafe_code)]
1183unsafe extern "C" fn get_own_property_descriptor(
1184 cx: *mut RawJSContext,
1185 proxy: RawHandleObject,
1186 id: RawHandleId,
1187 property_descriptor: RawMutableHandle<PropertyDescriptor>,
1188 is_none: *mut bool,
1189) -> bool {
1190 let mut cx = unsafe {
1191 JSContext::from_ptr(NonNull::new(cx).expect("JSContext should not be null in SM hook"))
1194 };
1195 let mut cx = CurrentRealm::assert(&mut cx);
1196 let cx = &mut cx;
1197 let proxy = unsafe { Handle::from_raw(proxy) };
1198 let id = unsafe { Handle::from_raw(id) };
1199 let mut property_descriptor = unsafe { MutableHandle::from_raw(property_descriptor) };
1200 let is_none = unsafe { &mut *is_none };
1201
1202 rooted!(&in(cx) let target = window_proxy_target(proxy));
1204 let window = WindowOrDissimilarOriginWindow::new(cx, target.handle());
1205
1206 if let Some(index) = get_array_index_from_id(id) {
1209 if let Some(window_proxy) = window.window_proxy_for_child_navigable_at_index(index) {
1213 rooted!(&in(cx) let mut window_proxy_jsval = UndefinedValue());
1220 window_proxy.to_jsval(cx, window_proxy_jsval.handle_mut());
1221
1222 set_property_descriptor(
1225 property_descriptor,
1226 window_proxy_jsval.handle(),
1227 (JSPROP_ENUMERATE | JSPROP_READONLY) as u32,
1228 is_none,
1229 );
1230 return true;
1231 } else {
1232 *is_none = true;
1234 if is_platform_object_same_origin(cx, proxy) {
1237 return true;
1238 }
1239 return report_cross_origin_denial::<DomTypeHolder>(cx, id, "get");
1241 }
1242 }
1243
1244 if is_platform_object_same_origin(cx, proxy) {
1246 return unsafe {
1247 JS_GetOwnPropertyDescriptorById(cx, target.handle(), id, property_descriptor, is_none)
1248 };
1249 }
1250
1251 if !cross_origin_get_own_property_helper(
1253 cx,
1254 proxy,
1255 window.cross_origin_properties(),
1256 id,
1257 property_descriptor.reborrow(),
1258 is_none,
1259 ) {
1260 return false;
1261 }
1262
1263 if !*is_none {
1265 return true;
1266 }
1267
1268 if let Some(named_child_navigable) = window.named_child_navigable(cx, id) {
1271 rooted!(&in(cx) let mut window_proxy_value = UndefinedValue());
1273 named_child_navigable.to_jsval(cx, window_proxy_value.handle_mut());
1274 set_property_descriptor(
1277 property_descriptor.reborrow(),
1278 window_proxy_value.handle(),
1279 JSPROP_READONLY as u32,
1280 is_none,
1281 );
1282 return true;
1283 }
1284
1285 cross_origin_property_fallback::<DomTypeHolder>(
1287 cx,
1288 proxy,
1289 id,
1290 property_descriptor.reborrow(),
1291 is_none,
1292 )
1293}
1294
1295#[expect(unsafe_code)]
1297unsafe extern "C" fn define_property(
1298 cx: *mut RawJSContext,
1299 proxy: RawHandleObject,
1300 id: RawHandleId,
1301 desc: RawHandle<PropertyDescriptor>,
1302 res: *mut ObjectOpResult,
1303) -> bool {
1304 let mut cx = unsafe {
1305 JSContext::from_ptr(NonNull::new(cx).expect("JSContext should not be null in SM hook"))
1308 };
1309 let mut cx = CurrentRealm::assert(&mut cx);
1310 let cx = &mut cx;
1311 let id = unsafe { Handle::from_raw(id) };
1312 let proxy = unsafe { Handle::from_raw(proxy) };
1313
1314 if is_platform_object_same_origin(cx, proxy) {
1319 if get_array_index_from_id(id).is_some() {
1321 unsafe {
1326 (*res).code_ = JSErrNum::JSMSG_CANT_DEFINE_WINDOW_ELEMENT as usize;
1327 }
1328 return true;
1329 }
1330
1331 rooted!(&in(cx) let target = window_proxy_target(proxy));
1333 return unsafe {
1334 JS_DefinePropertyById(cx.raw_cx(), target.handle().into(), id.into(), desc, res)
1335 };
1336 }
1337
1338 report_cross_origin_denial::<DomTypeHolder>(cx, id, "define")
1340}
1341
1342#[expect(unsafe_code)]
1343unsafe extern "C" fn has(
1344 cx: *mut RawJSContext,
1345 proxy: RawHandleObject,
1346 id: RawHandleId,
1347 bp: *mut bool,
1348) -> bool {
1349 unsafe { has_or_has_own(cx, proxy, id, bp, IncludePrototypes::Yes) }
1350}
1351
1352#[expect(unsafe_code)]
1353unsafe extern "C" fn has_own(
1354 cx: *mut RawJSContext,
1355 proxy: RawHandleObject,
1356 id: RawHandleId,
1357 bp: *mut bool,
1358) -> bool {
1359 unsafe { has_or_has_own(cx, proxy, id, bp, IncludePrototypes::No) }
1360}
1361
1362enum IncludePrototypes {
1363 Yes,
1364 No,
1365}
1366
1367#[expect(unsafe_code)]
1368unsafe fn has_or_has_own(
1369 cx: *mut RawJSContext,
1370 proxy: RawHandleObject,
1371 id: RawHandleId,
1372 bp: *mut bool,
1373 include_prototypes: IncludePrototypes,
1374) -> bool {
1375 let mut cx = unsafe { JSContext::from_ptr(ptr::NonNull::new(cx).unwrap()) };
1376 let mut cx = CurrentRealm::assert(&mut cx);
1377 let cx = &mut cx;
1378 let proxy = unsafe { Handle::from_raw(proxy) };
1379 let id = unsafe { Handle::from_raw(id) };
1380
1381 rooted!(&in(cx) let target = window_proxy_target(proxy));
1382 let window = WindowOrDissimilarOriginWindow::new(cx, target.handle());
1383
1384 let (success, found) = if is_platform_object_same_origin(cx, proxy) {
1385 if let Some(array_index) = get_array_index_from_id(id) &&
1386 window
1387 .window_proxy_for_child_navigable_at_index(array_index)
1388 .is_some()
1389 {
1390 unsafe { *bp = true };
1391 return true;
1392 }
1393
1394 let mut found = false;
1395 let success = match include_prototypes {
1396 IncludePrototypes::Yes => unsafe {
1397 JS_HasPropertyById(cx, target.handle(), id, &mut found)
1398 },
1399 IncludePrototypes::No => unsafe {
1400 JS_HasOwnPropertyById(cx, target.handle(), id, &mut found)
1401 },
1402 };
1403 (success, found)
1404 } else {
1405 rooted!(&in(cx) let mut property_descriptor = PropertyDescriptor::default());
1406 let mut is_none = false;
1407 let success = unsafe {
1408 get_own_property_descriptor(
1409 cx.raw_cx(),
1410 proxy.into(),
1411 id.into(),
1412 property_descriptor.handle_mut().into(),
1413 &mut is_none,
1414 )
1415 };
1416 (success, !is_none)
1417 };
1418
1419 if !success {
1420 return false;
1421 }
1422 unsafe { *bp = found };
1423 true
1424}
1425
1426#[expect(unsafe_code)]
1428unsafe extern "C" fn get(
1429 cx: *mut RawJSContext,
1430 proxy: RawHandleObject,
1431 receiver: RawHandleValue,
1432 id: RawHandleId,
1433 return_value: RawMutableHandleValue,
1434) -> bool {
1435 let mut cx = unsafe {
1436 JSContext::from_ptr(NonNull::new(cx).expect("JSContext should not be null in SM hook"))
1438 };
1439 let mut cx = CurrentRealm::assert(&mut cx);
1440 let cx = &mut cx;
1441 let proxy = unsafe { Handle::from_raw(proxy) };
1442 let receiver = unsafe { Handle::from_raw(receiver) };
1443 let id = unsafe { Handle::from_raw(id) };
1444 let return_value = unsafe { MutableHandle::from_raw(return_value) };
1445
1446 rooted!(&in(cx) let target = window_proxy_target(proxy));
1448 let window = WindowOrDissimilarOriginWindow::new(cx, target.handle());
1449
1450 if is_platform_object_same_origin(cx, proxy) {
1457 if let Some(index) = get_array_index_from_id(id) &&
1458 let Some(window_proxy) = window.window_proxy_for_child_navigable_at_index(index)
1459 {
1460 window_proxy.to_jsval(cx, return_value);
1461 return true;
1462 }
1463
1464 return unsafe { JS_ForwardGetPropertyTo(cx, target.handle(), id, receiver, return_value) };
1465 }
1466
1467 proxyhandler::cross_origin_get::<DomTypeHolder>(cx, proxy, receiver, id, return_value)
1469}
1470
1471#[expect(unsafe_code)]
1473unsafe extern "C" fn set(
1474 cx: *mut RawJSContext,
1475 proxy: RawHandleObject,
1476 id: RawHandleId,
1477 v: RawHandleValue,
1478 receiver: RawHandleValue,
1479 res: *mut ObjectOpResult,
1480) -> bool {
1481 let mut cx = unsafe {
1482 JSContext::from_ptr(NonNull::new(cx).expect("JSContext should not be null in SM hook"))
1484 };
1485 let mut cx = CurrentRealm::assert(&mut cx);
1486 let cx = &mut cx;
1487
1488 let proxy = unsafe { Handle::from_raw(proxy) };
1497 let id = unsafe { Handle::from_raw(id) };
1498
1499 if is_platform_object_same_origin(cx, proxy) {
1501 if get_array_index_from_id(id).is_some() {
1503 unsafe { (*res).code_ = JSErrNum::JSMSG_READ_ONLY as usize };
1505 return true;
1506 }
1507
1508 rooted!(&in(cx) let target = window_proxy_target(proxy));
1510 return unsafe {
1511 JS_ForwardSetPropertyTo(
1512 cx.raw_cx(),
1513 target.handle().into(),
1514 id.into(),
1515 v,
1516 receiver,
1517 res,
1518 )
1519 };
1520 }
1521
1522 let receiver = unsafe { HandleValue::from_raw(receiver) };
1524 unsafe { cross_origin_set::<DomTypeHolder>(cx, proxy, id, v, receiver, res) }
1525}
1526
1527#[expect(unsafe_code)]
1528unsafe extern "C" fn get_prototype_if_ordinary(
1529 _: *mut RawJSContext,
1530 _: RawHandleObject,
1531 is_ordinary: *mut bool,
1532 _: RawMutableHandleObject,
1533) -> bool {
1534 unsafe { *is_ordinary = false };
1547 true
1548}
1549
1550#[expect(unsafe_code)]
1552unsafe extern "C" fn get_prototype(
1553 cx: *mut RawJSContext,
1554 proxy: RawHandleObject,
1555 result: RawMutableHandleObject,
1556) -> bool {
1557 let mut cx = unsafe { JSContext::from_ptr(ptr::NonNull::new(cx).unwrap()) };
1558 let mut realm = CurrentRealm::assert(&mut cx);
1559 let proxy = unsafe { Handle::from_raw(proxy) };
1560 let result = unsafe { MutableHandleObject::from_raw(result) };
1561 maybe_cross_origin_get_prototype::<DomTypeHolder>(
1562 &mut realm,
1563 proxy,
1564 GetProtoObject::<DomTypeHolder>,
1565 result,
1566 )
1567}
1568
1569#[expect(unsafe_code)]
1571unsafe extern "C" fn delete(
1572 cx: *mut RawJSContext,
1573 proxy: RawHandleObject,
1574 id: RawHandleId,
1575 result: *mut ObjectOpResult,
1576) -> bool {
1577 let mut cx = unsafe { JSContext::from_ptr(ptr::NonNull::new(cx).unwrap()) };
1578 let mut cx = CurrentRealm::assert(&mut cx);
1579 let cx = &mut cx;
1580 let proxy = unsafe { Handle::from_raw(proxy) };
1581 let id = unsafe { Handle::from_raw(id) };
1582
1583 rooted!(&in(cx) let target = window_proxy_target(proxy));
1585 let window = WindowOrDissimilarOriginWindow::new(cx, target.handle());
1586
1587 if is_platform_object_same_origin(cx, proxy) {
1589 if let Some(array_index) = get_array_index_from_id(id) {
1591 let code = if window
1593 .window_proxy_for_child_navigable_at_index(array_index)
1594 .is_none()
1595 {
1596 0 } else {
1599 JSErrNum::JSMSG_CANT_DELETE_WINDOW_ELEMENT as usize
1601 };
1602 unsafe { (*result).code_ = code };
1603 return true;
1604 }
1605 rooted!(&in(cx) let target = window_proxy_target(proxy));
1607 return unsafe {
1608 JS_DeletePropertyById(cx.raw_cx(), target.handle().into(), id.into(), result)
1609 };
1610 }
1611
1612 report_cross_origin_denial::<DomTypeHolder>(cx, id, "delete")
1614}
1615
1616#[expect(unsafe_code)]
1618unsafe extern "C" fn own_property_keys(
1619 cx: *mut RawJSContext,
1620 proxy: RawHandleObject,
1621 property_keys: RawMutableHandleIdVector,
1622) -> bool {
1623 let mut cx = unsafe { JSContext::from_ptr(ptr::NonNull::new(cx).unwrap()) };
1624 let mut cx = CurrentRealm::assert(&mut cx);
1625 let cx = &mut cx;
1626 let proxy = unsafe { Handle::from_raw(proxy) };
1627
1628 rooted!(&in(cx) let target = window_proxy_target(proxy));
1630 let window = WindowOrDissimilarOriginWindow::new(cx, target.handle());
1631
1632 let max_properties = window.iframe_count();
1638
1639 rooted!(&in(cx) let mut rooted_index_jsid: jsid);
1641 for index in 0..max_properties {
1642 unsafe { int_to_jsid(index as i32, rooted_index_jsid.handle_mut()) };
1643 unsafe { AppendToIdVector(property_keys, rooted_index_jsid.handle()) };
1644 }
1645
1646 if is_platform_object_same_origin(cx, proxy) {
1647 return unsafe {
1650 GetPropertyKeys(
1651 cx,
1652 target.handle(),
1653 JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS,
1654 property_keys,
1655 )
1656 };
1657 }
1658
1659 cross_origin_own_property_keys(cx, proxy, window.cross_origin_properties(), property_keys)
1661}
1662
1663static PROXY_TRAPS: ProxyTraps = ProxyTraps {
1664 enter: None,
1665 getOwnPropertyDescriptor: Some(get_own_property_descriptor),
1666 defineProperty: Some(define_property),
1667 ownPropertyKeys: Some(own_property_keys),
1668 delete_: Some(delete),
1669 enumerate: None,
1670 getPrototypeIfOrdinary: Some(get_prototype_if_ordinary),
1671 getPrototype: Some(get_prototype),
1672 setPrototype: Some(maybe_cross_origin_set_prototype_rawcx),
1673 setImmutablePrototype: None,
1674 preventExtensions: Some(prevent_extensions),
1675 isExtensible: Some(is_extensible),
1676 has: Some(has),
1677 get: Some(get),
1678 set: Some(set),
1679 call: None,
1680 construct: None,
1681 hasOwn: Some(has_own),
1682 getOwnEnumerablePropertyKeys: None,
1683 nativeCall: None,
1684 objectClassIs: None,
1685 className: None,
1686 fun_toString: None,
1687 boxedValue_unbox: None,
1688 defaultValue: None,
1689 trace: Some(trace),
1690 finalize: Some(finalize),
1691 objectMoved: None,
1692 isCallable: None,
1693 isConstructor: None,
1694};
1695
1696pub(crate) struct WindowProxyHandler(*const libc::c_void);
1699
1700impl MallocSizeOf for WindowProxyHandler {
1701 fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
1702 0
1704 }
1705}
1706
1707#[expect(unsafe_code)]
1709unsafe impl Send for WindowProxyHandler {}
1710#[expect(unsafe_code)]
1712unsafe impl Sync for WindowProxyHandler {}
1713
1714#[expect(unsafe_code)]
1715impl WindowProxyHandler {
1716 fn new(traps: &ProxyTraps) -> Self {
1717 let ptr = unsafe { CreateWrapperProxyHandler(traps) };
1719 assert!(!ptr.is_null());
1720 Self(ptr)
1721 }
1722
1723 pub(crate) fn proxy_handler() -> &'static Self {
1725 use std::sync::OnceLock;
1726 static SINGLETON: OnceLock<WindowProxyHandler> = OnceLock::new();
1732 SINGLETON.get_or_init(|| Self::new(&PROXY_TRAPS))
1733 }
1734
1735 fn new_window_proxy(
1738 &self,
1739 cx: &mut JSContext,
1740 window_jsobject: js::gc::HandleObject,
1741 ) -> *mut JSObject {
1742 let obj = unsafe { NewWindowProxy(cx, window_jsobject, self.0) };
1743 assert!(!obj.is_null());
1744 obj
1745 }
1746}
1747
1748#[expect(unsafe_code)]
1749impl Drop for WindowProxyHandler {
1750 fn drop(&mut self) {
1751 unsafe {
1754 DeleteWrapperProxyHandler(self.0);
1755 }
1756 }
1757}
1758
1759#[expect(unsafe_code)]
1762unsafe extern "C" fn finalize(_fop: *mut GCContext, obj: *mut JSObject) {
1763 let mut slot = UndefinedValue();
1764 unsafe { GetProxyReservedSlot(obj, 0, &mut slot) };
1765 let this = slot.to_private() as *mut WindowProxy;
1766 if this.is_null() {
1767 return;
1769 }
1770 unsafe {
1771 (*this).reflector.drop_memory(&*this);
1772 let jsobject = (*this).reflector.get_jsobject().get();
1773 debug!(
1774 "WindowProxy finalize: {:p}, with reflector {:p} from {:p}.",
1775 this, jsobject, obj
1776 );
1777 let _ = Box::from_raw(this);
1778 }
1779}
1780
1781#[expect(unsafe_code)]
1782unsafe extern "C" fn trace(trc: *mut JSTracer, obj: *mut JSObject) {
1783 let mut slot = UndefinedValue();
1784 unsafe { GetProxyReservedSlot(obj, 0, &mut slot) };
1785 let this = slot.to_private() as *const WindowProxy;
1786 if this.is_null() {
1787 return;
1789 }
1790 unsafe { (*this).trace(trc) };
1791}
1792
1793enum WindowOrDissimilarOriginWindow {
1796 Window(DomRoot<Window>),
1797 DissimilarOriginWindow(DomRoot<DissimilarOriginWindow>),
1798}
1799
1800impl WindowOrDissimilarOriginWindow {
1801 fn new(cx: &mut JSContext, handle_object: HandleObject) -> Self {
1802 if let Ok(window) = root_from_handleobject::<Window>(cx, handle_object) {
1803 Self::Window(window)
1804 } else if let Ok(window) =
1805 root_from_handleobject::<DissimilarOriginWindow>(cx, handle_object)
1806 {
1807 Self::DissimilarOriginWindow(window)
1808 } else {
1809 unreachable!("WindowProxy should always be backed by some kind of window");
1810 }
1811 }
1812
1813 fn global_scope(&self) -> DomRoot<GlobalScope> {
1814 match self {
1815 WindowOrDissimilarOriginWindow::Window(window) => window.global(),
1816 WindowOrDissimilarOriginWindow::DissimilarOriginWindow(window) => window.global(),
1817 }
1818 }
1819
1820 fn window_proxy(&self) -> DomRoot<WindowProxy> {
1821 match self {
1822 WindowOrDissimilarOriginWindow::Window(window) => window.window_proxy(),
1823 WindowOrDissimilarOriginWindow::DissimilarOriginWindow(window) => window.window_proxy(),
1824 }
1825 }
1826
1827 #[expect(unsafe_code)]
1828 fn cross_origin_properties(&self) -> &'static CrossOriginProperties {
1829 match self {
1830 WindowOrDissimilarOriginWindow::Window(..) => unsafe {
1831 WindowBinding::CROSS_ORIGIN_PROPERTIES.get()
1832 },
1833 WindowOrDissimilarOriginWindow::DissimilarOriginWindow(..) => unsafe {
1834 DissimilarOriginWindowBinding::CROSS_ORIGIN_PROPERTIES.get()
1835 },
1836 }
1837 }
1838
1839 fn iframe_count(&self) -> u32 {
1840 match self {
1841 WindowOrDissimilarOriginWindow::Window(window) => window.Length(),
1842 WindowOrDissimilarOriginWindow::DissimilarOriginWindow(window) => window.Length(),
1843 }
1844 }
1845
1846 fn window_proxy_for_child_navigable_at_index(
1847 &self,
1848 index: u32,
1849 ) -> Option<DomRoot<WindowProxy>> {
1850 let browsing_context_id = self.window_proxy().browsing_context_id();
1851 let (result_sender, result_receiver) = generic_channel::channel().unwrap();
1852 let _ = self.global_scope().script_to_constellation_chan().send(
1853 ScriptToConstellationMessage::GetChildBrowsingContextId(
1854 browsing_context_id,
1855 index as usize,
1856 result_sender,
1857 ),
1858 );
1859 result_receiver
1860 .recv()
1861 .ok()
1862 .flatten()
1863 .and_then(|id| ScriptThread::window_proxies().find_window_proxy(id))
1864 }
1865
1866 fn named_child_navigable(
1887 &self,
1888 cx: &mut CurrentRealm,
1889 id: HandleId,
1890 ) -> Option<DomRoot<WindowProxy>> {
1891 let Self::Window(window) = &self else {
1893 return None;
1894 };
1895
1896 let name = jsid_to_string(cx, id)?;
1897 if name.is_empty() {
1898 return None;
1899 }
1900 window
1901 .Document()
1902 .iframes()
1903 .iter()
1904 .filter_map(|iframe| iframe.GetContentWindow())
1905 .find(|window_proxy| window_proxy.get_name() == name)
1906 }
1907}