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_PROPERTY_HOLDER_WEAK_MAP_SLOT, CrossOriginProperties,
53 cross_origin_get_own_property_helper, cross_origin_own_property_keys,
54 cross_origin_property_fallback, cross_origin_set, is_extensible,
55 is_platform_object_same_origin, maybe_cross_origin_get_prototype,
56 maybe_cross_origin_set_prototype_rawcx, prevent_extensions, report_cross_origin_denial,
57 set_property_descriptor,
58};
59use script_bindings::reflector::{DomObject, MutDomObject, Reflector};
60use script_traits::{NewPipelineInfo, WebViewState};
61use serde::{Deserialize, Serialize};
62use servo_base::generic_channel;
63use servo_base::generic_channel::GenericSend;
64use servo_base::id::{BrowsingContextId, PipelineId, WebViewId};
65use servo_constellation_traits::{
66 AuxiliaryWebViewCreationRequest, LoadData, LoadOrigin, NavigationHistoryBehavior,
67 ScriptToConstellationMessage, TargetSnapshotParams,
68};
69use servo_url::{ImmutableOrigin, OriginSnapshot, ServoUrl};
70use storage_traits::webstorage_thread::WebStorageThreadMsg;
71use style::attr::parse_integer;
72
73use crate::DomTypeHolder;
74use crate::dom::bindings::conversions::{ToJSValConvertible, root_from_handleobject};
75use crate::dom::bindings::error::{Error, Fallible};
76use crate::dom::bindings::inheritance::Castable;
77use crate::dom::bindings::reflector::DomGlobal;
78use crate::dom::bindings::root::{Dom, DomRoot};
79use crate::dom::bindings::settings_stack::maybe_entry_global;
80use crate::dom::bindings::str::{DOMString, USVString};
81use crate::dom::bindings::trace::JSTraceable;
82use crate::dom::bindings::utils::get_array_index_from_id;
83use crate::dom::dissimilaroriginwindow::DissimilarOriginWindow;
84use crate::dom::document::Document;
85use crate::dom::element::Element;
86use crate::dom::globalscope::GlobalScope;
87use crate::dom::node::node::NodeTraits;
88use crate::dom::window::Window;
89use crate::event_loop::script_thread::{ScriptThread, with_script_thread};
90use crate::event_loop::script_window_proxies::ScriptWindowProxies;
91use crate::navigation::navigate;
92
93#[dom_struct]
94pub(crate) struct WindowProxy {
99 reflector: Reflector,
104
105 #[no_trace]
109 browsing_context_id: BrowsingContextId,
110
111 #[no_trace]
113 opener: Option<BrowsingContextId>,
114
115 #[no_trace]
118 webview_id: WebViewId,
119
120 name: DomRefCell<DOMString>,
123 #[no_trace]
129 currently_active: Cell<Option<PipelineId>>,
130
131 discarded: Cell<bool>,
133
134 disowned: Cell<bool>,
136
137 is_closing: Cell<bool>,
139
140 frame_element: Option<Dom<Element>>,
144
145 parent: Option<Dom<WindowProxy>>,
147
148 delaying_load_events_mode: Cell<bool>,
150
151 #[no_trace]
153 creator_url: Option<ServoUrl>,
154
155 #[no_trace]
157 creator_origin: Option<ImmutableOrigin>,
158
159 #[conditional_malloc_size_of]
161 script_window_proxies: Rc<ScriptWindowProxies>,
162}
163
164impl WindowProxy {
165 fn new_inherited(
166 browsing_context_id: BrowsingContextId,
167 webview_id: WebViewId,
168 currently_active: Option<PipelineId>,
169 frame_element: Option<&Element>,
170 parent: Option<&WindowProxy>,
171 opener: Option<BrowsingContextId>,
172 creator: CreatorBrowsingContextInfo,
173 ) -> WindowProxy {
174 let name = frame_element.map_or(DOMString::new(), |e| {
175 e.get_string_attribute(&local_name!("name"))
176 });
177 WindowProxy {
178 reflector: Reflector::new(),
179 browsing_context_id,
180 webview_id,
181 name: DomRefCell::new(name),
182 currently_active: Cell::new(currently_active),
183 discarded: Cell::new(false),
184 disowned: Cell::new(false),
185 is_closing: Cell::new(false),
186 frame_element: frame_element.map(Dom::from_ref),
187 parent: parent.map(Dom::from_ref),
188 delaying_load_events_mode: Cell::new(false),
189 opener,
190 creator_url: creator.url,
191 creator_origin: creator.origin,
192 script_window_proxies: ScriptThread::window_proxies(),
193 }
194 }
195
196 #[expect(unsafe_code)]
197 #[expect(clippy::too_many_arguments)]
198 pub(crate) fn new(
199 cx: &mut JSContext,
200 window: &Window,
201 browsing_context_id: BrowsingContextId,
202 webview_id: WebViewId,
203 frame_element: Option<&Element>,
204 parent: Option<&WindowProxy>,
205 opener: Option<BrowsingContextId>,
206 creator: CreatorBrowsingContextInfo,
207 ) -> DomRoot<WindowProxy> {
208 unsafe {
209 let handler = window.windowproxy_handler();
210
211 let window_jsobject = window.reflector().get_jsobject();
212 assert!(!window_jsobject.get().is_null());
213 assert_ne!(
214 ((*get_object_class(window_jsobject.get())).flags & JSCLASS_IS_GLOBAL),
215 0
216 );
217
218 let mut realm = AutoRealm::new_from_handle(cx, window_jsobject);
219 let cx = &mut realm;
220
221 rooted!(&in(cx) let js_proxy = handler.new_window_proxy(cx, window_jsobject));
223 assert!(!js_proxy.is_null());
224
225 let current = Some(window.upcast::<GlobalScope>().pipeline_id());
228 let window_proxy = Box::new(WindowProxy::new_inherited(
229 browsing_context_id,
230 webview_id,
231 current,
232 frame_element,
233 parent,
234 opener,
235 creator,
236 ));
237
238 SetProxyReservedSlot(
241 js_proxy.get(),
242 0,
243 &PrivateValue(&raw const (*window_proxy) as *const libc::c_void),
244 );
245
246 SetWindowProxy(cx, window_jsobject, js_proxy.handle());
248
249 debug!(
251 "Initializing reflector of {:p} to {:p}.",
252 window_proxy,
253 js_proxy.get()
254 );
255 window_proxy
256 .reflector
257 .init_reflector::<WindowProxy>(js_proxy.get());
258 DomRoot::from_ref(&*Box::into_raw(window_proxy))
259 }
260 }
261
262 #[expect(unsafe_code)]
263 pub(crate) fn new_dissimilar_origin(
264 cx: &mut JSContext,
265 global_to_clone_from: &GlobalScope,
266 browsing_context_id: BrowsingContextId,
267 webview_id: WebViewId,
268 parent: Option<&WindowProxy>,
269 opener: Option<BrowsingContextId>,
270 creator: CreatorBrowsingContextInfo,
271 ) -> DomRoot<WindowProxy> {
272 unsafe {
273 let window_proxy = Box::new(WindowProxy::new_inherited(
275 browsing_context_id,
276 webview_id,
277 None,
278 None,
279 parent,
280 opener,
281 creator,
282 ));
283
284 let window = DissimilarOriginWindow::new(cx, global_to_clone_from, &window_proxy);
286 let window_jsobject = window.reflector().get_jsobject();
287 assert!(!window_jsobject.get().is_null());
288 assert_ne!(
289 ((*get_object_class(window_jsobject.get())).flags & JSCLASS_IS_GLOBAL),
290 0
291 );
292
293 let mut realm = AutoRealm::new_from_handle(cx, window_jsobject);
294 let cx = &mut realm;
295
296 let handler = WindowProxyHandler::proxy_handler();
298 rooted!(&in(cx) let js_proxy = handler.new_window_proxy(cx, window_jsobject));
299 assert!(!js_proxy.is_null());
300
301 SetProxyReservedSlot(
304 js_proxy.get(),
305 0,
306 &PrivateValue(&raw const (*window_proxy) as *const libc::c_void),
307 );
308
309 SetWindowProxy(cx, window_jsobject, js_proxy.handle());
311
312 debug!(
314 "Initializing reflector of {:p} to {:p}.",
315 window_proxy,
316 js_proxy.get()
317 );
318 window_proxy
319 .reflector
320 .init_reflector::<WindowProxy>(js_proxy.get());
321 DomRoot::from_ref(&*Box::into_raw(window_proxy))
322 }
323 }
324
325 fn create_auxiliary_browsing_context(
327 &self,
328 cx: &mut JSContext,
329 name: DOMString,
330 noopener: bool,
331 ) -> Option<DomRoot<WindowProxy>> {
332 let (response_sender, response_receiver) = generic_channel::channel().unwrap();
333 let window = self
334 .currently_active
335 .get()
336 .and_then(ScriptThread::find_document)
337 .map(|doc| DomRoot::from_ref(doc.window()))
338 .unwrap();
339
340 let document = self
341 .currently_active
342 .get()
343 .and_then(ScriptThread::find_document)
344 .expect("A WindowProxy creating an auxiliary to have an active document");
345
346 let sandboxing_flag_set = document.active_sandboxing_flag_set();
354 let propagate_sandbox = sandboxing_flag_set
355 .contains(SandboxingFlagSet::SANDBOX_PROPOGATES_TO_AUXILIARY_BROWSING_CONTEXTS_FLAG);
356 let sandboxing_flag_set = if propagate_sandbox {
357 sandboxing_flag_set
358 } else {
359 SandboxingFlagSet::empty()
360 };
361
362 let blank_url = ServoUrl::parse("about:blank").ok().unwrap();
363 let mut load_data = LoadData::new(
364 LoadOrigin::Script(document.origin().snapshot()),
365 blank_url,
366 Some(document.base_url()),
367 Some(window.pipeline_id()),
370 document.global().get_referrer(),
371 document.get_referrer_policy(),
372 None, None,
374 false,
375 sandboxing_flag_set,
376 );
377 load_data.is_initial_about_blank = true;
378 let load_info = AuxiliaryWebViewCreationRequest {
379 load_data: load_data.clone(),
380 opener_webview_id: window.webview_id(),
381 opener_pipeline_id: self.currently_active.get().unwrap(),
382 response_sender,
383 };
384 let constellation_msg = ScriptToConstellationMessage::CreateAuxiliaryWebView(load_info);
385 window.send_to_constellation(constellation_msg);
386
387 let response = response_receiver.recv().unwrap()?;
388 let new_browsing_context_id = BrowsingContextId::from(response.new_webview_id);
389 let new_pipeline_info = NewPipelineInfo {
390 webview_state: WebViewState {
391 id: response.new_webview_id,
392 theme: Cell::new(window.webview_theme()),
395 has_system_focus: Cell::new(true),
397 },
398 parent_info: None,
399 new_pipeline_id: response.new_pipeline_id,
400 browsing_context_id: new_browsing_context_id,
401 opener: Some(self.browsing_context_id),
402 load_data,
403 viewport_details: window.viewport_details(),
404 user_content_manager_id: response.user_content_manager_id,
405 target_snapshot_params: TargetSnapshotParams {
406 sandboxing_flags: sandboxing_flag_set,
407 iframe_element_referrer_policy: ReferrerPolicy::EmptyString,
408 },
409 frame_name: None,
410 };
411
412 with_script_thread(|script_thread| {
413 script_thread.spawn_pipeline(cx, new_pipeline_info);
414 });
415
416 let new_window_proxy = ScriptThread::find_document(response.new_pipeline_id)
417 .and_then(|doc| doc.browsing_context())?;
418 if !name.eq_ignore_ascii_case("_blank") {
419 new_window_proxy.set_name(name);
420 }
421 if noopener {
422 new_window_proxy.disown();
423 } else {
424 let (sender, receiver) = generic_channel::channel().unwrap();
429
430 let msg = WebStorageThreadMsg::Clone {
431 sender,
432 src: window.window_proxy().webview_id(),
433 dest: response.new_webview_id,
434 };
435
436 GenericSend::send(document.global().storage_threads(), msg).unwrap();
437 receiver.recv().unwrap();
438 }
439 Some(new_window_proxy)
440 }
441
442 pub(crate) fn start_delaying_load_events_mode(&self) {
444 self.delaying_load_events_mode.set(true);
445 }
446
447 pub(crate) fn stop_delaying_load_events_mode(&self) {
449 self.delaying_load_events_mode.set(false);
450 }
451
452 pub(crate) fn disown(&self) {
454 self.disowned.set(true);
455 }
456
457 pub(crate) fn close(&self) {
460 self.is_closing.set(true);
461 }
462
463 pub(crate) fn is_closing(&self) -> bool {
465 self.is_closing.get()
466 }
467
468 pub(crate) fn opener(&self, cx: &mut CurrentRealm, mut retval: MutableHandleValue) {
470 if self.disowned.get() {
471 return retval.set(NullValue());
472 }
473 let opener_id = match self.opener {
474 Some(opener_browsing_context_id) => opener_browsing_context_id,
475 None => return retval.set(NullValue()),
476 };
477 let parent_browsing_context = self.parent.as_deref();
478 let opener_proxy = match self.script_window_proxies.find_window_proxy(opener_id) {
479 Some(window_proxy) => window_proxy,
480 None => {
481 let sender_pipeline_id = self.currently_active().unwrap();
482 match ScriptThread::get_top_level_for_browsing_context(
483 self.webview_id(),
484 sender_pipeline_id,
485 opener_id,
486 ) {
487 Some(opener_top_id) => {
488 let global_to_clone_from = GlobalScope::from_current_realm(cx);
489 let creator =
490 CreatorBrowsingContextInfo::from(parent_browsing_context, None);
491 WindowProxy::new_dissimilar_origin(
492 cx,
493 &global_to_clone_from,
494 opener_id,
495 opener_top_id,
496 None,
497 None,
498 creator,
499 )
500 },
501 None => return retval.set(NullValue()),
502 }
503 },
504 };
505 if opener_proxy.is_browsing_context_discarded() {
506 return retval.set(NullValue());
507 }
508 opener_proxy.to_jsval(cx, retval);
509 }
510
511 pub(crate) fn open(
513 &self,
514 cx: &mut JSContext,
515 url: USVString,
516 target: DOMString,
517 features: DOMString,
518 ) -> Fallible<Option<DomRoot<WindowProxy>>> {
519 if self.discarded.get() {
525 return Ok(None);
526 }
527 let source_document = maybe_entry_global()
533 .map(|global| global.as_window().Document())
534 .or_else(|| self.document())
535 .expect("Must have an entry global or active document");
536 let url_record = if !url.is_empty() {
538 let Ok(url) = source_document.encoding_parse_a_url(&url) else {
540 return Err(Error::Syntax(Some(format!(
542 "Error parsing URL '{url}' relative to '{}'",
543 source_document.url()
544 ))));
545 };
546 Some(url)
547 } else {
548 None
550 };
551 let non_empty_target = if target.is_empty() {
553 DOMString::from_static("_blank")
554 } else {
555 target
556 };
557 let tokenized_features = tokenize_open_features(features);
559 let noreferrer = parse_open_feature_boolean(&tokenized_features, "noreferrer");
563
564 let noopener = if noreferrer {
567 true
568 } else {
569 parse_open_feature_boolean(&tokenized_features, "noopener")
570 };
571 let (chosen, new) = match self.choose_a_navigable(cx, non_empty_target, noopener) {
581 (Some(chosen), new) => (chosen, new),
582 (None, _) => return Ok(None),
583 };
584 let target_document = match chosen.document() {
587 Some(target_document) => target_document,
588 None => return Ok(None),
589 };
590 let has_trustworthy_ancestor_origin = if new {
591 target_document.has_trustworthy_ancestor_or_current_origin()
592 } else {
593 false
594 };
595 let target_window = target_document.window();
596 let url_record = url_record.unwrap_or(ServoUrl::parse("about:blank").unwrap());
598 if !url_record.matches_about_blank() {
602 let referrer = if noreferrer {
603 Referrer::NoReferrer
604 } else {
605 target_window.as_global_scope().get_referrer()
606 };
607 let csp_list = source_document.get_csp_list().clone();
609 target_document.set_csp_list(csp_list);
610
611 let mut load_data = LoadData::new(
615 LoadOrigin::Script(source_document.origin().snapshot()),
616 url_record,
617 target_document.about_base_url(),
618 Some(target_window.pipeline_id()),
619 referrer,
620 target_document.get_referrer_policy(),
621 Some(target_window.as_global_scope().is_secure_context()),
622 Some(target_document.insecure_requests_policy()),
623 has_trustworthy_ancestor_origin,
624 target_document.creation_sandboxing_flag_set_considering_parent_iframe(),
625 );
626
627 if load_data.url.scheme() == "javascript" {
630 let existing_global = source_document.global();
631
632 if !ScriptThread::can_navigate_to_javascript_url(
634 cx,
635 &existing_global,
636 target_window.as_global_scope(),
637 &mut load_data,
638 None,
639 ) {
640 return Ok(target_document.browsing_context());
642 }
643 }
644
645 let history_handling = if new {
646 NavigationHistoryBehavior::Replace
647 } else {
648 NavigationHistoryBehavior::Push
649 };
650 navigate(cx, target_window, history_handling, false, load_data);
651 }
652 if noopener {
654 return Ok(None);
655 }
656 Ok(target_document.browsing_context())
658 }
659
660 pub(crate) fn choose_a_navigable(
662 &self,
663 cx: &mut JSContext,
664 name: DOMString,
665 noopener: bool,
666 ) -> (Option<DomRoot<WindowProxy>>, bool) {
667 let sandboxing_flag_set = self
673 .document()
674 .map(|document| document.active_sandboxing_flag_set())
675 .unwrap_or_default();
676
677 let chosen = if name.is_empty() || name.eq_ignore_ascii_case("_self") {
678 Some((Some(DomRoot::from_ref(self)), false))
681 } else if name.eq_ignore_ascii_case("_parent") {
682 Some(
686 self.parent()
687 .map(|parent| (Some(DomRoot::from_ref(parent)), false))
688 .unwrap_or_else(|| (Some(DomRoot::from_ref(self)), false)),
689 )
690 } else if name.eq_ignore_ascii_case("_top") {
691 Some((Some(DomRoot::from_ref(self.top())), false))
694 } else if !name.eq_ignore_ascii_case("_blank") {
695 self.find_navigable_by_target_name(&name)
703 .map(|proxy| (Some(proxy), false))
704 } else {
705 None
706 };
707
708 if let Some(chosen) = chosen {
709 return chosen;
710 }
711
712 if sandboxing_flag_set
726 .contains(SandboxingFlagSet::SANDBOXED_AUXILIARY_NAVIGATION_BROWSING_CONTEXT_FLAG)
727 {
728 (None, false)
729 }
730 else {
734 (
735 self.create_auxiliary_browsing_context(cx, name, noopener),
736 true,
737 )
738 }
739 }
740
741 fn find_navigable_by_target_name(&self, name: &DOMString) -> Option<DomRoot<WindowProxy>> {
743 let top = self.top();
763 let subtrees_to_search = if top != self {
764 Either::Left([self, top])
765 } else {
766 Either::Right([self])
767 };
768
769 for subtree_to_search in subtrees_to_search.into_iter() {
771 if let Some(result) =
775 subtree_to_search.find_navigable_by_target_name_in_descendants(name)
776 {
777 return Some(result);
778 }
779 }
780
781 let mut top_level_window_proxies = self.script_window_proxies.top_level_window_proxies();
788
789 top_level_window_proxies
797 .sort_by_key(|proxy| std::cmp::Reverse(proxy.browsing_context_id()));
798
799 for window_proxy in top_level_window_proxies {
800 if &*window_proxy == top {
803 continue;
804 }
805 if let Some(result) = window_proxy.find_navigable_by_target_name_in_descendants(name) {
823 return Some(result);
824 }
825 }
826
827 None
829 }
830
831 fn find_navigable_by_target_name_in_descendants(
833 &self,
834 name: &DOMString,
835 ) -> Option<DomRoot<WindowProxy>> {
836 if self.is_browsing_context_discarded() {
838 return None;
839 }
840
841 if self.get_name() == *name {
847 return Some(DomRoot::from_ref(self));
848 }
849
850 let document = self.document()?;
851 let iframes: Vec<_> = document.iframes().iter().collect();
852 iframes.iter().find_map(|iframe| {
853 iframe
854 .browsing_context_id()
855 .and_then(|browsing_context_id| {
856 self.script_window_proxies
857 .find_window_proxy(browsing_context_id)?
858 .find_navigable_by_target_name_in_descendants(name)
859 })
860 })
861 }
862
863 pub(crate) fn is_auxiliary(&self) -> bool {
864 self.opener.is_some()
865 }
866
867 pub(crate) fn discard_browsing_context(&self) {
868 self.discarded.set(true);
869 }
870
871 pub(crate) fn is_browsing_context_discarded(&self) -> bool {
872 self.discarded.get()
873 }
874
875 pub(crate) fn browsing_context_id(&self) -> BrowsingContextId {
876 self.browsing_context_id
877 }
878
879 pub(crate) fn webview_id(&self) -> WebViewId {
880 self.webview_id
881 }
882
883 pub(crate) fn frame_element(&self) -> Option<&Element> {
887 self.frame_element.as_deref()
888 }
889
890 pub(crate) fn document(&self) -> Option<DomRoot<Document>> {
891 self.currently_active
892 .get()
893 .and_then(ScriptThread::find_document)
894 }
895
896 pub(crate) fn parent(&self) -> Option<&WindowProxy> {
897 self.parent.as_deref()
898 }
899
900 pub(crate) fn top(&self) -> &WindowProxy {
901 let mut result = self;
902 while let Some(parent) = result.parent() {
903 result = parent;
904 }
905 result
906 }
907
908 pub(crate) fn document_origin_and_internal_ancestor_origin_objects_list(
909 &self,
910 ) -> Option<(OriginSnapshot, Vec<ImmutableOrigin>)> {
911 let pipeline_id = self.currently_active()?;
912 let (result_sender, result_receiver) = generic_channel::channel().unwrap();
913 self.global()
914 .script_to_constellation_chan()
915 .send(ScriptToConstellationMessage::GetDocumentOriginDetails(
916 pipeline_id,
917 result_sender,
918 ))
919 .ok()?;
920 result_receiver.recv().ok()?
921 }
922
923 pub(crate) fn parent_origin_and_internal_ancestor_origin_objects_list(
925 &self,
926 ) -> Option<(OriginSnapshot, Vec<ImmutableOrigin>)> {
927 if let Some(frame_element) = self.frame_element() {
928 let parent_document = frame_element.owner_document();
929 if !parent_document.is_fully_active() {
932 return None;
933 }
934 Some((
935 parent_document.origin().snapshot(),
936 parent_document
937 .internal_ancestor_origin_objects_list()
938 .clone()
939 .expect("Must always be fully active"),
940 ))
941 } else if let Some(parent_proxy) = self.parent() {
942 assert!(parent_proxy.currently_active().is_some());
944 parent_proxy.document_origin_and_internal_ancestor_origin_objects_list()
945 } else {
946 None
947 }
948 }
949
950 #[expect(unsafe_code)]
951 fn set_window(&self, cx: &mut JSContext, window: &GlobalScope) {
955 unsafe {
956 debug!("Setting window of {:p}.", self);
957
958 let window_jsobject = window.reflector().get_jsobject();
959 let old_js_proxy = self.reflector.get_jsobject();
960 assert!(!window_jsobject.get().is_null());
961 assert_ne!(
962 ((*get_object_class(window_jsobject.get())).flags & JSCLASS_IS_GLOBAL),
963 0
964 );
965
966 let mut realm = AutoRealm::new_from_handle(cx, window_jsobject);
967 let cx = &mut realm;
968
969 SetProxyReservedSlot(old_js_proxy.get(), 0, &PrivateValue(ptr::null_mut()));
971 SetProxyReservedSlot(
973 old_js_proxy.get(),
974 CROSS_ORIGIN_PROPERTY_HOLDER_WEAK_MAP_SLOT,
975 &UndefinedValue(),
976 );
977
978 rooted!(&in(cx) let new_js_proxy = WindowProxyHandler::proxy_handler().new_window_proxy(cx, window_jsobject));
983 SetProxyReservedSlot(new_js_proxy.get(), 0, &PrivateValue(ptr::null_mut()));
986 debug!(
987 "Transplanting proxy from {:p} to {:p}.",
988 old_js_proxy.get(),
989 new_js_proxy.get()
990 );
991 rooted!(&in(cx) let new_js_proxy = JS_TransplantObject(cx, old_js_proxy, new_js_proxy.handle()));
992 debug!("Transplanted proxy is {:p}.", new_js_proxy.get());
993
994 SetProxyReservedSlot(
996 new_js_proxy.get(),
997 0,
998 &PrivateValue(self as *const _ as *const libc::c_void),
999 );
1000
1001 SetWindowProxy(cx, window_jsobject, new_js_proxy.handle());
1003
1004 debug!(
1006 "Setting reflector of {:p} to {:p}.",
1007 self,
1008 new_js_proxy.get()
1009 );
1010 self.reflector.rootable().set(new_js_proxy.get());
1011 }
1012 }
1013
1014 pub(crate) fn set_pipeline_id(&self, pipeline_id: PipelineId) {
1015 self.currently_active.set(Some(pipeline_id));
1016 }
1017
1018 pub(crate) fn set_currently_active(&self, cx: &mut JSContext, window: &Window) {
1019 if let Some(pipeline_id) = self.currently_active() &&
1020 pipeline_id == window.pipeline_id()
1021 {
1022 return debug!(
1023 "Attempt to set the currently active window to the currently active window."
1024 );
1025 }
1026
1027 let global_scope = window.as_global_scope();
1028 self.set_window(cx, global_scope);
1029 self.currently_active.set(Some(global_scope.pipeline_id()));
1030 }
1031
1032 pub(crate) fn unset_currently_active(&self, cx: &mut JSContext) {
1033 if self.currently_active().is_none() {
1034 return debug!(
1035 "Attempt to unset the currently active window on a windowproxy that does not have one."
1036 );
1037 }
1038 let globalscope = self.global();
1039 let window = DissimilarOriginWindow::new(cx, &globalscope, self);
1040 self.set_window(cx, window.upcast());
1041 self.currently_active.set(None);
1042 }
1043
1044 pub(crate) fn currently_active(&self) -> Option<PipelineId> {
1045 self.currently_active.get()
1046 }
1047
1048 pub(crate) fn get_name(&self) -> DOMString {
1049 self.name.borrow().clone()
1050 }
1051
1052 pub(crate) fn set_name(&self, name: DOMString) {
1053 *self.name.borrow_mut() = name;
1054 }
1055}
1056
1057#[derive(Debug, Deserialize, Serialize)]
1069pub(crate) struct CreatorBrowsingContextInfo {
1070 url: Option<ServoUrl>,
1072
1073 origin: Option<ImmutableOrigin>,
1075}
1076
1077impl CreatorBrowsingContextInfo {
1078 pub(crate) fn from(
1079 parent: Option<&WindowProxy>,
1080 opener: Option<&WindowProxy>,
1081 ) -> CreatorBrowsingContextInfo {
1082 let creator = match (parent, opener) {
1083 (Some(parent), _) => parent.document(),
1084 (None, Some(opener)) => opener.document(),
1085 (None, None) => None,
1086 };
1087
1088 let url = creator.as_deref().map(|document| document.url());
1089 let origin = creator
1090 .as_deref()
1091 .map(|document| document.origin().immutable().clone());
1092
1093 CreatorBrowsingContextInfo { url, origin }
1094 }
1095}
1096
1097fn tokenize_open_features(features: DOMString) -> IndexMap<String, String> {
1099 let is_feature_sep = |c: char| c.is_ascii_whitespace() || ['=', ','].contains(&c);
1100 let mut tokenized_features = IndexMap::new();
1102 let features = features.str();
1104 let mut iter = features.chars();
1105 let mut cur = iter.next();
1106
1107 while cur.is_some() {
1109 let mut name = String::new();
1111 let mut value = String::new();
1112 while let Some(cur_char) = cur {
1114 if !is_feature_sep(cur_char) {
1115 break;
1116 }
1117 cur = iter.next();
1118 }
1119 while let Some(cur_char) = cur {
1121 if is_feature_sep(cur_char) {
1122 break;
1123 }
1124 name.push(cur_char.to_ascii_lowercase());
1125 cur = iter.next();
1126 }
1127 let normalized_name = String::from(match name.as_ref() {
1129 "screenx" => "left",
1130 "screeny" => "top",
1131 "innerwidth" => "width",
1132 "innerheight" => "height",
1133 _ => name.as_ref(),
1134 });
1135 while let Some(cur_char) = cur {
1137 if cur_char == '=' || cur_char == ',' || !is_feature_sep(cur_char) {
1138 break;
1139 }
1140 cur = iter.next();
1141 }
1142 if cur.is_some() && is_feature_sep(cur.unwrap()) {
1144 while let Some(cur_char) = cur {
1146 if !is_feature_sep(cur_char) || cur_char == ',' {
1147 break;
1148 }
1149 cur = iter.next();
1150 }
1151 while let Some(cur_char) = cur {
1153 if is_feature_sep(cur_char) {
1154 break;
1155 }
1156 value.push(cur_char.to_ascii_lowercase());
1157 cur = iter.next();
1158 }
1159 }
1160 if !name.is_empty() {
1162 tokenized_features.insert(normalized_name, value);
1163 }
1164 }
1165 tokenized_features
1167}
1168
1169fn parse_open_feature_boolean(tokenized_features: &IndexMap<String, String>, name: &str) -> bool {
1171 if let Some(value) = tokenized_features.get(name) {
1172 if value.is_empty() || value == "yes" {
1174 return true;
1175 }
1176 if let Ok(int) = parse_integer(value.chars()) {
1178 return int != 0;
1179 }
1180 }
1181 false
1183}
1184
1185#[expect(unsafe_code)]
1186fn window_proxy_target(proxy: HandleObject) -> *mut JSObject {
1187 let mut slot = UndefinedValue();
1188 unsafe { GetProxyPrivate(proxy.as_raw(), &mut slot) };
1189 slot.to_object()
1190}
1191
1192#[expect(unsafe_code)]
1194unsafe extern "C" fn get_own_property_descriptor(
1195 cx: *mut RawJSContext,
1196 proxy: RawHandleObject,
1197 id: RawHandleId,
1198 property_descriptor: RawMutableHandle<PropertyDescriptor>,
1199 is_none: *mut bool,
1200) -> bool {
1201 let mut cx = unsafe {
1202 JSContext::from_ptr(NonNull::new(cx).expect("JSContext should not be null in SM hook"))
1205 };
1206 let mut cx = CurrentRealm::assert(&mut cx);
1207 let cx = &mut cx;
1208 let proxy = unsafe { Handle::from_raw(proxy) };
1209 let id = unsafe { Handle::from_raw(id) };
1210 let mut property_descriptor = unsafe { MutableHandle::from_raw(property_descriptor) };
1211 let is_none = unsafe { &mut *is_none };
1212
1213 rooted!(&in(cx) let target = window_proxy_target(proxy));
1215 let window = WindowOrDissimilarOriginWindow::new(cx, target.handle());
1216
1217 if let Some(index) = get_array_index_from_id(id) {
1220 if let Some(window_proxy) = window.window_proxy_for_child_navigable_at_index(index) {
1224 rooted!(&in(cx) let mut window_proxy_jsval = UndefinedValue());
1231 window_proxy.to_jsval(cx, window_proxy_jsval.handle_mut());
1232
1233 set_property_descriptor(
1236 property_descriptor,
1237 window_proxy_jsval.handle(),
1238 (JSPROP_ENUMERATE | JSPROP_READONLY) as u32,
1239 is_none,
1240 );
1241 return true;
1242 } else {
1243 *is_none = true;
1245 if is_platform_object_same_origin(cx, proxy) {
1248 return true;
1249 }
1250 return report_cross_origin_denial::<DomTypeHolder>(cx, id, "get");
1252 }
1253 }
1254
1255 if is_platform_object_same_origin(cx, proxy) {
1257 return unsafe {
1258 JS_GetOwnPropertyDescriptorById(cx, target.handle(), id, property_descriptor, is_none)
1259 };
1260 }
1261
1262 if !cross_origin_get_own_property_helper(
1264 cx,
1265 proxy,
1266 window.cross_origin_properties(),
1267 id,
1268 property_descriptor.reborrow(),
1269 is_none,
1270 ) {
1271 return false;
1272 }
1273
1274 if !*is_none {
1276 return true;
1277 }
1278
1279 if let Some(named_child_navigable) = window.named_child_navigable(cx, id) {
1282 rooted!(&in(cx) let mut window_proxy_value = UndefinedValue());
1284 named_child_navigable.to_jsval(cx, window_proxy_value.handle_mut());
1285 set_property_descriptor(
1288 property_descriptor.reborrow(),
1289 window_proxy_value.handle(),
1290 JSPROP_READONLY as u32,
1291 is_none,
1292 );
1293 return true;
1294 }
1295
1296 cross_origin_property_fallback::<DomTypeHolder>(
1298 cx,
1299 proxy,
1300 id,
1301 property_descriptor.reborrow(),
1302 is_none,
1303 )
1304}
1305
1306#[expect(unsafe_code)]
1308unsafe extern "C" fn define_property(
1309 cx: *mut RawJSContext,
1310 proxy: RawHandleObject,
1311 id: RawHandleId,
1312 desc: RawHandle<PropertyDescriptor>,
1313 res: *mut ObjectOpResult,
1314) -> bool {
1315 let mut cx = unsafe {
1316 JSContext::from_ptr(NonNull::new(cx).expect("JSContext should not be null in SM hook"))
1319 };
1320 let mut cx = CurrentRealm::assert(&mut cx);
1321 let cx = &mut cx;
1322 let id = unsafe { Handle::from_raw(id) };
1323 let proxy = unsafe { Handle::from_raw(proxy) };
1324
1325 if is_platform_object_same_origin(cx, proxy) {
1330 if get_array_index_from_id(id).is_some() {
1332 unsafe {
1337 (*res).code_ = JSErrNum::JSMSG_CANT_DEFINE_WINDOW_ELEMENT as usize;
1338 }
1339 return true;
1340 }
1341
1342 rooted!(&in(cx) let target = window_proxy_target(proxy));
1344 return unsafe {
1345 JS_DefinePropertyById(cx.raw_cx(), target.handle().into(), id.into(), desc, res)
1346 };
1347 }
1348
1349 report_cross_origin_denial::<DomTypeHolder>(cx, id, "define")
1351}
1352
1353#[expect(unsafe_code)]
1354unsafe extern "C" fn has(
1355 cx: *mut RawJSContext,
1356 proxy: RawHandleObject,
1357 id: RawHandleId,
1358 bp: *mut bool,
1359) -> bool {
1360 unsafe { has_or_has_own(cx, proxy, id, bp, IncludePrototypes::Yes) }
1361}
1362
1363#[expect(unsafe_code)]
1364unsafe extern "C" fn has_own(
1365 cx: *mut RawJSContext,
1366 proxy: RawHandleObject,
1367 id: RawHandleId,
1368 bp: *mut bool,
1369) -> bool {
1370 unsafe { has_or_has_own(cx, proxy, id, bp, IncludePrototypes::No) }
1371}
1372
1373enum IncludePrototypes {
1374 Yes,
1375 No,
1376}
1377
1378#[expect(unsafe_code)]
1379unsafe fn has_or_has_own(
1380 cx: *mut RawJSContext,
1381 proxy: RawHandleObject,
1382 id: RawHandleId,
1383 bp: *mut bool,
1384 include_prototypes: IncludePrototypes,
1385) -> bool {
1386 let mut cx = unsafe { JSContext::from_ptr(ptr::NonNull::new(cx).unwrap()) };
1387 let mut cx = CurrentRealm::assert(&mut cx);
1388 let cx = &mut cx;
1389 let proxy = unsafe { Handle::from_raw(proxy) };
1390 let id = unsafe { Handle::from_raw(id) };
1391
1392 rooted!(&in(cx) let target = window_proxy_target(proxy));
1393 let window = WindowOrDissimilarOriginWindow::new(cx, target.handle());
1394
1395 let (success, found) = if is_platform_object_same_origin(cx, proxy) {
1396 if let Some(array_index) = get_array_index_from_id(id) &&
1397 window
1398 .window_proxy_for_child_navigable_at_index(array_index)
1399 .is_some()
1400 {
1401 unsafe { *bp = true };
1402 return true;
1403 }
1404
1405 let mut found = false;
1406 let success = match include_prototypes {
1407 IncludePrototypes::Yes => unsafe {
1408 JS_HasPropertyById(cx, target.handle(), id, &mut found)
1409 },
1410 IncludePrototypes::No => unsafe {
1411 JS_HasOwnPropertyById(cx, target.handle(), id, &mut found)
1412 },
1413 };
1414 (success, found)
1415 } else {
1416 rooted!(&in(cx) let mut property_descriptor = PropertyDescriptor::default());
1417 let mut is_none = false;
1418 let success = unsafe {
1419 get_own_property_descriptor(
1420 cx.raw_cx(),
1421 proxy.into(),
1422 id.into(),
1423 property_descriptor.handle_mut().into(),
1424 &mut is_none,
1425 )
1426 };
1427 (success, !is_none)
1428 };
1429
1430 if !success {
1431 return false;
1432 }
1433 unsafe { *bp = found };
1434 true
1435}
1436
1437#[expect(unsafe_code)]
1439unsafe extern "C" fn get(
1440 cx: *mut RawJSContext,
1441 proxy: RawHandleObject,
1442 receiver: RawHandleValue,
1443 id: RawHandleId,
1444 return_value: RawMutableHandleValue,
1445) -> bool {
1446 let mut cx = unsafe {
1447 JSContext::from_ptr(NonNull::new(cx).expect("JSContext should not be null in SM hook"))
1449 };
1450 let mut cx = CurrentRealm::assert(&mut cx);
1451 let cx = &mut cx;
1452 let proxy = unsafe { Handle::from_raw(proxy) };
1453 let receiver = unsafe { Handle::from_raw(receiver) };
1454 let id = unsafe { Handle::from_raw(id) };
1455 let return_value = unsafe { MutableHandle::from_raw(return_value) };
1456
1457 rooted!(&in(cx) let target = window_proxy_target(proxy));
1459 let window = WindowOrDissimilarOriginWindow::new(cx, target.handle());
1460
1461 if is_platform_object_same_origin(cx, proxy) {
1468 if let Some(index) = get_array_index_from_id(id) &&
1469 let Some(window_proxy) = window.window_proxy_for_child_navigable_at_index(index)
1470 {
1471 window_proxy.to_jsval(cx, return_value);
1472 return true;
1473 }
1474
1475 return unsafe { JS_ForwardGetPropertyTo(cx, target.handle(), id, receiver, return_value) };
1476 }
1477
1478 proxyhandler::cross_origin_get::<DomTypeHolder>(cx, proxy, receiver, id, return_value)
1480}
1481
1482#[expect(unsafe_code)]
1484unsafe extern "C" fn set(
1485 cx: *mut RawJSContext,
1486 proxy: RawHandleObject,
1487 id: RawHandleId,
1488 v: RawHandleValue,
1489 receiver: RawHandleValue,
1490 res: *mut ObjectOpResult,
1491) -> bool {
1492 let mut cx = unsafe {
1493 JSContext::from_ptr(NonNull::new(cx).expect("JSContext should not be null in SM hook"))
1495 };
1496 let mut cx = CurrentRealm::assert(&mut cx);
1497 let cx = &mut cx;
1498
1499 let proxy = unsafe { Handle::from_raw(proxy) };
1508 let id = unsafe { Handle::from_raw(id) };
1509
1510 if is_platform_object_same_origin(cx, proxy) {
1512 if get_array_index_from_id(id).is_some() {
1514 unsafe { (*res).code_ = JSErrNum::JSMSG_READ_ONLY as usize };
1516 return true;
1517 }
1518
1519 rooted!(&in(cx) let target = window_proxy_target(proxy));
1521 return unsafe {
1522 JS_ForwardSetPropertyTo(
1523 cx.raw_cx(),
1524 target.handle().into(),
1525 id.into(),
1526 v,
1527 receiver,
1528 res,
1529 )
1530 };
1531 }
1532
1533 let receiver = unsafe { HandleValue::from_raw(receiver) };
1535 unsafe { cross_origin_set::<DomTypeHolder>(cx, proxy, id, v, receiver, res) }
1536}
1537
1538#[expect(unsafe_code)]
1539unsafe extern "C" fn get_prototype_if_ordinary(
1540 _: *mut RawJSContext,
1541 _: RawHandleObject,
1542 is_ordinary: *mut bool,
1543 _: RawMutableHandleObject,
1544) -> bool {
1545 unsafe { *is_ordinary = false };
1558 true
1559}
1560
1561#[expect(unsafe_code)]
1563unsafe extern "C" fn get_prototype(
1564 cx: *mut RawJSContext,
1565 proxy: RawHandleObject,
1566 result: RawMutableHandleObject,
1567) -> bool {
1568 let mut cx = unsafe { JSContext::from_ptr(ptr::NonNull::new(cx).unwrap()) };
1569 let mut realm = CurrentRealm::assert(&mut cx);
1570 let proxy = unsafe { Handle::from_raw(proxy) };
1571 let result = unsafe { MutableHandleObject::from_raw(result) };
1572 maybe_cross_origin_get_prototype::<DomTypeHolder>(
1573 &mut realm,
1574 proxy,
1575 GetProtoObject::<DomTypeHolder>,
1576 result,
1577 )
1578}
1579
1580#[expect(unsafe_code)]
1582unsafe extern "C" fn delete(
1583 cx: *mut RawJSContext,
1584 proxy: RawHandleObject,
1585 id: RawHandleId,
1586 result: *mut ObjectOpResult,
1587) -> bool {
1588 let mut cx = unsafe { JSContext::from_ptr(ptr::NonNull::new(cx).unwrap()) };
1589 let mut cx = CurrentRealm::assert(&mut cx);
1590 let cx = &mut cx;
1591 let proxy = unsafe { Handle::from_raw(proxy) };
1592 let id = unsafe { Handle::from_raw(id) };
1593
1594 rooted!(&in(cx) let target = window_proxy_target(proxy));
1596 let window = WindowOrDissimilarOriginWindow::new(cx, target.handle());
1597
1598 if is_platform_object_same_origin(cx, proxy) {
1600 if let Some(array_index) = get_array_index_from_id(id) {
1602 let code = if window
1604 .window_proxy_for_child_navigable_at_index(array_index)
1605 .is_none()
1606 {
1607 0 } else {
1610 JSErrNum::JSMSG_CANT_DELETE_WINDOW_ELEMENT as usize
1612 };
1613 unsafe { (*result).code_ = code };
1614 return true;
1615 }
1616 rooted!(&in(cx) let target = window_proxy_target(proxy));
1618 return unsafe {
1619 JS_DeletePropertyById(cx.raw_cx(), target.handle().into(), id.into(), result)
1620 };
1621 }
1622
1623 report_cross_origin_denial::<DomTypeHolder>(cx, id, "delete")
1625}
1626
1627#[expect(unsafe_code)]
1629unsafe extern "C" fn own_property_keys(
1630 cx: *mut RawJSContext,
1631 proxy: RawHandleObject,
1632 property_keys: RawMutableHandleIdVector,
1633) -> bool {
1634 let mut cx = unsafe { JSContext::from_ptr(ptr::NonNull::new(cx).unwrap()) };
1635 let mut cx = CurrentRealm::assert(&mut cx);
1636 let cx = &mut cx;
1637 let proxy = unsafe { Handle::from_raw(proxy) };
1638
1639 rooted!(&in(cx) let target = window_proxy_target(proxy));
1641 let window = WindowOrDissimilarOriginWindow::new(cx, target.handle());
1642
1643 let max_properties = window.iframe_count();
1645
1646 rooted!(&in(cx) let mut rooted_index_jsid: jsid);
1648 for index in 0..max_properties {
1649 unsafe { int_to_jsid(index as i32, rooted_index_jsid.handle_mut()) };
1650 unsafe { AppendToIdVector(property_keys, rooted_index_jsid.handle()) };
1651 }
1652
1653 if is_platform_object_same_origin(cx, proxy) {
1654 return unsafe {
1657 GetPropertyKeys(
1658 cx,
1659 target.handle(),
1660 JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS,
1661 property_keys,
1662 )
1663 };
1664 }
1665
1666 cross_origin_own_property_keys(cx, proxy, window.cross_origin_properties(), property_keys)
1668}
1669
1670#[expect(unsafe_code)]
1675unsafe extern "C" fn get_own_enumerable_property_keys(
1676 cx: *mut RawJSContext,
1677 proxy: RawHandleObject,
1678 property_keys: RawMutableHandleIdVector,
1679) -> bool {
1680 let mut cx = unsafe { JSContext::from_ptr(ptr::NonNull::new(cx).unwrap()) };
1681 let mut cx = CurrentRealm::assert(&mut cx);
1682 let cx = &mut cx;
1683 let proxy = unsafe { Handle::from_raw(proxy) };
1684
1685 rooted!(&in(cx) let target = window_proxy_target(proxy));
1687 let window = WindowOrDissimilarOriginWindow::new(cx, target.handle());
1688
1689 let max_properties = window.iframe_count();
1691
1692 rooted!(&in(cx) let mut rooted_index_jsid: jsid);
1694 for index in 0..max_properties {
1695 unsafe { int_to_jsid(index as i32, rooted_index_jsid.handle_mut()) };
1696 unsafe { AppendToIdVector(property_keys, rooted_index_jsid.handle()) };
1697 }
1698
1699 if is_platform_object_same_origin(cx, proxy) {
1700 return unsafe { GetPropertyKeys(cx, target.handle(), JSITER_OWNONLY, property_keys) };
1703 }
1704
1705 true
1708}
1709
1710#[expect(unsafe_code)]
1711unsafe extern "C" fn enumerate(
1712 cx: *mut RawJSContext,
1713 proxy: RawHandleObject,
1714 property_keys: RawMutableHandleIdVector,
1715) -> bool {
1716 let mut cx = unsafe { JSContext::from_ptr(ptr::NonNull::new(cx).unwrap()) };
1720 let cx = &mut cx;
1721 let proxy = unsafe { Handle::from_raw(proxy) };
1722
1723 unsafe { GetPropertyKeys(cx, proxy, 0, property_keys) }
1724}
1725
1726static PROXY_TRAPS: ProxyTraps = ProxyTraps {
1727 enter: None,
1728 getOwnPropertyDescriptor: Some(get_own_property_descriptor),
1729 defineProperty: Some(define_property),
1730 ownPropertyKeys: Some(own_property_keys),
1731 delete_: Some(delete),
1732 enumerate: Some(enumerate),
1733 getPrototypeIfOrdinary: Some(get_prototype_if_ordinary),
1734 getPrototype: Some(get_prototype),
1735 setPrototype: Some(maybe_cross_origin_set_prototype_rawcx),
1736 setImmutablePrototype: None,
1737 preventExtensions: Some(prevent_extensions),
1738 isExtensible: Some(is_extensible),
1739 has: Some(has),
1740 get: Some(get),
1741 set: Some(set),
1742 call: None,
1743 construct: None,
1744 hasOwn: Some(has_own),
1745 getOwnEnumerablePropertyKeys: Some(get_own_enumerable_property_keys),
1746 nativeCall: None,
1747 objectClassIs: None,
1748 className: None,
1749 fun_toString: None,
1750 boxedValue_unbox: None,
1751 defaultValue: None,
1752 trace: Some(trace),
1753 finalize: Some(finalize),
1754 objectMoved: None,
1755 isCallable: None,
1756 isConstructor: None,
1757};
1758
1759pub(crate) struct WindowProxyHandler(*const libc::c_void);
1762
1763impl MallocSizeOf for WindowProxyHandler {
1764 fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
1765 0
1767 }
1768}
1769
1770#[expect(unsafe_code)]
1772unsafe impl Send for WindowProxyHandler {}
1773#[expect(unsafe_code)]
1775unsafe impl Sync for WindowProxyHandler {}
1776
1777#[expect(unsafe_code)]
1778impl WindowProxyHandler {
1779 fn new(traps: &ProxyTraps) -> Self {
1780 let ptr = unsafe { CreateWrapperProxyHandler(traps) };
1782 assert!(!ptr.is_null());
1783 Self(ptr)
1784 }
1785
1786 pub(crate) fn proxy_handler() -> &'static Self {
1788 use std::sync::OnceLock;
1789 static SINGLETON: OnceLock<WindowProxyHandler> = OnceLock::new();
1795 SINGLETON.get_or_init(|| Self::new(&PROXY_TRAPS))
1796 }
1797
1798 fn new_window_proxy(
1801 &self,
1802 cx: &mut JSContext,
1803 window_jsobject: js::gc::HandleObject,
1804 ) -> *mut JSObject {
1805 let obj = unsafe { NewWindowProxy(cx, window_jsobject, self.0) };
1806 assert!(!obj.is_null());
1807 obj
1808 }
1809}
1810
1811#[expect(unsafe_code)]
1812impl Drop for WindowProxyHandler {
1813 fn drop(&mut self) {
1814 unsafe {
1817 DeleteWrapperProxyHandler(self.0);
1818 }
1819 }
1820}
1821
1822#[expect(unsafe_code)]
1825unsafe extern "C" fn finalize(_fop: *mut GCContext, obj: *mut JSObject) {
1826 let mut slot = UndefinedValue();
1827 unsafe { GetProxyReservedSlot(obj, 0, &mut slot) };
1828 let this = slot.to_private() as *mut WindowProxy;
1829 if this.is_null() {
1830 return;
1832 }
1833 unsafe {
1834 (*this).reflector.drop_memory(&*this);
1835 let jsobject = (*this).reflector.get_jsobject().get();
1836 debug!(
1837 "WindowProxy finalize: {:p}, with reflector {:p} from {:p}.",
1838 this, jsobject, obj
1839 );
1840 let _ = Box::from_raw(this);
1841 }
1842}
1843
1844#[expect(unsafe_code)]
1845unsafe extern "C" fn trace(trc: *mut JSTracer, obj: *mut JSObject) {
1846 let mut slot = UndefinedValue();
1847 unsafe { GetProxyReservedSlot(obj, 0, &mut slot) };
1848 let this = slot.to_private() as *const WindowProxy;
1849 if this.is_null() {
1850 return;
1852 }
1853 unsafe { (*this).trace(trc) };
1854}
1855
1856enum WindowOrDissimilarOriginWindow {
1859 Window(DomRoot<Window>),
1860 DissimilarOriginWindow(DomRoot<DissimilarOriginWindow>),
1861}
1862
1863impl WindowOrDissimilarOriginWindow {
1864 fn new(cx: &mut JSContext, handle_object: HandleObject) -> Self {
1865 if let Ok(window) = root_from_handleobject::<Window>(cx, handle_object) {
1866 Self::Window(window)
1867 } else if let Ok(window) =
1868 root_from_handleobject::<DissimilarOriginWindow>(cx, handle_object)
1869 {
1870 Self::DissimilarOriginWindow(window)
1871 } else {
1872 unreachable!("WindowProxy should always be backed by some kind of window");
1873 }
1874 }
1875
1876 fn global_scope(&self) -> DomRoot<GlobalScope> {
1877 match self {
1878 WindowOrDissimilarOriginWindow::Window(window) => window.global(),
1879 WindowOrDissimilarOriginWindow::DissimilarOriginWindow(window) => window.global(),
1880 }
1881 }
1882
1883 fn window_proxy(&self) -> DomRoot<WindowProxy> {
1884 match self {
1885 WindowOrDissimilarOriginWindow::Window(window) => window.window_proxy(),
1886 WindowOrDissimilarOriginWindow::DissimilarOriginWindow(window) => window.window_proxy(),
1887 }
1888 }
1889
1890 #[expect(unsafe_code)]
1891 fn cross_origin_properties(&self) -> &'static CrossOriginProperties {
1892 match self {
1893 WindowOrDissimilarOriginWindow::Window(..) => unsafe {
1894 WindowBinding::CROSS_ORIGIN_PROPERTIES.get()
1895 },
1896 WindowOrDissimilarOriginWindow::DissimilarOriginWindow(..) => unsafe {
1897 DissimilarOriginWindowBinding::CROSS_ORIGIN_PROPERTIES.get()
1898 },
1899 }
1900 }
1901
1902 fn iframe_count(&self) -> u32 {
1903 match self {
1904 WindowOrDissimilarOriginWindow::Window(window) => window.Length(),
1905 WindowOrDissimilarOriginWindow::DissimilarOriginWindow(window) => window.Length(),
1906 }
1907 }
1908
1909 fn window_proxy_for_child_navigable_at_index(
1910 &self,
1911 index: u32,
1912 ) -> Option<DomRoot<WindowProxy>> {
1913 let window_proxy = self.window_proxy();
1914 let browsing_context_id = if let Some(document) = window_proxy.document() {
1915 document.iframes().at_insertion_index(index as usize)?
1916 } else {
1917 let parent_browsing_context_id = window_proxy.browsing_context_id();
1918 let (result_sender, result_receiver) = generic_channel::channel().unwrap();
1919 let _ = self.global_scope().script_to_constellation_chan().send(
1920 ScriptToConstellationMessage::GetChildBrowsingContextId(
1921 parent_browsing_context_id,
1922 index as usize,
1923 result_sender,
1924 ),
1925 );
1926 result_receiver.recv().ok().flatten()?
1927 };
1928 ScriptThread::window_proxies().find_window_proxy(browsing_context_id)
1929 }
1930
1931 fn named_child_navigable(
1952 &self,
1953 cx: &mut CurrentRealm,
1954 id: HandleId,
1955 ) -> Option<DomRoot<WindowProxy>> {
1956 let Self::Window(window) = &self else {
1958 return None;
1959 };
1960
1961 let name = jsid_to_string(cx, id)?;
1962 if name.is_empty() {
1963 return None;
1964 }
1965 window
1966 .Document()
1967 .iframes()
1968 .iter()
1969 .filter_map(|iframe| iframe.GetContentWindow())
1970 .find(|window_proxy| window_proxy.get_name() == name)
1971 }
1972}