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::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_ForwardSetPropertyTo,
24 JSContext as RawJSContext, JSErrNum, JSObject, JSPROP_ENUMERATE, JSPROP_READONLY, JSTracer,
25 MutableHandle as RawMutableHandle, MutableHandleObject as RawMutableHandleObject,
26 MutableHandleValue as RawMutableHandleValue, ObjectOpResult, PropertyDescriptor,
27};
28use js::jsval::{NullValue, PrivateValue, UndefinedValue};
29use js::realm::{AutoRealm, CurrentRealm};
30use js::rust::wrappers2::{
31 JS_ForwardGetPropertyTo, JS_GetOwnPropertyDescriptorById, JS_HasOwnPropertyById,
32 JS_HasPropertyById, JS_IsExceptionPending, JS_TransplantObject, NewWindowProxy, SetWindowProxy,
33};
34use js::rust::{Handle, MutableHandle, MutableHandleValue, get_object_class};
35use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
36use net_traits::ReferrerPolicy;
37use net_traits::request::Referrer;
38use script_bindings::cell::DomRefCell;
39use script_bindings::codegen::GenericBindings::WindowBinding::{GetProtoObject, WindowMethods};
40use script_bindings::proxyhandler::{
41 is_extensible, maybe_cross_origin_get_prototype,
42 maybe_cross_origin_get_prototype_if_ordinary_rawcx, maybe_cross_origin_set_prototype_rawcx,
43 prevent_extensions, set_property_descriptor,
44};
45use script_bindings::reflector::{DomObject, MutDomObject, Reflector};
46use script_traits::NewPipelineInfo;
47use serde::{Deserialize, Serialize};
48use servo_base::generic_channel;
49use servo_base::generic_channel::GenericSend;
50use servo_base::id::{BrowsingContextId, PipelineId, WebViewId};
51use servo_constellation_traits::{
52 AuxiliaryWebViewCreationRequest, LoadData, LoadOrigin, NavigationHistoryBehavior,
53 ScriptToConstellationMessage, TargetSnapshotParams,
54};
55use servo_url::{ImmutableOrigin, OriginSnapshot, ServoUrl};
56use storage_traits::webstorage_thread::WebStorageThreadMsg;
57use style::attr::parse_integer;
58
59use crate::DomTypeHolder;
60use crate::dom::bindings::conversions::{ToJSValConvertible, root_from_handleobject};
61use crate::dom::bindings::error::{Error, Fallible, throw_dom_exception};
62use crate::dom::bindings::inheritance::Castable;
63use crate::dom::bindings::reflector::DomGlobal;
64use crate::dom::bindings::root::{Dom, DomRoot};
65use crate::dom::bindings::settings_stack::maybe_entry_global;
66use crate::dom::bindings::str::{DOMString, USVString};
67use crate::dom::bindings::trace::JSTraceable;
68use crate::dom::bindings::utils::get_array_index_from_id;
69use crate::dom::dissimilaroriginwindow::DissimilarOriginWindow;
70use crate::dom::document::Document;
71use crate::dom::element::Element;
72use crate::dom::globalscope::GlobalScope;
73use crate::dom::node::node::NodeTraits;
74use crate::dom::window::Window;
75use crate::event_loop::script_thread::{ScriptThread, with_script_thread};
76use crate::event_loop::script_window_proxies::ScriptWindowProxies;
77use crate::navigation::navigate;
78
79#[dom_struct]
80pub(crate) struct WindowProxy {
85 reflector: Reflector,
90
91 #[no_trace]
95 browsing_context_id: BrowsingContextId,
96
97 #[no_trace]
99 opener: Option<BrowsingContextId>,
100
101 #[no_trace]
104 webview_id: WebViewId,
105
106 name: DomRefCell<DOMString>,
109 #[no_trace]
115 currently_active: Cell<Option<PipelineId>>,
116
117 discarded: Cell<bool>,
119
120 disowned: Cell<bool>,
122
123 is_closing: Cell<bool>,
125
126 frame_element: Option<Dom<Element>>,
130
131 parent: Option<Dom<WindowProxy>>,
133
134 delaying_load_events_mode: Cell<bool>,
136
137 #[no_trace]
139 creator_url: Option<ServoUrl>,
140
141 #[no_trace]
143 creator_origin: Option<ImmutableOrigin>,
144
145 #[conditional_malloc_size_of]
147 script_window_proxies: Rc<ScriptWindowProxies>,
148}
149
150impl WindowProxy {
151 fn new_inherited(
152 browsing_context_id: BrowsingContextId,
153 webview_id: WebViewId,
154 currently_active: Option<PipelineId>,
155 frame_element: Option<&Element>,
156 parent: Option<&WindowProxy>,
157 opener: Option<BrowsingContextId>,
158 creator: CreatorBrowsingContextInfo,
159 ) -> WindowProxy {
160 let name = frame_element.map_or(DOMString::new(), |e| {
161 e.get_string_attribute(&local_name!("name"))
162 });
163 WindowProxy {
164 reflector: Reflector::new(),
165 browsing_context_id,
166 webview_id,
167 name: DomRefCell::new(name),
168 currently_active: Cell::new(currently_active),
169 discarded: Cell::new(false),
170 disowned: Cell::new(false),
171 is_closing: Cell::new(false),
172 frame_element: frame_element.map(Dom::from_ref),
173 parent: parent.map(Dom::from_ref),
174 delaying_load_events_mode: Cell::new(false),
175 opener,
176 creator_url: creator.url,
177 creator_origin: creator.origin,
178 script_window_proxies: ScriptThread::window_proxies(),
179 }
180 }
181
182 #[expect(unsafe_code)]
183 #[expect(clippy::too_many_arguments)]
184 pub(crate) fn new(
185 cx: &mut JSContext,
186 window: &Window,
187 browsing_context_id: BrowsingContextId,
188 webview_id: WebViewId,
189 frame_element: Option<&Element>,
190 parent: Option<&WindowProxy>,
191 opener: Option<BrowsingContextId>,
192 creator: CreatorBrowsingContextInfo,
193 ) -> DomRoot<WindowProxy> {
194 unsafe {
195 let handler = window.windowproxy_handler();
196
197 let window_jsobject = window.reflector().get_jsobject();
198 assert!(!window_jsobject.get().is_null());
199 assert_ne!(
200 ((*get_object_class(window_jsobject.get())).flags & JSCLASS_IS_GLOBAL),
201 0
202 );
203
204 let mut realm = AutoRealm::new_from_handle(cx, window_jsobject);
205 let cx = &mut realm;
206
207 rooted!(&in(cx) let js_proxy = handler.new_window_proxy(cx, window_jsobject));
209 assert!(!js_proxy.is_null());
210
211 let current = Some(window.upcast::<GlobalScope>().pipeline_id());
214 let window_proxy = Box::new(WindowProxy::new_inherited(
215 browsing_context_id,
216 webview_id,
217 current,
218 frame_element,
219 parent,
220 opener,
221 creator,
222 ));
223
224 SetProxyReservedSlot(
227 js_proxy.get(),
228 0,
229 &PrivateValue(&raw const (*window_proxy) as *const libc::c_void),
230 );
231
232 SetWindowProxy(cx, window_jsobject, js_proxy.handle());
234
235 debug!(
237 "Initializing reflector of {:p} to {:p}.",
238 window_proxy,
239 js_proxy.get()
240 );
241 window_proxy
242 .reflector
243 .init_reflector::<WindowProxy>(js_proxy.get());
244 DomRoot::from_ref(&*Box::into_raw(window_proxy))
245 }
246 }
247
248 #[expect(unsafe_code)]
249 pub(crate) fn new_dissimilar_origin(
250 cx: &mut JSContext,
251 global_to_clone_from: &GlobalScope,
252 browsing_context_id: BrowsingContextId,
253 webview_id: WebViewId,
254 parent: Option<&WindowProxy>,
255 opener: Option<BrowsingContextId>,
256 creator: CreatorBrowsingContextInfo,
257 ) -> DomRoot<WindowProxy> {
258 unsafe {
259 let handler = WindowProxyHandler::x_origin_proxy_handler();
260
261 let window_proxy = Box::new(WindowProxy::new_inherited(
263 browsing_context_id,
264 webview_id,
265 None,
266 None,
267 parent,
268 opener,
269 creator,
270 ));
271
272 let window = DissimilarOriginWindow::new(cx, global_to_clone_from, &window_proxy);
274 let window_jsobject = window.reflector().get_jsobject();
275 assert!(!window_jsobject.get().is_null());
276 assert_ne!(
277 ((*get_object_class(window_jsobject.get())).flags & JSCLASS_IS_GLOBAL),
278 0
279 );
280
281 let mut realm = AutoRealm::new_from_handle(cx, window_jsobject);
282 let cx = &mut realm;
283
284 rooted!(&in(cx) let js_proxy = handler.new_window_proxy(cx, window_jsobject));
286 assert!(!js_proxy.is_null());
287
288 SetProxyReservedSlot(
291 js_proxy.get(),
292 0,
293 &PrivateValue(&raw const (*window_proxy) as *const libc::c_void),
294 );
295
296 SetWindowProxy(cx, window_jsobject, js_proxy.handle());
298
299 debug!(
301 "Initializing reflector of {:p} to {:p}.",
302 window_proxy,
303 js_proxy.get()
304 );
305 window_proxy
306 .reflector
307 .init_reflector::<WindowProxy>(js_proxy.get());
308 DomRoot::from_ref(&*Box::into_raw(window_proxy))
309 }
310 }
311
312 fn create_auxiliary_browsing_context(
314 &self,
315 cx: &mut JSContext,
316 name: DOMString,
317 noopener: bool,
318 ) -> Option<DomRoot<WindowProxy>> {
319 let (response_sender, response_receiver) = generic_channel::channel().unwrap();
320 let window = self
321 .currently_active
322 .get()
323 .and_then(ScriptThread::find_document)
324 .map(|doc| DomRoot::from_ref(doc.window()))
325 .unwrap();
326
327 let document = self
328 .currently_active
329 .get()
330 .and_then(ScriptThread::find_document)
331 .expect("A WindowProxy creating an auxiliary to have an active document");
332
333 let sandboxing_flag_set = document.active_sandboxing_flag_set();
341 let propagate_sandbox = sandboxing_flag_set
342 .contains(SandboxingFlagSet::SANDBOX_PROPOGATES_TO_AUXILIARY_BROWSING_CONTEXTS_FLAG);
343 let sandboxing_flag_set = if propagate_sandbox {
344 sandboxing_flag_set
345 } else {
346 SandboxingFlagSet::empty()
347 };
348
349 let blank_url = ServoUrl::parse("about:blank").ok().unwrap();
350 let mut load_data = LoadData::new(
351 LoadOrigin::Script(document.origin().snapshot()),
352 blank_url,
353 Some(document.base_url()),
354 Some(window.pipeline_id()),
357 document.global().get_referrer(),
358 document.get_referrer_policy(),
359 None, None,
361 false,
362 sandboxing_flag_set,
363 );
364 load_data.is_initial_about_blank = true;
365 let load_info = AuxiliaryWebViewCreationRequest {
366 load_data: load_data.clone(),
367 opener_webview_id: window.webview_id(),
368 opener_pipeline_id: self.currently_active.get().unwrap(),
369 response_sender,
370 };
371 let constellation_msg = ScriptToConstellationMessage::CreateAuxiliaryWebView(load_info);
372 window.send_to_constellation(constellation_msg);
373
374 let response = response_receiver.recv().unwrap()?;
375 let new_browsing_context_id = BrowsingContextId::from(response.new_webview_id);
376 let new_pipeline_info = NewPipelineInfo {
377 parent_info: None,
378 new_pipeline_id: response.new_pipeline_id,
379 browsing_context_id: new_browsing_context_id,
380 webview_id: response.new_webview_id,
381 opener: Some(self.browsing_context_id),
382 load_data,
383 viewport_details: window.viewport_details(),
384 user_content_manager_id: response.user_content_manager_id,
385 embedder_theme: window.embedder_theme(),
388 target_snapshot_params: TargetSnapshotParams {
389 sandboxing_flags: sandboxing_flag_set,
390 iframe_element_referrer_policy: ReferrerPolicy::EmptyString,
391 },
392 frame_name: None,
393 };
394
395 with_script_thread(|script_thread| {
396 script_thread.spawn_pipeline(cx, new_pipeline_info);
397 });
398
399 let new_window_proxy = ScriptThread::find_document(response.new_pipeline_id)
400 .and_then(|doc| doc.browsing_context())?;
401 if !name.eq_ignore_ascii_case("_blank") {
402 new_window_proxy.set_name(name);
403 }
404 if noopener {
405 new_window_proxy.disown();
406 } else {
407 let (sender, receiver) = generic_channel::channel().unwrap();
412
413 let msg = WebStorageThreadMsg::Clone {
414 sender,
415 src: window.window_proxy().webview_id(),
416 dest: response.new_webview_id,
417 };
418
419 GenericSend::send(document.global().storage_threads(), msg).unwrap();
420 receiver.recv().unwrap();
421 }
422 Some(new_window_proxy)
423 }
424
425 pub(crate) fn start_delaying_load_events_mode(&self) {
427 self.delaying_load_events_mode.set(true);
428 }
429
430 pub(crate) fn stop_delaying_load_events_mode(&self) {
432 self.delaying_load_events_mode.set(false);
433 }
434
435 pub(crate) fn disown(&self) {
437 self.disowned.set(true);
438 }
439
440 pub(crate) fn close(&self) {
443 self.is_closing.set(true);
444 }
445
446 pub(crate) fn is_closing(&self) -> bool {
448 self.is_closing.get()
449 }
450
451 pub(crate) fn opener(&self, cx: &mut CurrentRealm, mut retval: MutableHandleValue) {
453 if self.disowned.get() {
454 return retval.set(NullValue());
455 }
456 let opener_id = match self.opener {
457 Some(opener_browsing_context_id) => opener_browsing_context_id,
458 None => return retval.set(NullValue()),
459 };
460 let parent_browsing_context = self.parent.as_deref();
461 let opener_proxy = match self.script_window_proxies.find_window_proxy(opener_id) {
462 Some(window_proxy) => window_proxy,
463 None => {
464 let sender_pipeline_id = self.currently_active().unwrap();
465 match ScriptThread::get_top_level_for_browsing_context(
466 self.webview_id(),
467 sender_pipeline_id,
468 opener_id,
469 ) {
470 Some(opener_top_id) => {
471 let global_to_clone_from = GlobalScope::from_current_realm(cx);
472 let creator =
473 CreatorBrowsingContextInfo::from(parent_browsing_context, None);
474 WindowProxy::new_dissimilar_origin(
475 cx,
476 &global_to_clone_from,
477 opener_id,
478 opener_top_id,
479 None,
480 None,
481 creator,
482 )
483 },
484 None => return retval.set(NullValue()),
485 }
486 },
487 };
488 if opener_proxy.is_browsing_context_discarded() {
489 return retval.set(NullValue());
490 }
491 opener_proxy.safe_to_jsval(cx, retval);
492 }
493
494 pub(crate) fn open(
496 &self,
497 cx: &mut JSContext,
498 url: USVString,
499 target: DOMString,
500 features: DOMString,
501 ) -> Fallible<Option<DomRoot<WindowProxy>>> {
502 if self.discarded.get() {
508 return Ok(None);
509 }
510 let source_document = maybe_entry_global()
516 .map(|global| global.as_window().Document())
517 .or_else(|| self.document())
518 .expect("Must have an entry global or active document");
519 let url_record = if !url.is_empty() {
521 let Ok(url) = source_document.encoding_parse_a_url(&url) else {
523 return Err(Error::Syntax(Some(format!(
525 "Error parsing URL '{url}' relative to '{}'",
526 source_document.url()
527 ))));
528 };
529 Some(url)
530 } else {
531 None
533 };
534 let non_empty_target = if target.is_empty() {
536 DOMString::from_static("_blank")
537 } else {
538 target
539 };
540 let tokenized_features = tokenize_open_features(features);
542 let noreferrer = parse_open_feature_boolean(&tokenized_features, "noreferrer");
546
547 let noopener = if noreferrer {
550 true
551 } else {
552 parse_open_feature_boolean(&tokenized_features, "noopener")
553 };
554 let (chosen, new) = match self.choose_a_navigable(cx, non_empty_target, noopener) {
564 (Some(chosen), new) => (chosen, new),
565 (None, _) => return Ok(None),
566 };
567 let target_document = match chosen.document() {
570 Some(target_document) => target_document,
571 None => return Ok(None),
572 };
573 let has_trustworthy_ancestor_origin = if new {
574 target_document.has_trustworthy_ancestor_or_current_origin()
575 } else {
576 false
577 };
578 let target_window = target_document.window();
579 let url_record = url_record.unwrap_or(ServoUrl::parse("about:blank").unwrap());
581 if !url_record.matches_about_blank() {
585 let referrer = if noreferrer {
586 Referrer::NoReferrer
587 } else {
588 target_window.as_global_scope().get_referrer()
589 };
590 let csp_list = source_document.get_csp_list().clone();
592 target_document.set_csp_list(csp_list);
593
594 let mut load_data = LoadData::new(
598 LoadOrigin::Script(source_document.origin().snapshot()),
599 url_record,
600 target_document.about_base_url(),
601 Some(target_window.pipeline_id()),
602 referrer,
603 target_document.get_referrer_policy(),
604 Some(target_window.as_global_scope().is_secure_context()),
605 Some(target_document.insecure_requests_policy()),
606 has_trustworthy_ancestor_origin,
607 target_document.creation_sandboxing_flag_set_considering_parent_iframe(),
608 );
609
610 if load_data.url.scheme() == "javascript" {
613 let existing_global = source_document.global();
614
615 if !ScriptThread::can_navigate_to_javascript_url(
617 cx,
618 &existing_global,
619 target_window.as_global_scope(),
620 &mut load_data,
621 None,
622 ) {
623 return Ok(target_document.browsing_context());
625 }
626 }
627
628 let history_handling = if new {
629 NavigationHistoryBehavior::Replace
630 } else {
631 NavigationHistoryBehavior::Push
632 };
633 navigate(cx, target_window, history_handling, false, load_data);
634 }
635 if noopener {
637 return Ok(None);
638 }
639 Ok(target_document.browsing_context())
641 }
642
643 pub(crate) fn choose_a_navigable(
645 &self,
646 cx: &mut JSContext,
647 name: DOMString,
648 noopener: bool,
649 ) -> (Option<DomRoot<WindowProxy>>, bool) {
650 let chosen = if name.is_empty() || name.eq_ignore_ascii_case("_self") {
657 Some((Some(DomRoot::from_ref(self)), false))
660 } else if name.eq_ignore_ascii_case("_parent") {
661 Some(
665 self.parent()
666 .map(|parent| (Some(DomRoot::from_ref(parent)), false))
667 .unwrap_or_else(|| (Some(DomRoot::from_ref(self)), false)),
668 )
669 } else if name.eq_ignore_ascii_case("_top") {
670 Some((Some(DomRoot::from_ref(self.top())), false))
673 } else if !name.eq_ignore_ascii_case("_blank") {
674 self.find_navigable_by_target_name(&name)
682 .map(|proxy| (Some(proxy), false))
683 } else {
684 None
685 };
686
687 if let Some(chosen) = chosen {
688 return chosen;
689 }
690
691 (
696 self.create_auxiliary_browsing_context(cx, name, noopener),
697 true,
698 )
699 }
700
701 fn find_navigable_by_target_name(&self, name: &DOMString) -> Option<DomRoot<WindowProxy>> {
703 let top = self.top();
723 let subtrees_to_search = if top != self {
724 Either::Left([self, top])
725 } else {
726 Either::Right([self])
727 };
728
729 for subtree_to_search in subtrees_to_search.into_iter() {
731 if let Some(result) =
735 subtree_to_search.find_navigable_by_target_name_in_descendants(name)
736 {
737 return Some(result);
738 }
739 }
740
741 let mut top_level_window_proxies = self.script_window_proxies.top_level_window_proxies();
748
749 top_level_window_proxies
757 .sort_by_key(|proxy| std::cmp::Reverse(proxy.browsing_context_id()));
758
759 for window_proxy in top_level_window_proxies {
760 if &*window_proxy == top {
763 continue;
764 }
765 if let Some(result) = window_proxy.find_navigable_by_target_name_in_descendants(name) {
783 return Some(result);
784 }
785 }
786
787 None
789 }
790
791 fn find_navigable_by_target_name_in_descendants(
793 &self,
794 name: &DOMString,
795 ) -> Option<DomRoot<WindowProxy>> {
796 if self.is_browsing_context_discarded() {
798 return None;
799 }
800
801 if self.get_name() == *name {
807 return Some(DomRoot::from_ref(self));
808 }
809
810 let document = self.document()?;
811 let iframes: Vec<_> = document.iframes().iter().collect();
812 iframes.iter().find_map(|iframe| {
813 iframe
814 .browsing_context_id()
815 .and_then(|browsing_context_id| {
816 self.script_window_proxies
817 .find_window_proxy(browsing_context_id)?
818 .find_navigable_by_target_name_in_descendants(name)
819 })
820 })
821 }
822
823 pub(crate) fn is_auxiliary(&self) -> bool {
824 self.opener.is_some()
825 }
826
827 pub(crate) fn discard_browsing_context(&self) {
828 self.discarded.set(true);
829 }
830
831 pub(crate) fn is_browsing_context_discarded(&self) -> bool {
832 self.discarded.get()
833 }
834
835 pub(crate) fn browsing_context_id(&self) -> BrowsingContextId {
836 self.browsing_context_id
837 }
838
839 pub(crate) fn webview_id(&self) -> WebViewId {
840 self.webview_id
841 }
842
843 pub(crate) fn frame_element(&self) -> Option<&Element> {
847 self.frame_element.as_deref()
848 }
849
850 pub(crate) fn document(&self) -> Option<DomRoot<Document>> {
851 self.currently_active
852 .get()
853 .and_then(ScriptThread::find_document)
854 }
855
856 pub(crate) fn parent(&self) -> Option<&WindowProxy> {
857 self.parent.as_deref()
858 }
859
860 pub(crate) fn top(&self) -> &WindowProxy {
861 let mut result = self;
862 while let Some(parent) = result.parent() {
863 result = parent;
864 }
865 result
866 }
867
868 pub(crate) fn document_origin(&self) -> Option<OriginSnapshot> {
869 let pipeline_id = self.currently_active()?;
870 let (result_sender, result_receiver) = generic_channel::channel().unwrap();
871 self.global()
872 .script_to_constellation_chan()
873 .send(ScriptToConstellationMessage::GetDocumentOrigin(
874 pipeline_id,
875 result_sender,
876 ))
877 .ok()?;
878 result_receiver.recv().ok()?
879 }
880
881 pub(crate) fn internal_ancestor_origin_objects_list(&self) -> Option<Vec<ImmutableOrigin>> {
882 let pipeline_id = self.currently_active()?;
883 let (result_sender, result_receiver) = generic_channel::channel().unwrap();
884 self.global()
885 .script_to_constellation_chan()
886 .send(
887 ScriptToConstellationMessage::GetInternalAncestorOriginObjectsList(
888 pipeline_id,
889 result_sender,
890 ),
891 )
892 .ok()?;
893 result_receiver.recv().ok()?
894 }
895
896 pub(crate) fn parent_origin_and_internal_ancestor_origin_objects_list(
898 &self,
899 ) -> Option<(OriginSnapshot, Vec<ImmutableOrigin>)> {
900 if let Some(frame_element) = self.frame_element() {
901 let parent_document = frame_element.owner_document();
902 if !parent_document.is_fully_active() {
905 return None;
906 }
907 Some((
908 parent_document.origin().snapshot(),
909 parent_document
910 .internal_ancestor_origin_objects_list()
911 .clone()
912 .expect("Must always be fully active"),
913 ))
914 } else if let Some(parent_proxy) = self.parent() {
915 assert!(parent_proxy.currently_active().is_some());
917
918 let origin = parent_proxy
919 .document_origin()
920 .expect("Must always be active");
921 let list = parent_proxy
922 .internal_ancestor_origin_objects_list()
923 .expect("Must always be active");
924 Some((origin, list))
925 } else {
926 None
927 }
928 }
929
930 #[expect(unsafe_code)]
931 fn set_window(&self, cx: &mut JSContext, window: &GlobalScope, handler: &WindowProxyHandler) {
935 unsafe {
936 debug!("Setting window of {:p}.", self);
937
938 let window_jsobject = window.reflector().get_jsobject();
939 let old_js_proxy = self.reflector.get_jsobject();
940 assert!(!window_jsobject.get().is_null());
941 assert_ne!(
942 ((*get_object_class(window_jsobject.get())).flags & JSCLASS_IS_GLOBAL),
943 0
944 );
945
946 let mut realm = AutoRealm::new_from_handle(cx, window_jsobject);
947 let cx = &mut realm;
948
949 SetProxyReservedSlot(old_js_proxy.get(), 0, &PrivateValue(ptr::null_mut()));
951
952 rooted!(&in(cx) let new_js_proxy = handler.new_window_proxy(cx, window_jsobject));
958 SetProxyReservedSlot(new_js_proxy.get(), 0, &PrivateValue(ptr::null_mut()));
961 debug!(
962 "Transplanting proxy from {:p} to {:p}.",
963 old_js_proxy.get(),
964 new_js_proxy.get()
965 );
966 rooted!(&in(cx) let new_js_proxy = JS_TransplantObject(cx, old_js_proxy, new_js_proxy.handle()));
967 debug!("Transplanted proxy is {:p}.", new_js_proxy.get());
968
969 SetProxyReservedSlot(
971 new_js_proxy.get(),
972 0,
973 &PrivateValue(self as *const _ as *const libc::c_void),
974 );
975
976 SetWindowProxy(cx, window_jsobject, new_js_proxy.handle());
978
979 debug!(
981 "Setting reflector of {:p} to {:p}.",
982 self,
983 new_js_proxy.get()
984 );
985 self.reflector.rootable().set(new_js_proxy.get());
986 }
987 }
988
989 pub(crate) fn set_pipeline_id(&self, pipeline_id: PipelineId) {
990 self.currently_active.set(Some(pipeline_id));
991 }
992
993 pub(crate) fn set_currently_active(&self, cx: &mut JSContext, window: &Window) {
994 if let Some(pipeline_id) = self.currently_active() &&
995 pipeline_id == window.pipeline_id()
996 {
997 return debug!(
998 "Attempt to set the currently active window to the currently active window."
999 );
1000 }
1001
1002 let global_scope = window.as_global_scope();
1003 self.set_window(cx, global_scope, WindowProxyHandler::proxy_handler());
1004 self.currently_active.set(Some(global_scope.pipeline_id()));
1005 }
1006
1007 pub(crate) fn unset_currently_active(&self, cx: &mut JSContext) {
1008 if self.currently_active().is_none() {
1009 return debug!(
1010 "Attempt to unset the currently active window on a windowproxy that does not have one."
1011 );
1012 }
1013 let globalscope = self.global();
1014 let window = DissimilarOriginWindow::new(cx, &globalscope, self);
1015 self.set_window(
1016 cx,
1017 window.upcast(),
1018 WindowProxyHandler::x_origin_proxy_handler(),
1019 );
1020 self.currently_active.set(None);
1021 }
1022
1023 pub(crate) fn currently_active(&self) -> Option<PipelineId> {
1024 self.currently_active.get()
1025 }
1026
1027 pub(crate) fn get_name(&self) -> DOMString {
1028 self.name.borrow().clone()
1029 }
1030
1031 pub(crate) fn set_name(&self, name: DOMString) {
1032 *self.name.borrow_mut() = name;
1033 }
1034}
1035
1036#[derive(Debug, Deserialize, Serialize)]
1048pub(crate) struct CreatorBrowsingContextInfo {
1049 url: Option<ServoUrl>,
1051
1052 origin: Option<ImmutableOrigin>,
1054}
1055
1056impl CreatorBrowsingContextInfo {
1057 pub(crate) fn from(
1058 parent: Option<&WindowProxy>,
1059 opener: Option<&WindowProxy>,
1060 ) -> CreatorBrowsingContextInfo {
1061 let creator = match (parent, opener) {
1062 (Some(parent), _) => parent.document(),
1063 (None, Some(opener)) => opener.document(),
1064 (None, None) => None,
1065 };
1066
1067 let url = creator.as_deref().map(|document| document.url());
1068 let origin = creator
1069 .as_deref()
1070 .map(|document| document.origin().immutable().clone());
1071
1072 CreatorBrowsingContextInfo { url, origin }
1073 }
1074}
1075
1076fn tokenize_open_features(features: DOMString) -> IndexMap<String, String> {
1078 let is_feature_sep = |c: char| c.is_ascii_whitespace() || ['=', ','].contains(&c);
1079 let mut tokenized_features = IndexMap::new();
1081 let features = features.str();
1083 let mut iter = features.chars();
1084 let mut cur = iter.next();
1085
1086 while cur.is_some() {
1088 let mut name = String::new();
1090 let mut value = String::new();
1091 while let Some(cur_char) = cur {
1093 if !is_feature_sep(cur_char) {
1094 break;
1095 }
1096 cur = iter.next();
1097 }
1098 while let Some(cur_char) = cur {
1100 if is_feature_sep(cur_char) {
1101 break;
1102 }
1103 name.push(cur_char.to_ascii_lowercase());
1104 cur = iter.next();
1105 }
1106 let normalized_name = String::from(match name.as_ref() {
1108 "screenx" => "left",
1109 "screeny" => "top",
1110 "innerwidth" => "width",
1111 "innerheight" => "height",
1112 _ => name.as_ref(),
1113 });
1114 while let Some(cur_char) = cur {
1116 if cur_char == '=' || cur_char == ',' || !is_feature_sep(cur_char) {
1117 break;
1118 }
1119 cur = iter.next();
1120 }
1121 if cur.is_some() && is_feature_sep(cur.unwrap()) {
1123 while let Some(cur_char) = cur {
1125 if !is_feature_sep(cur_char) || cur_char == ',' {
1126 break;
1127 }
1128 cur = iter.next();
1129 }
1130 while let Some(cur_char) = cur {
1132 if is_feature_sep(cur_char) {
1133 break;
1134 }
1135 value.push(cur_char.to_ascii_lowercase());
1136 cur = iter.next();
1137 }
1138 }
1139 if !name.is_empty() {
1141 tokenized_features.insert(normalized_name, value);
1142 }
1143 }
1144 tokenized_features
1146}
1147
1148fn parse_open_feature_boolean(tokenized_features: &IndexMap<String, String>, name: &str) -> bool {
1150 if let Some(value) = tokenized_features.get(name) {
1151 if value.is_empty() || value == "yes" {
1153 return true;
1154 }
1155 if let Ok(int) = parse_integer(value.chars()) {
1157 return int != 0;
1158 }
1159 }
1160 false
1162}
1163
1164#[expect(unsafe_code)]
1168#[expect(non_snake_case)]
1169unsafe fn GetSubframeWindowProxy(
1170 cx: &mut JSContext,
1171 proxy: RawHandleObject,
1172 id: RawHandleId,
1173) -> Option<(DomRoot<WindowProxy>, u32)> {
1174 let index = get_array_index_from_id(unsafe { Handle::from_raw(id) });
1175 if let Some(index) = index {
1176 let mut slot = UndefinedValue();
1177 unsafe { GetProxyPrivate(*proxy, &mut slot) };
1178 rooted!(&in(cx) let target = slot.to_object());
1179 let script_window_proxies = ScriptThread::window_proxies();
1180 if let Ok(win) = root_from_handleobject::<Window>(cx, target.handle()) {
1181 let browsing_context_id = win.window_proxy().browsing_context_id();
1182 let (result_sender, result_receiver) = generic_channel::channel().unwrap();
1183
1184 let _ = win.as_global_scope().script_to_constellation_chan().send(
1185 ScriptToConstellationMessage::GetChildBrowsingContextId(
1186 browsing_context_id,
1187 index as usize,
1188 result_sender,
1189 ),
1190 );
1191 return result_receiver
1192 .recv()
1193 .ok()
1194 .and_then(|maybe_bcid| maybe_bcid)
1195 .and_then(|id| script_window_proxies.find_window_proxy(id))
1196 .map(|proxy| (proxy, (JSPROP_ENUMERATE | JSPROP_READONLY) as u32));
1197 } else if let Ok(win) =
1198 root_from_handleobject::<DissimilarOriginWindow>(cx, target.handle())
1199 {
1200 let browsing_context_id = win.window_proxy().browsing_context_id();
1201 let (result_sender, result_receiver) = generic_channel::channel().unwrap();
1202
1203 let _ = win.global().script_to_constellation_chan().send(
1204 ScriptToConstellationMessage::GetChildBrowsingContextId(
1205 browsing_context_id,
1206 index as usize,
1207 result_sender,
1208 ),
1209 );
1210 return result_receiver
1211 .recv()
1212 .ok()
1213 .and_then(|maybe_bcid| maybe_bcid)
1214 .and_then(|id| script_window_proxies.find_window_proxy(id))
1215 .map(|proxy| (proxy, JSPROP_READONLY as u32));
1216 }
1217 }
1218
1219 None
1220}
1221
1222#[expect(unsafe_code)]
1223unsafe extern "C" fn get_own_property_descriptor(
1224 cx: *mut RawJSContext,
1225 proxy: RawHandleObject,
1226 id: RawHandleId,
1227 desc: RawMutableHandle<PropertyDescriptor>,
1228 is_none: *mut bool,
1229) -> bool {
1230 let mut cx = unsafe {
1231 JSContext::from_ptr(NonNull::new(cx).expect("JSContext should not be null in SM hook"))
1233 };
1234 let cx = &mut cx;
1235 let window = unsafe { GetSubframeWindowProxy(cx, proxy, id) };
1236 let desc = unsafe { MutableHandle::from_raw(desc) };
1237 if let Some((window, attrs)) = window {
1238 rooted!(&in(cx) let mut val = UndefinedValue());
1239 window.safe_to_jsval(cx, val.handle_mut());
1240 set_property_descriptor(desc, val.handle(), attrs, unsafe { &mut *is_none });
1241 return true;
1242 }
1243
1244 let mut slot = UndefinedValue();
1245 unsafe { GetProxyPrivate(proxy.get(), &mut slot) };
1246 rooted!(&in(cx) let target = slot.to_object());
1247 unsafe {
1248 JS_GetOwnPropertyDescriptorById(cx, target.handle(), Handle::from_raw(id), desc, is_none)
1249 }
1250}
1251
1252#[expect(unsafe_code)]
1253unsafe extern "C" fn define_property(
1254 cx: *mut RawJSContext,
1255 proxy: RawHandleObject,
1256 id: RawHandleId,
1257 desc: RawHandle<PropertyDescriptor>,
1258 res: *mut ObjectOpResult,
1259) -> bool {
1260 if get_array_index_from_id(unsafe { Handle::from_raw(id) }).is_some() {
1261 unsafe {
1266 (*res).code_ = JSErrNum::JSMSG_CANT_DEFINE_WINDOW_ELEMENT as ::libc::uintptr_t;
1267 }
1268 return true;
1269 }
1270
1271 let mut slot = UndefinedValue();
1272 unsafe { GetProxyPrivate(*proxy.ptr, &mut slot) };
1273 rooted!(in(cx) let target = slot.to_object());
1274 unsafe { JS_DefinePropertyById(cx, target.handle().into(), id, desc, res) }
1275}
1276
1277#[expect(unsafe_code)]
1278unsafe extern "C" fn has(
1279 cx: *mut RawJSContext,
1280 proxy: RawHandleObject,
1281 id: RawHandleId,
1282 bp: *mut bool,
1283) -> bool {
1284 let mut cx = unsafe {
1285 JSContext::from_ptr(NonNull::new(cx).expect("JSContext should not be null in SM hook"))
1287 };
1288 let cx = &mut cx;
1289 let window = unsafe { GetSubframeWindowProxy(cx, proxy, id) };
1290 if window.is_some() {
1291 unsafe { *bp = true };
1292 return true;
1293 }
1294
1295 let mut slot = UndefinedValue();
1296 unsafe { GetProxyPrivate(*proxy.ptr, &mut slot) };
1297 rooted!(&in(cx) let target = slot.to_object());
1298 let mut found = false;
1299 if !unsafe { JS_HasPropertyById(cx, target.handle(), Handle::from_raw(id), &mut found) } {
1300 return false;
1301 }
1302
1303 unsafe { *bp = found };
1304 true
1305}
1306
1307#[expect(unsafe_code)]
1308unsafe extern "C" fn get(
1309 cx: *mut RawJSContext,
1310 proxy: RawHandleObject,
1311 receiver: RawHandleValue,
1312 id: RawHandleId,
1313 vp: RawMutableHandleValue,
1314) -> bool {
1315 let mut cx = unsafe {
1316 JSContext::from_ptr(NonNull::new(cx).expect("JSContext should not be null in SM hook"))
1318 };
1319 let cx = &mut cx;
1320 let window = unsafe { GetSubframeWindowProxy(cx, proxy, id) };
1321 let vp = unsafe { MutableHandle::from_raw(vp) };
1322 if let Some((window, _attrs)) = window {
1323 window.safe_to_jsval(cx, vp);
1324 return true;
1325 }
1326
1327 let mut slot = UndefinedValue();
1328 unsafe { GetProxyPrivate(*proxy.ptr, &mut slot) };
1329 rooted!(&in(cx) let target = slot.to_object());
1330 unsafe {
1331 JS_ForwardGetPropertyTo(
1332 cx,
1333 target.handle(),
1334 Handle::from_raw(id),
1335 Handle::from_raw(receiver),
1336 vp,
1337 )
1338 }
1339}
1340
1341#[expect(unsafe_code)]
1342unsafe extern "C" fn set(
1343 cx: *mut RawJSContext,
1344 proxy: RawHandleObject,
1345 id: RawHandleId,
1346 v: RawHandleValue,
1347 receiver: RawHandleValue,
1348 res: *mut ObjectOpResult,
1349) -> bool {
1350 if get_array_index_from_id(unsafe { Handle::from_raw(id) }).is_some() {
1351 unsafe { (*res).code_ = JSErrNum::JSMSG_READ_ONLY as ::libc::uintptr_t };
1353 return true;
1354 }
1355
1356 let mut slot = UndefinedValue();
1357 unsafe { GetProxyPrivate(*proxy.ptr, &mut slot) };
1358 rooted!(in(cx) let target = slot.to_object());
1359 unsafe { JS_ForwardSetPropertyTo(cx, target.handle().into(), id, v, receiver, res) }
1360}
1361
1362#[expect(unsafe_code)]
1363unsafe extern "C" fn get_prototype_if_ordinary(
1364 _: *mut RawJSContext,
1365 _: RawHandleObject,
1366 is_ordinary: *mut bool,
1367 _: RawMutableHandleObject,
1368) -> bool {
1369 unsafe { *is_ordinary = false };
1382 true
1383}
1384
1385#[expect(unsafe_code)]
1386unsafe extern "C" fn maybe_cross_origin_get_prototype_wrapper_rawcx(
1387 cx: *mut RawJSContext,
1388 proxy: RawHandleObject,
1389 result: RawMutableHandleObject,
1390) -> bool {
1391 let mut cx = unsafe { JSContext::from_ptr(ptr::NonNull::new(cx).unwrap()) };
1392 let mut realm = CurrentRealm::assert(&mut cx);
1393 let proxy = unsafe { Handle::from_raw(proxy) };
1394 let result = unsafe { MutableHandleObject::from_raw(result) };
1395 maybe_cross_origin_get_prototype::<DomTypeHolder>(
1396 &mut realm,
1397 proxy,
1398 GetProtoObject::<DomTypeHolder>,
1399 result,
1400 )
1401}
1402
1403static PROXY_TRAPS: ProxyTraps = ProxyTraps {
1407 enter: None,
1408 getOwnPropertyDescriptor: Some(get_own_property_descriptor),
1409 defineProperty: Some(define_property),
1410 ownPropertyKeys: None,
1411 delete_: None,
1412 enumerate: None,
1413 getPrototypeIfOrdinary: Some(get_prototype_if_ordinary),
1414 getPrototype: Some(maybe_cross_origin_get_prototype_wrapper_rawcx),
1415 setPrototype: Some(maybe_cross_origin_set_prototype_rawcx),
1416 setImmutablePrototype: None,
1417 preventExtensions: Some(prevent_extensions),
1418 isExtensible: Some(is_extensible),
1419 has: Some(has),
1420 get: Some(get),
1421 set: Some(set),
1422 call: None,
1423 construct: None,
1424 hasOwn: None,
1425 getOwnEnumerablePropertyKeys: None,
1426 nativeCall: None,
1427 objectClassIs: None,
1428 className: None,
1429 fun_toString: None,
1430 boxedValue_unbox: None,
1431 defaultValue: None,
1432 trace: Some(trace),
1433 finalize: Some(finalize),
1434 objectMoved: None,
1435 isCallable: None,
1436 isConstructor: None,
1437};
1438
1439pub(crate) struct WindowProxyHandler(*const libc::c_void);
1442
1443impl MallocSizeOf for WindowProxyHandler {
1444 fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
1445 0
1447 }
1448}
1449
1450#[expect(unsafe_code)]
1452unsafe impl Send for WindowProxyHandler {}
1453#[expect(unsafe_code)]
1455unsafe impl Sync for WindowProxyHandler {}
1456
1457#[expect(unsafe_code)]
1458impl WindowProxyHandler {
1459 fn new(traps: &ProxyTraps) -> Self {
1460 let ptr = unsafe { CreateWrapperProxyHandler(traps) };
1462 assert!(!ptr.is_null());
1463 Self(ptr)
1464 }
1465
1466 pub(crate) fn x_origin_proxy_handler() -> &'static Self {
1468 use std::sync::OnceLock;
1469 static SINGLETON: OnceLock<WindowProxyHandler> = OnceLock::new();
1475 SINGLETON.get_or_init(|| Self::new(&CROSS_ORIGIN_PROXY_TRAPS))
1476 }
1477
1478 pub(crate) fn proxy_handler() -> &'static Self {
1480 use std::sync::OnceLock;
1481 static SINGLETON: OnceLock<WindowProxyHandler> = OnceLock::new();
1487 SINGLETON.get_or_init(|| Self::new(&PROXY_TRAPS))
1488 }
1489
1490 fn new_window_proxy(
1493 &self,
1494 cx: &mut JSContext,
1495 window_jsobject: js::gc::HandleObject,
1496 ) -> *mut JSObject {
1497 let obj = unsafe { NewWindowProxy(cx, window_jsobject, self.0) };
1498 assert!(!obj.is_null());
1499 obj
1500 }
1501}
1502
1503#[expect(unsafe_code)]
1504impl Drop for WindowProxyHandler {
1505 fn drop(&mut self) {
1506 unsafe {
1509 DeleteWrapperProxyHandler(self.0);
1510 }
1511 }
1512}
1513
1514#[expect(unsafe_code)]
1522fn throw_security_error(realm: &mut CurrentRealm) -> bool {
1523 if !unsafe { JS_IsExceptionPending(realm) } {
1524 let global = GlobalScope::from_current_realm(realm);
1525 throw_dom_exception(realm, &global, Error::Security(None));
1526 }
1527 false
1528}
1529
1530#[expect(unsafe_code)]
1531unsafe extern "C" fn cross_origin_has(
1532 cx: *mut RawJSContext,
1533 proxy: RawHandleObject,
1534 id: RawHandleId,
1535 bp: *mut bool,
1536) -> bool {
1537 let mut cx = unsafe {
1538 JSContext::from_ptr(NonNull::new(cx).expect("JSContext should not be null in SM hook"))
1540 };
1541 let mut slot = UndefinedValue();
1542 unsafe { GetProxyPrivate(*proxy.ptr, &mut slot) };
1543 rooted!(&in(cx) let target = slot.to_object());
1544 let mut found = false;
1545 unsafe { JS_HasOwnPropertyById(&mut cx, target.handle(), Handle::from_raw(id), &mut found) };
1546 if found {
1547 unsafe { *bp = true };
1548 true
1549 } else {
1550 let mut realm = CurrentRealm::assert(&mut cx);
1551 throw_security_error(&mut realm)
1552 }
1553}
1554
1555#[expect(unsafe_code)]
1556unsafe extern "C" fn cross_origin_get(
1557 cx: *mut RawJSContext,
1558 proxy: RawHandleObject,
1559 receiver: RawHandleValue,
1560 id: RawHandleId,
1561 vp: RawMutableHandleValue,
1562) -> bool {
1563 let mut found = false;
1564 unsafe { cross_origin_has(cx, proxy, id, &mut found) };
1565 found && unsafe { get(cx, proxy, receiver, id, vp) }
1566}
1567
1568#[expect(unsafe_code)]
1569unsafe extern "C" fn cross_origin_set(
1570 cx: *mut RawJSContext,
1571 _: RawHandleObject,
1572 _: RawHandleId,
1573 _: RawHandleValue,
1574 _: RawHandleValue,
1575 _: *mut ObjectOpResult,
1576) -> bool {
1577 let mut cx = unsafe {
1578 JSContext::from_ptr(NonNull::new(cx).expect("JSContext should not be null in SM hook"))
1580 };
1581 let mut realm = CurrentRealm::assert(&mut cx);
1582 throw_security_error(&mut realm)
1583}
1584
1585#[expect(unsafe_code)]
1586unsafe extern "C" fn delete_xorigin(
1587 cx: *mut RawJSContext,
1588 _: RawHandleObject,
1589 _: RawHandleId,
1590 _: *mut ObjectOpResult,
1591) -> bool {
1592 let mut cx = unsafe {
1593 JSContext::from_ptr(NonNull::new(cx).expect("JSContext should not be null in SM hook"))
1595 };
1596 let mut realm = CurrentRealm::assert(&mut cx);
1597 throw_security_error(&mut realm)
1598}
1599
1600#[expect(unsafe_code)]
1601unsafe extern "C" fn cross_origin_get_own_property_descriptor(
1602 cx: *mut RawJSContext,
1603 proxy: RawHandleObject,
1604 id: RawHandleId,
1605 desc: RawMutableHandle<PropertyDescriptor>,
1606 is_none: *mut bool,
1607) -> bool {
1608 let mut found = false;
1609 unsafe { cross_origin_has(cx, proxy, id, &mut found) };
1610 found && unsafe { get_own_property_descriptor(cx, proxy, id, desc, is_none) }
1611}
1612
1613#[expect(unsafe_code)]
1614unsafe extern "C" fn cross_origin_define_property(
1615 cx: *mut RawJSContext,
1616 _: RawHandleObject,
1617 _: RawHandleId,
1618 _: RawHandle<PropertyDescriptor>,
1619 _: *mut ObjectOpResult,
1620) -> bool {
1621 let mut cx = unsafe {
1622 JSContext::from_ptr(NonNull::new(cx).expect("JSContext should not be null in SM hook"))
1624 };
1625 let mut realm = CurrentRealm::assert(&mut cx);
1626 throw_security_error(&mut realm)
1627}
1628
1629static CROSS_ORIGIN_PROXY_TRAPS: ProxyTraps = ProxyTraps {
1635 enter: None,
1636 getOwnPropertyDescriptor: Some(cross_origin_get_own_property_descriptor),
1637 defineProperty: Some(cross_origin_define_property),
1638 ownPropertyKeys: None,
1639 delete_: Some(delete_xorigin),
1640 enumerate: None,
1641 getPrototypeIfOrdinary: Some(maybe_cross_origin_get_prototype_if_ordinary_rawcx),
1642 getPrototype: Some(maybe_cross_origin_get_prototype_wrapper_rawcx),
1643 setPrototype: Some(maybe_cross_origin_set_prototype_rawcx),
1644 setImmutablePrototype: None,
1645 preventExtensions: Some(prevent_extensions),
1646 isExtensible: Some(is_extensible),
1647 has: Some(cross_origin_has),
1648 get: Some(cross_origin_get),
1649 set: Some(cross_origin_set),
1650 call: None,
1651 construct: None,
1652 hasOwn: Some(cross_origin_has),
1653 getOwnEnumerablePropertyKeys: None,
1654 nativeCall: None,
1655 objectClassIs: None,
1656 className: None,
1657 fun_toString: None,
1658 boxedValue_unbox: None,
1659 defaultValue: None,
1660 trace: Some(trace),
1661 finalize: Some(finalize),
1662 objectMoved: None,
1663 isCallable: None,
1664 isConstructor: None,
1665};
1666
1667#[expect(unsafe_code)]
1670unsafe extern "C" fn finalize(_fop: *mut GCContext, obj: *mut JSObject) {
1671 let mut slot = UndefinedValue();
1672 unsafe { GetProxyReservedSlot(obj, 0, &mut slot) };
1673 let this = slot.to_private() as *mut WindowProxy;
1674 if this.is_null() {
1675 return;
1677 }
1678 unsafe {
1679 (*this).reflector.drop_memory(&*this);
1680 let jsobject = (*this).reflector.get_jsobject().get();
1681 debug!(
1682 "WindowProxy finalize: {:p}, with reflector {:p} from {:p}.",
1683 this, jsobject, obj
1684 );
1685 let _ = Box::from_raw(this);
1686 }
1687}
1688
1689#[expect(unsafe_code)]
1690unsafe extern "C" fn trace(trc: *mut JSTracer, obj: *mut JSObject) {
1691 let mut slot = UndefinedValue();
1692 unsafe { GetProxyReservedSlot(obj, 0, &mut slot) };
1693 let this = slot.to_private() as *const WindowProxy;
1694 if this.is_null() {
1695 return;
1697 }
1698 unsafe { (*this).trace(trc) };
1699}