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, cross_origin_get_own_property_helper, cross_origin_own_property_keys,
53 cross_origin_property_fallback, cross_origin_set, is_extensible,
54 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 chosen = if name.is_empty() || name.eq_ignore_ascii_case("_self") {
669 Some((Some(DomRoot::from_ref(self)), false))
672 } else if name.eq_ignore_ascii_case("_parent") {
673 Some(
677 self.parent()
678 .map(|parent| (Some(DomRoot::from_ref(parent)), false))
679 .unwrap_or_else(|| (Some(DomRoot::from_ref(self)), false)),
680 )
681 } else if name.eq_ignore_ascii_case("_top") {
682 Some((Some(DomRoot::from_ref(self.top())), false))
685 } else if !name.eq_ignore_ascii_case("_blank") {
686 self.find_navigable_by_target_name(&name)
694 .map(|proxy| (Some(proxy), false))
695 } else {
696 None
697 };
698
699 if let Some(chosen) = chosen {
700 return chosen;
701 }
702
703 (
708 self.create_auxiliary_browsing_context(cx, name, noopener),
709 true,
710 )
711 }
712
713 fn find_navigable_by_target_name(&self, name: &DOMString) -> Option<DomRoot<WindowProxy>> {
715 let top = self.top();
735 let subtrees_to_search = if top != self {
736 Either::Left([self, top])
737 } else {
738 Either::Right([self])
739 };
740
741 for subtree_to_search in subtrees_to_search.into_iter() {
743 if let Some(result) =
747 subtree_to_search.find_navigable_by_target_name_in_descendants(name)
748 {
749 return Some(result);
750 }
751 }
752
753 let mut top_level_window_proxies = self.script_window_proxies.top_level_window_proxies();
760
761 top_level_window_proxies
769 .sort_by_key(|proxy| std::cmp::Reverse(proxy.browsing_context_id()));
770
771 for window_proxy in top_level_window_proxies {
772 if &*window_proxy == top {
775 continue;
776 }
777 if let Some(result) = window_proxy.find_navigable_by_target_name_in_descendants(name) {
795 return Some(result);
796 }
797 }
798
799 None
801 }
802
803 fn find_navigable_by_target_name_in_descendants(
805 &self,
806 name: &DOMString,
807 ) -> Option<DomRoot<WindowProxy>> {
808 if self.is_browsing_context_discarded() {
810 return None;
811 }
812
813 if self.get_name() == *name {
819 return Some(DomRoot::from_ref(self));
820 }
821
822 let document = self.document()?;
823 let iframes: Vec<_> = document.iframes().iter().collect();
824 iframes.iter().find_map(|iframe| {
825 iframe
826 .browsing_context_id()
827 .and_then(|browsing_context_id| {
828 self.script_window_proxies
829 .find_window_proxy(browsing_context_id)?
830 .find_navigable_by_target_name_in_descendants(name)
831 })
832 })
833 }
834
835 pub(crate) fn is_auxiliary(&self) -> bool {
836 self.opener.is_some()
837 }
838
839 pub(crate) fn discard_browsing_context(&self) {
840 self.discarded.set(true);
841 }
842
843 pub(crate) fn is_browsing_context_discarded(&self) -> bool {
844 self.discarded.get()
845 }
846
847 pub(crate) fn browsing_context_id(&self) -> BrowsingContextId {
848 self.browsing_context_id
849 }
850
851 pub(crate) fn webview_id(&self) -> WebViewId {
852 self.webview_id
853 }
854
855 pub(crate) fn frame_element(&self) -> Option<&Element> {
859 self.frame_element.as_deref()
860 }
861
862 pub(crate) fn document(&self) -> Option<DomRoot<Document>> {
863 self.currently_active
864 .get()
865 .and_then(ScriptThread::find_document)
866 }
867
868 pub(crate) fn parent(&self) -> Option<&WindowProxy> {
869 self.parent.as_deref()
870 }
871
872 pub(crate) fn top(&self) -> &WindowProxy {
873 let mut result = self;
874 while let Some(parent) = result.parent() {
875 result = parent;
876 }
877 result
878 }
879
880 pub(crate) fn document_origin(&self) -> Option<OriginSnapshot> {
881 let pipeline_id = self.currently_active()?;
882 let (result_sender, result_receiver) = generic_channel::channel().unwrap();
883 self.global()
884 .script_to_constellation_chan()
885 .send(ScriptToConstellationMessage::GetDocumentOrigin(
886 pipeline_id,
887 result_sender,
888 ))
889 .ok()?;
890 result_receiver.recv().ok()?
891 }
892
893 pub(crate) fn internal_ancestor_origin_objects_list(&self) -> Option<Vec<ImmutableOrigin>> {
894 let pipeline_id = self.currently_active()?;
895 let (result_sender, result_receiver) = generic_channel::channel().unwrap();
896 self.global()
897 .script_to_constellation_chan()
898 .send(
899 ScriptToConstellationMessage::GetInternalAncestorOriginObjectsList(
900 pipeline_id,
901 result_sender,
902 ),
903 )
904 .ok()?;
905 result_receiver.recv().ok()?
906 }
907
908 pub(crate) fn parent_origin_and_internal_ancestor_origin_objects_list(
910 &self,
911 ) -> Option<(OriginSnapshot, Vec<ImmutableOrigin>)> {
912 if let Some(frame_element) = self.frame_element() {
913 let parent_document = frame_element.owner_document();
914 if !parent_document.is_fully_active() {
917 return None;
918 }
919 Some((
920 parent_document.origin().snapshot(),
921 parent_document
922 .internal_ancestor_origin_objects_list()
923 .clone()
924 .expect("Must always be fully active"),
925 ))
926 } else if let Some(parent_proxy) = self.parent() {
927 assert!(parent_proxy.currently_active().is_some());
929
930 let origin = parent_proxy
931 .document_origin()
932 .expect("Must always be active");
933 let list = parent_proxy
934 .internal_ancestor_origin_objects_list()
935 .expect("Must always be active");
936 Some((origin, list))
937 } else {
938 None
939 }
940 }
941
942 #[expect(unsafe_code)]
943 fn set_window(&self, cx: &mut JSContext, window: &GlobalScope) {
947 unsafe {
948 debug!("Setting window of {:p}.", self);
949
950 let window_jsobject = window.reflector().get_jsobject();
951 let old_js_proxy = self.reflector.get_jsobject();
952 assert!(!window_jsobject.get().is_null());
953 assert_ne!(
954 ((*get_object_class(window_jsobject.get())).flags & JSCLASS_IS_GLOBAL),
955 0
956 );
957
958 let mut realm = AutoRealm::new_from_handle(cx, window_jsobject);
959 let cx = &mut realm;
960
961 SetProxyReservedSlot(old_js_proxy.get(), 0, &PrivateValue(ptr::null_mut()));
963
964 rooted!(&in(cx) let new_js_proxy = WindowProxyHandler::proxy_handler().new_window_proxy(cx, window_jsobject));
969 SetProxyReservedSlot(new_js_proxy.get(), 0, &PrivateValue(ptr::null_mut()));
972 debug!(
973 "Transplanting proxy from {:p} to {:p}.",
974 old_js_proxy.get(),
975 new_js_proxy.get()
976 );
977 rooted!(&in(cx) let new_js_proxy = JS_TransplantObject(cx, old_js_proxy, new_js_proxy.handle()));
978 debug!("Transplanted proxy is {:p}.", new_js_proxy.get());
979
980 SetProxyReservedSlot(
982 new_js_proxy.get(),
983 0,
984 &PrivateValue(self as *const _ as *const libc::c_void),
985 );
986
987 SetWindowProxy(cx, window_jsobject, new_js_proxy.handle());
989
990 debug!(
992 "Setting reflector of {:p} to {:p}.",
993 self,
994 new_js_proxy.get()
995 );
996 self.reflector.rootable().set(new_js_proxy.get());
997 }
998 }
999
1000 pub(crate) fn set_pipeline_id(&self, pipeline_id: PipelineId) {
1001 self.currently_active.set(Some(pipeline_id));
1002 }
1003
1004 pub(crate) fn set_currently_active(&self, cx: &mut JSContext, window: &Window) {
1005 if let Some(pipeline_id) = self.currently_active() &&
1006 pipeline_id == window.pipeline_id()
1007 {
1008 return debug!(
1009 "Attempt to set the currently active window to the currently active window."
1010 );
1011 }
1012
1013 let global_scope = window.as_global_scope();
1014 self.set_window(cx, global_scope);
1015 self.currently_active.set(Some(global_scope.pipeline_id()));
1016 }
1017
1018 pub(crate) fn unset_currently_active(&self, cx: &mut JSContext) {
1019 if self.currently_active().is_none() {
1020 return debug!(
1021 "Attempt to unset the currently active window on a windowproxy that does not have one."
1022 );
1023 }
1024 let globalscope = self.global();
1025 let window = DissimilarOriginWindow::new(cx, &globalscope, self);
1026 self.set_window(cx, window.upcast());
1027 self.currently_active.set(None);
1028 }
1029
1030 pub(crate) fn currently_active(&self) -> Option<PipelineId> {
1031 self.currently_active.get()
1032 }
1033
1034 pub(crate) fn get_name(&self) -> DOMString {
1035 self.name.borrow().clone()
1036 }
1037
1038 pub(crate) fn set_name(&self, name: DOMString) {
1039 *self.name.borrow_mut() = name;
1040 }
1041}
1042
1043#[derive(Debug, Deserialize, Serialize)]
1055pub(crate) struct CreatorBrowsingContextInfo {
1056 url: Option<ServoUrl>,
1058
1059 origin: Option<ImmutableOrigin>,
1061}
1062
1063impl CreatorBrowsingContextInfo {
1064 pub(crate) fn from(
1065 parent: Option<&WindowProxy>,
1066 opener: Option<&WindowProxy>,
1067 ) -> CreatorBrowsingContextInfo {
1068 let creator = match (parent, opener) {
1069 (Some(parent), _) => parent.document(),
1070 (None, Some(opener)) => opener.document(),
1071 (None, None) => None,
1072 };
1073
1074 let url = creator.as_deref().map(|document| document.url());
1075 let origin = creator
1076 .as_deref()
1077 .map(|document| document.origin().immutable().clone());
1078
1079 CreatorBrowsingContextInfo { url, origin }
1080 }
1081}
1082
1083fn tokenize_open_features(features: DOMString) -> IndexMap<String, String> {
1085 let is_feature_sep = |c: char| c.is_ascii_whitespace() || ['=', ','].contains(&c);
1086 let mut tokenized_features = IndexMap::new();
1088 let features = features.str();
1090 let mut iter = features.chars();
1091 let mut cur = iter.next();
1092
1093 while cur.is_some() {
1095 let mut name = String::new();
1097 let mut value = String::new();
1098 while let Some(cur_char) = cur {
1100 if !is_feature_sep(cur_char) {
1101 break;
1102 }
1103 cur = iter.next();
1104 }
1105 while let Some(cur_char) = cur {
1107 if is_feature_sep(cur_char) {
1108 break;
1109 }
1110 name.push(cur_char.to_ascii_lowercase());
1111 cur = iter.next();
1112 }
1113 let normalized_name = String::from(match name.as_ref() {
1115 "screenx" => "left",
1116 "screeny" => "top",
1117 "innerwidth" => "width",
1118 "innerheight" => "height",
1119 _ => name.as_ref(),
1120 });
1121 while let Some(cur_char) = cur {
1123 if cur_char == '=' || cur_char == ',' || !is_feature_sep(cur_char) {
1124 break;
1125 }
1126 cur = iter.next();
1127 }
1128 if cur.is_some() && is_feature_sep(cur.unwrap()) {
1130 while let Some(cur_char) = cur {
1132 if !is_feature_sep(cur_char) || cur_char == ',' {
1133 break;
1134 }
1135 cur = iter.next();
1136 }
1137 while let Some(cur_char) = cur {
1139 if is_feature_sep(cur_char) {
1140 break;
1141 }
1142 value.push(cur_char.to_ascii_lowercase());
1143 cur = iter.next();
1144 }
1145 }
1146 if !name.is_empty() {
1148 tokenized_features.insert(normalized_name, value);
1149 }
1150 }
1151 tokenized_features
1153}
1154
1155fn parse_open_feature_boolean(tokenized_features: &IndexMap<String, String>, name: &str) -> bool {
1157 if let Some(value) = tokenized_features.get(name) {
1158 if value.is_empty() || value == "yes" {
1160 return true;
1161 }
1162 if let Ok(int) = parse_integer(value.chars()) {
1164 return int != 0;
1165 }
1166 }
1167 false
1169}
1170
1171#[expect(unsafe_code)]
1175#[expect(non_snake_case)]
1176unsafe fn GetSubframeWindowProxy(
1177 cx: &mut JSContext,
1178 proxy: RawHandleObject,
1179 id: RawHandleId,
1180) -> Option<(DomRoot<WindowProxy>, u32)> {
1181 let index = get_array_index_from_id(unsafe { Handle::from_raw(id) });
1182 if let Some(index) = index {
1183 let mut slot = UndefinedValue();
1184 unsafe { GetProxyPrivate(*proxy, &mut slot) };
1185 rooted!(&in(cx) let target = slot.to_object());
1186 let script_window_proxies = ScriptThread::window_proxies();
1187 if let Ok(win) = root_from_handleobject::<Window>(cx, target.handle()) {
1188 let browsing_context_id = win.window_proxy().browsing_context_id();
1189 let (result_sender, result_receiver) = generic_channel::channel().unwrap();
1190
1191 let _ = win.as_global_scope().script_to_constellation_chan().send(
1192 ScriptToConstellationMessage::GetChildBrowsingContextId(
1193 browsing_context_id,
1194 index as usize,
1195 result_sender,
1196 ),
1197 );
1198 return result_receiver
1199 .recv()
1200 .ok()
1201 .and_then(|maybe_bcid| maybe_bcid)
1202 .and_then(|id| script_window_proxies.find_window_proxy(id))
1203 .map(|proxy| (proxy, (JSPROP_ENUMERATE | JSPROP_READONLY) as u32));
1204 } else if let Ok(win) =
1205 root_from_handleobject::<DissimilarOriginWindow>(cx, target.handle())
1206 {
1207 let browsing_context_id = win.window_proxy().browsing_context_id();
1208 let (result_sender, result_receiver) = generic_channel::channel().unwrap();
1209
1210 let _ = win.global().script_to_constellation_chan().send(
1211 ScriptToConstellationMessage::GetChildBrowsingContextId(
1212 browsing_context_id,
1213 index as usize,
1214 result_sender,
1215 ),
1216 );
1217 return result_receiver
1218 .recv()
1219 .ok()
1220 .and_then(|maybe_bcid| maybe_bcid)
1221 .and_then(|id| script_window_proxies.find_window_proxy(id))
1222 .map(|proxy| (proxy, JSPROP_READONLY as u32));
1223 }
1224 }
1225
1226 None
1227}
1228
1229#[expect(unsafe_code)]
1230fn window_proxy_target(proxy: HandleObject) -> *mut JSObject {
1231 let mut slot = UndefinedValue();
1232 unsafe { GetProxyPrivate(proxy.as_raw(), &mut slot) };
1233 slot.to_object()
1234}
1235
1236#[expect(unsafe_code)]
1238unsafe extern "C" fn get_own_property_descriptor(
1239 cx: *mut RawJSContext,
1240 proxy: RawHandleObject,
1241 id: RawHandleId,
1242 property_descriptor: RawMutableHandle<PropertyDescriptor>,
1243 is_none: *mut bool,
1244) -> bool {
1245 let mut cx = unsafe {
1246 JSContext::from_ptr(NonNull::new(cx).expect("JSContext should not be null in SM hook"))
1249 };
1250 let mut cx = CurrentRealm::assert(&mut cx);
1251 let cx = &mut cx;
1252 let proxy = unsafe { Handle::from_raw(proxy) };
1253 let id = unsafe { Handle::from_raw(id) };
1254 let mut property_descriptor = unsafe { MutableHandle::from_raw(property_descriptor) };
1255 let is_none = unsafe { &mut *is_none };
1256
1257 let window = unsafe { GetSubframeWindowProxy(cx, proxy.into(), id.into()) };
1266 if let Some((window, attrs)) = window {
1267 rooted!(&in(cx) let mut val = UndefinedValue());
1268 window.to_jsval(cx, val.handle_mut());
1269 set_property_descriptor(property_descriptor, val.handle(), attrs, is_none);
1270 return true;
1271 }
1272
1273 rooted!(&in(cx) let target = window_proxy_target(proxy));
1275 if is_platform_object_same_origin(cx, proxy) {
1276 return unsafe {
1277 JS_GetOwnPropertyDescriptorById(cx, target.handle(), id, property_descriptor, is_none)
1278 };
1279 }
1280
1281 let cross_origin_properties = if root_from_handleobject::<Window>(cx, target.handle()).is_ok() {
1282 unsafe { WindowBinding::CROSS_ORIGIN_PROPERTIES.get() }
1283 } else if root_from_handleobject::<DissimilarOriginWindow>(cx, target.handle()).is_ok() {
1284 unsafe { DissimilarOriginWindowBinding::CROSS_ORIGIN_PROPERTIES.get() }
1285 } else {
1286 unreachable!("WindowProxy should always be backed by some kind of window");
1287 };
1288
1289 if !cross_origin_get_own_property_helper(
1291 cx,
1292 proxy,
1293 cross_origin_properties,
1294 id,
1295 property_descriptor.reborrow(),
1296 is_none,
1297 ) {
1298 return false;
1299 }
1300
1301 if !*is_none {
1303 return true;
1304 }
1305
1306 if let Some(named_child_navigable) = named_child_navigable(cx, proxy, id) {
1309 rooted!(&in(cx) let mut window_proxy_value = UndefinedValue());
1311 named_child_navigable.to_jsval(cx, window_proxy_value.handle_mut());
1312 set_property_descriptor(
1315 property_descriptor.reborrow(),
1316 window_proxy_value.handle(),
1317 JSPROP_READONLY as u32,
1318 is_none,
1319 );
1320 return true;
1321 }
1322
1323 cross_origin_property_fallback::<DomTypeHolder>(
1325 cx,
1326 proxy,
1327 id,
1328 property_descriptor.reborrow(),
1329 is_none,
1330 )
1331}
1332
1333#[expect(unsafe_code)]
1335unsafe extern "C" fn define_property(
1336 cx: *mut RawJSContext,
1337 proxy: RawHandleObject,
1338 id: RawHandleId,
1339 desc: RawHandle<PropertyDescriptor>,
1340 res: *mut ObjectOpResult,
1341) -> bool {
1342 let mut cx = unsafe {
1343 JSContext::from_ptr(NonNull::new(cx).expect("JSContext should not be null in SM hook"))
1346 };
1347 let mut cx = CurrentRealm::assert(&mut cx);
1348 let cx = &mut cx;
1349 let id = unsafe { Handle::from_raw(id) };
1350 let proxy = unsafe { Handle::from_raw(proxy) };
1351
1352 if is_platform_object_same_origin(cx, proxy) {
1357 if get_array_index_from_id(id).is_some() {
1359 unsafe {
1364 (*res).code_ = JSErrNum::JSMSG_CANT_DEFINE_WINDOW_ELEMENT as usize;
1365 }
1366 return true;
1367 }
1368
1369 rooted!(&in(cx) let target = window_proxy_target(proxy));
1371 return unsafe {
1372 JS_DefinePropertyById(cx.raw_cx(), target.handle().into(), id.into(), desc, res)
1373 };
1374 }
1375
1376 report_cross_origin_denial::<DomTypeHolder>(cx, id, "define")
1378}
1379
1380#[expect(unsafe_code)]
1381unsafe extern "C" fn has(
1382 cx: *mut RawJSContext,
1383 proxy: RawHandleObject,
1384 id: RawHandleId,
1385 bp: *mut bool,
1386) -> bool {
1387 unsafe { has_or_has_own(cx, proxy, id, bp, IncludePrototypes::Yes) }
1388}
1389
1390#[expect(unsafe_code)]
1391unsafe extern "C" fn has_own(
1392 cx: *mut RawJSContext,
1393 proxy: RawHandleObject,
1394 id: RawHandleId,
1395 bp: *mut bool,
1396) -> bool {
1397 unsafe { has_or_has_own(cx, proxy, id, bp, IncludePrototypes::No) }
1398}
1399
1400enum IncludePrototypes {
1401 Yes,
1402 No,
1403}
1404
1405#[expect(unsafe_code)]
1406unsafe fn has_or_has_own(
1407 cx: *mut RawJSContext,
1408 proxy: RawHandleObject,
1409 id: RawHandleId,
1410 bp: *mut bool,
1411 include_prototypes: IncludePrototypes,
1412) -> bool {
1413 let mut cx = unsafe { JSContext::from_ptr(ptr::NonNull::new(cx).unwrap()) };
1414 let mut cx = CurrentRealm::assert(&mut cx);
1415 let cx = &mut cx;
1416 let proxy = unsafe { Handle::from_raw(proxy) };
1417 let id = unsafe { Handle::from_raw(id) };
1418
1419 let (success, found) = if is_platform_object_same_origin(cx, proxy) {
1420 let window = unsafe { GetSubframeWindowProxy(cx, proxy.into(), id.into()) };
1421 if window.is_some() {
1422 unsafe { *bp = true };
1423 return true;
1424 }
1425
1426 rooted!(&in(cx) let target = window_proxy_target(proxy));
1427 let mut found = false;
1428 let success = match include_prototypes {
1429 IncludePrototypes::Yes => unsafe {
1430 JS_HasPropertyById(cx, target.handle(), id, &mut found)
1431 },
1432 IncludePrototypes::No => unsafe {
1433 JS_HasOwnPropertyById(cx, target.handle(), id, &mut found)
1434 },
1435 };
1436 (success, found)
1437 } else {
1438 rooted!(&in(cx) let mut property_descriptor = PropertyDescriptor::default());
1439 let mut is_none = false;
1440 let success = unsafe {
1441 get_own_property_descriptor(
1442 cx.raw_cx(),
1443 proxy.into(),
1444 id.into(),
1445 property_descriptor.handle_mut().into(),
1446 &mut is_none,
1447 )
1448 };
1449 (success, !is_none)
1450 };
1451
1452 if !success {
1453 return false;
1454 }
1455 unsafe { *bp = found };
1456 true
1457}
1458
1459#[expect(unsafe_code)]
1461unsafe extern "C" fn get(
1462 cx: *mut RawJSContext,
1463 proxy: RawHandleObject,
1464 receiver: RawHandleValue,
1465 id: RawHandleId,
1466 return_value: RawMutableHandleValue,
1467) -> bool {
1468 let mut cx = unsafe {
1469 JSContext::from_ptr(NonNull::new(cx).expect("JSContext should not be null in SM hook"))
1471 };
1472 let mut cx = CurrentRealm::assert(&mut cx);
1473 let cx = &mut cx;
1474 let proxy = unsafe { Handle::from_raw(proxy) };
1475 let receiver = unsafe { Handle::from_raw(receiver) };
1476 let id = unsafe { Handle::from_raw(id) };
1477 let return_value = unsafe { MutableHandle::from_raw(return_value) };
1478
1479 if is_platform_object_same_origin(cx, proxy) {
1489 let window = unsafe { GetSubframeWindowProxy(cx, proxy.into(), id.into()) };
1490 if let Some((window, _attrs)) = window {
1491 window.to_jsval(cx, return_value);
1492 return true;
1493 }
1494
1495 rooted!(&in(cx) let target = window_proxy_target(proxy));
1496 return unsafe { JS_ForwardGetPropertyTo(cx, target.handle(), id, receiver, return_value) };
1497 }
1498
1499 proxyhandler::cross_origin_get::<DomTypeHolder>(cx, proxy, receiver, id, return_value)
1501}
1502
1503#[expect(unsafe_code)]
1505unsafe extern "C" fn set(
1506 cx: *mut RawJSContext,
1507 proxy: RawHandleObject,
1508 id: RawHandleId,
1509 v: RawHandleValue,
1510 receiver: RawHandleValue,
1511 res: *mut ObjectOpResult,
1512) -> bool {
1513 let mut cx = unsafe {
1514 JSContext::from_ptr(NonNull::new(cx).expect("JSContext should not be null in SM hook"))
1516 };
1517 let mut cx = CurrentRealm::assert(&mut cx);
1518 let cx = &mut cx;
1519
1520 let proxy = unsafe { Handle::from_raw(proxy) };
1529 let id = unsafe { Handle::from_raw(id) };
1530
1531 if is_platform_object_same_origin(cx, proxy) {
1533 if get_array_index_from_id(id).is_some() {
1535 unsafe { (*res).code_ = JSErrNum::JSMSG_READ_ONLY as usize };
1537 return true;
1538 }
1539
1540 rooted!(&in(cx) let target = window_proxy_target(proxy));
1542 return unsafe {
1543 JS_ForwardSetPropertyTo(
1544 cx.raw_cx(),
1545 target.handle().into(),
1546 id.into(),
1547 v,
1548 receiver,
1549 res,
1550 )
1551 };
1552 }
1553
1554 let receiver = unsafe { HandleValue::from_raw(receiver) };
1556 unsafe { cross_origin_set::<DomTypeHolder>(cx, proxy, id, v, receiver, res) }
1557}
1558
1559#[expect(unsafe_code)]
1560unsafe extern "C" fn get_prototype_if_ordinary(
1561 _: *mut RawJSContext,
1562 _: RawHandleObject,
1563 is_ordinary: *mut bool,
1564 _: RawMutableHandleObject,
1565) -> bool {
1566 unsafe { *is_ordinary = false };
1579 true
1580}
1581
1582#[expect(unsafe_code)]
1584unsafe extern "C" fn get_prototype(
1585 cx: *mut RawJSContext,
1586 proxy: RawHandleObject,
1587 result: RawMutableHandleObject,
1588) -> bool {
1589 let mut cx = unsafe { JSContext::from_ptr(ptr::NonNull::new(cx).unwrap()) };
1590 let mut realm = CurrentRealm::assert(&mut cx);
1591 let proxy = unsafe { Handle::from_raw(proxy) };
1592 let result = unsafe { MutableHandleObject::from_raw(result) };
1593 maybe_cross_origin_get_prototype::<DomTypeHolder>(
1594 &mut realm,
1595 proxy,
1596 GetProtoObject::<DomTypeHolder>,
1597 result,
1598 )
1599}
1600
1601#[expect(unsafe_code)]
1603unsafe extern "C" fn delete(
1604 cx: *mut RawJSContext,
1605 proxy: RawHandleObject,
1606 id: RawHandleId,
1607 result: *mut ObjectOpResult,
1608) -> bool {
1609 let mut cx = unsafe { JSContext::from_ptr(ptr::NonNull::new(cx).unwrap()) };
1610 let mut cx = CurrentRealm::assert(&mut cx);
1611 let cx = &mut cx;
1612 let proxy = unsafe { Handle::from_raw(proxy) };
1613 let id = unsafe { Handle::from_raw(id) };
1614
1615 if is_platform_object_same_origin(cx, proxy) {
1620 if get_array_index_from_id(id).is_some() {
1622 let window = unsafe { GetSubframeWindowProxy(cx, proxy.into(), id.into()) };
1624 let code = if window.is_none() {
1625 0 } else {
1628 JSErrNum::JSMSG_CANT_DELETE_WINDOW_ELEMENT as usize
1630 };
1631 unsafe { (*result).code_ = code };
1632 return true;
1633 }
1634 rooted!(&in(cx) let target = window_proxy_target(proxy));
1636 return unsafe {
1637 JS_DeletePropertyById(cx.raw_cx(), target.handle().into(), id.into(), result)
1638 };
1639 }
1640
1641 report_cross_origin_denial::<DomTypeHolder>(cx, id, "delete")
1643}
1644
1645#[expect(unsafe_code)]
1647unsafe extern "C" fn own_property_keys(
1648 cx: *mut RawJSContext,
1649 proxy: RawHandleObject,
1650 property_keys: RawMutableHandleIdVector,
1651) -> bool {
1652 let mut cx = unsafe { JSContext::from_ptr(ptr::NonNull::new(cx).unwrap()) };
1653 let mut cx = CurrentRealm::assert(&mut cx);
1654 let cx = &mut cx;
1655 let proxy = unsafe { Handle::from_raw(proxy) };
1656
1657 rooted!(&in(cx) let target = window_proxy_target(proxy));
1663 let (max_properties, cross_origin_properties) = if let Ok(window) =
1664 root_from_handleobject::<Window>(cx, target.handle())
1665 {
1666 (window.Length(), unsafe {
1667 WindowBinding::CROSS_ORIGIN_PROPERTIES.get()
1668 })
1669 } else if let Ok(window) = root_from_handleobject::<DissimilarOriginWindow>(cx, target.handle())
1670 {
1671 (window.Length(), unsafe {
1674 DissimilarOriginWindowBinding::CROSS_ORIGIN_PROPERTIES.get()
1675 })
1676 } else {
1677 unreachable!("WindowProxy should always be backed by some kind of window");
1678 };
1679
1680 rooted!(&in(cx) let mut rooted_index_jsid: jsid);
1682 for index in 0..max_properties {
1683 unsafe { int_to_jsid(index as i32, rooted_index_jsid.handle_mut()) };
1684 unsafe { AppendToIdVector(property_keys, rooted_index_jsid.handle()) };
1685 }
1686
1687 if is_platform_object_same_origin(cx, proxy) {
1688 return unsafe {
1691 GetPropertyKeys(
1692 cx,
1693 target.handle(),
1694 JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS,
1695 property_keys,
1696 )
1697 };
1698 }
1699
1700 cross_origin_own_property_keys(cx, proxy, cross_origin_properties, property_keys)
1702}
1703
1704fn named_child_navigable(
1725 cx: &mut CurrentRealm,
1726 proxy: HandleObject,
1727 id: HandleId,
1728) -> Option<DomRoot<WindowProxy>> {
1729 let name = jsid_to_string(cx, id)?;
1730 if name.is_empty() {
1731 return None;
1732 }
1733 rooted!(&in(cx) let target = window_proxy_target(proxy));
1734 let window = root_from_handleobject::<Window>(cx, target.handle()).ok()?;
1735 window
1736 .Document()
1737 .iframes()
1738 .iter()
1739 .filter_map(|iframe| iframe.GetContentWindow())
1740 .find(|window_proxy| window_proxy.get_name() == name)
1741}
1742
1743static PROXY_TRAPS: ProxyTraps = ProxyTraps {
1744 enter: None,
1745 getOwnPropertyDescriptor: Some(get_own_property_descriptor),
1746 defineProperty: Some(define_property),
1747 ownPropertyKeys: Some(own_property_keys),
1748 delete_: Some(delete),
1749 enumerate: None,
1750 getPrototypeIfOrdinary: Some(get_prototype_if_ordinary),
1751 getPrototype: Some(get_prototype),
1752 setPrototype: Some(maybe_cross_origin_set_prototype_rawcx),
1753 setImmutablePrototype: None,
1754 preventExtensions: Some(prevent_extensions),
1755 isExtensible: Some(is_extensible),
1756 has: Some(has),
1757 get: Some(get),
1758 set: Some(set),
1759 call: None,
1760 construct: None,
1761 hasOwn: Some(has_own),
1762 getOwnEnumerablePropertyKeys: None,
1763 nativeCall: None,
1764 objectClassIs: None,
1765 className: None,
1766 fun_toString: None,
1767 boxedValue_unbox: None,
1768 defaultValue: None,
1769 trace: Some(trace),
1770 finalize: Some(finalize),
1771 objectMoved: None,
1772 isCallable: None,
1773 isConstructor: None,
1774};
1775
1776pub(crate) struct WindowProxyHandler(*const libc::c_void);
1779
1780impl MallocSizeOf for WindowProxyHandler {
1781 fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
1782 0
1784 }
1785}
1786
1787#[expect(unsafe_code)]
1789unsafe impl Send for WindowProxyHandler {}
1790#[expect(unsafe_code)]
1792unsafe impl Sync for WindowProxyHandler {}
1793
1794#[expect(unsafe_code)]
1795impl WindowProxyHandler {
1796 fn new(traps: &ProxyTraps) -> Self {
1797 let ptr = unsafe { CreateWrapperProxyHandler(traps) };
1799 assert!(!ptr.is_null());
1800 Self(ptr)
1801 }
1802
1803 pub(crate) fn proxy_handler() -> &'static Self {
1805 use std::sync::OnceLock;
1806 static SINGLETON: OnceLock<WindowProxyHandler> = OnceLock::new();
1812 SINGLETON.get_or_init(|| Self::new(&PROXY_TRAPS))
1813 }
1814
1815 fn new_window_proxy(
1818 &self,
1819 cx: &mut JSContext,
1820 window_jsobject: js::gc::HandleObject,
1821 ) -> *mut JSObject {
1822 let obj = unsafe { NewWindowProxy(cx, window_jsobject, self.0) };
1823 assert!(!obj.is_null());
1824 obj
1825 }
1826}
1827
1828#[expect(unsafe_code)]
1829impl Drop for WindowProxyHandler {
1830 fn drop(&mut self) {
1831 unsafe {
1834 DeleteWrapperProxyHandler(self.0);
1835 }
1836 }
1837}
1838
1839#[expect(unsafe_code)]
1842unsafe extern "C" fn finalize(_fop: *mut GCContext, obj: *mut JSObject) {
1843 let mut slot = UndefinedValue();
1844 unsafe { GetProxyReservedSlot(obj, 0, &mut slot) };
1845 let this = slot.to_private() as *mut WindowProxy;
1846 if this.is_null() {
1847 return;
1849 }
1850 unsafe {
1851 (*this).reflector.drop_memory(&*this);
1852 let jsobject = (*this).reflector.get_jsobject().get();
1853 debug!(
1854 "WindowProxy finalize: {:p}, with reflector {:p} from {:p}.",
1855 this, jsobject, obj
1856 );
1857 let _ = Box::from_raw(this);
1858 }
1859}
1860
1861#[expect(unsafe_code)]
1862unsafe extern "C" fn trace(trc: *mut JSTracer, obj: *mut JSObject) {
1863 let mut slot = UndefinedValue();
1864 unsafe { GetProxyReservedSlot(obj, 0, &mut slot) };
1865 let this = slot.to_private() as *const WindowProxy;
1866 if this.is_null() {
1867 return;
1869 }
1870 unsafe { (*this).trace(trc) };
1871}