1use std::cell::Cell;
6use std::ptr;
7
8use base::generic_channel;
9use base::generic_channel::GenericSend;
10use base::id::{BrowsingContextId, PipelineId, WebViewId};
11use constellation_traits::{
12 AuxiliaryWebViewCreationRequest, LoadData, LoadOrigin, NavigationHistoryBehavior,
13 ScriptToConstellationMessage,
14};
15use dom_struct::dom_struct;
16use html5ever::local_name;
17use indexmap::map::IndexMap;
18use ipc_channel::ipc;
19use js::JSCLASS_IS_GLOBAL;
20use js::glue::{
21 CreateWrapperProxyHandler, DeleteWrapperProxyHandler, GetProxyPrivate, GetProxyReservedSlot,
22 ProxyTraps, SetProxyReservedSlot,
23};
24use js::jsapi::{
25 GCContext, Handle as RawHandle, HandleId as RawHandleId, HandleObject as RawHandleObject,
26 HandleValue as RawHandleValue, JS_DefinePropertyById, JS_ForwardGetPropertyTo,
27 JS_ForwardSetPropertyTo, JS_GetOwnPropertyDescriptorById, JS_HasOwnPropertyById,
28 JS_HasPropertyById, JS_IsExceptionPending, JSAutoRealm, JSContext, JSErrNum, JSObject,
29 JSPROP_ENUMERATE, JSPROP_READONLY, JSTracer, MutableHandle as RawMutableHandle,
30 MutableHandleObject as RawMutableHandleObject, MutableHandleValue as RawMutableHandleValue,
31 ObjectOpResult, PropertyDescriptor,
32};
33use js::jsval::{NullValue, PrivateValue, UndefinedValue};
34use js::rust::wrappers::{JS_TransplantObject, NewWindowProxy, SetWindowProxy};
35use js::rust::{Handle, MutableHandle, MutableHandleValue, get_object_class};
36use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
37use net_traits::request::Referrer;
38use net_traits::storage_thread::StorageThreadMsg;
39use script_traits::NewLayoutInfo;
40use serde::{Deserialize, Serialize};
41use servo_url::{ImmutableOrigin, ServoUrl};
42use style::attr::parse_integer;
43
44use crate::dom::bindings::cell::DomRefCell;
45use crate::dom::bindings::conversions::{ToJSValConvertible, root_from_handleobject};
46use crate::dom::bindings::error::{Error, Fallible, throw_dom_exception};
47use crate::dom::bindings::inheritance::Castable;
48use crate::dom::bindings::proxyhandler::set_property_descriptor;
49use crate::dom::bindings::reflector::{DomGlobal, DomObject, Reflector};
50use crate::dom::bindings::root::{Dom, DomRoot};
51use crate::dom::bindings::str::{DOMString, USVString};
52use crate::dom::bindings::trace::JSTraceable;
53use crate::dom::bindings::utils::{AsVoidPtr, get_array_index_from_id};
54use crate::dom::dissimilaroriginwindow::DissimilarOriginWindow;
55use crate::dom::document::Document;
56use crate::dom::element::Element;
57use crate::dom::globalscope::GlobalScope;
58use crate::dom::window::Window;
59use crate::realms::{AlreadyInRealm, InRealm, enter_realm};
60use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
61use crate::script_thread::ScriptThread;
62
63#[dom_struct]
64pub(crate) struct WindowProxy {
69 reflector: Reflector,
74
75 #[no_trace]
79 browsing_context_id: BrowsingContextId,
80
81 #[no_trace]
83 opener: Option<BrowsingContextId>,
84
85 #[no_trace]
88 webview_id: WebViewId,
89
90 name: DomRefCell<DOMString>,
93 #[no_trace]
99 currently_active: Cell<Option<PipelineId>>,
100
101 discarded: Cell<bool>,
103
104 disowned: Cell<bool>,
106
107 is_closing: Cell<bool>,
109
110 frame_element: Option<Dom<Element>>,
112
113 parent: Option<Dom<WindowProxy>>,
115
116 delaying_load_events_mode: Cell<bool>,
118
119 #[no_trace]
121 creator_base_url: Option<ServoUrl>,
122
123 #[no_trace]
125 creator_url: Option<ServoUrl>,
126
127 #[no_trace]
129 creator_origin: Option<ImmutableOrigin>,
130}
131
132impl WindowProxy {
133 fn new_inherited(
134 browsing_context_id: BrowsingContextId,
135 webview_id: WebViewId,
136 currently_active: Option<PipelineId>,
137 frame_element: Option<&Element>,
138 parent: Option<&WindowProxy>,
139 opener: Option<BrowsingContextId>,
140 creator: CreatorBrowsingContextInfo,
141 ) -> WindowProxy {
142 let name = frame_element.map_or(DOMString::new(), |e| {
143 e.get_string_attribute(&local_name!("name"))
144 });
145 WindowProxy {
146 reflector: Reflector::new(),
147 browsing_context_id,
148 webview_id,
149 name: DomRefCell::new(name),
150 currently_active: Cell::new(currently_active),
151 discarded: Cell::new(false),
152 disowned: Cell::new(false),
153 is_closing: Cell::new(false),
154 frame_element: frame_element.map(Dom::from_ref),
155 parent: parent.map(Dom::from_ref),
156 delaying_load_events_mode: Cell::new(false),
157 opener,
158 creator_base_url: creator.base_url,
159 creator_url: creator.url,
160 creator_origin: creator.origin,
161 }
162 }
163
164 #[allow(unsafe_code)]
165 pub(crate) fn new(
166 window: &Window,
167 browsing_context_id: BrowsingContextId,
168 webview_id: WebViewId,
169 frame_element: Option<&Element>,
170 parent: Option<&WindowProxy>,
171 opener: Option<BrowsingContextId>,
172 creator: CreatorBrowsingContextInfo,
173 ) -> DomRoot<WindowProxy> {
174 unsafe {
175 let handler = window.windowproxy_handler();
176
177 let cx = GlobalScope::get_cx();
178 let window_jsobject = window.reflector().get_jsobject();
179 assert!(!window_jsobject.get().is_null());
180 assert_ne!(
181 ((*get_object_class(window_jsobject.get())).flags & JSCLASS_IS_GLOBAL),
182 0
183 );
184 let _ac = JSAutoRealm::new(*cx, window_jsobject.get());
185
186 rooted!(in(*cx) let js_proxy = handler.new_window_proxy(&cx, window_jsobject));
188 assert!(!js_proxy.is_null());
189
190 let current = Some(window.upcast::<GlobalScope>().pipeline_id());
193 let window_proxy = Box::new(WindowProxy::new_inherited(
194 browsing_context_id,
195 webview_id,
196 current,
197 frame_element,
198 parent,
199 opener,
200 creator,
201 ));
202
203 SetProxyReservedSlot(
206 js_proxy.get(),
207 0,
208 &PrivateValue((*window_proxy).as_void_ptr()),
209 );
210
211 SetWindowProxy(*cx, window_jsobject, js_proxy.handle());
213
214 debug!(
216 "Initializing reflector of {:p} to {:p}.",
217 window_proxy,
218 js_proxy.get()
219 );
220 window_proxy.reflector.set_jsobject(js_proxy.get());
221 DomRoot::from_ref(&*Box::into_raw(window_proxy))
222 }
223 }
224
225 #[allow(unsafe_code)]
226 pub(crate) fn new_dissimilar_origin(
227 global_to_clone_from: &GlobalScope,
228 browsing_context_id: BrowsingContextId,
229 webview_id: WebViewId,
230 parent: Option<&WindowProxy>,
231 opener: Option<BrowsingContextId>,
232 creator: CreatorBrowsingContextInfo,
233 ) -> DomRoot<WindowProxy> {
234 unsafe {
235 let handler = WindowProxyHandler::x_origin_proxy_handler();
236
237 let cx = GlobalScope::get_cx();
238
239 let window_proxy = Box::new(WindowProxy::new_inherited(
241 browsing_context_id,
242 webview_id,
243 None,
244 None,
245 parent,
246 opener,
247 creator,
248 ));
249
250 let window = DissimilarOriginWindow::new(global_to_clone_from, &window_proxy);
252 let window_jsobject = window.reflector().get_jsobject();
253 assert!(!window_jsobject.get().is_null());
254 assert_ne!(
255 ((*get_object_class(window_jsobject.get())).flags & JSCLASS_IS_GLOBAL),
256 0
257 );
258 let _ac = JSAutoRealm::new(*cx, window_jsobject.get());
259
260 rooted!(in(*cx) let js_proxy = handler.new_window_proxy(&cx, window_jsobject));
262 assert!(!js_proxy.is_null());
263
264 SetProxyReservedSlot(
267 js_proxy.get(),
268 0,
269 &PrivateValue((*window_proxy).as_void_ptr()),
270 );
271
272 SetWindowProxy(*cx, window_jsobject, js_proxy.handle());
274
275 debug!(
277 "Initializing reflector of {:p} to {:p}.",
278 window_proxy,
279 js_proxy.get()
280 );
281 window_proxy.reflector.set_jsobject(js_proxy.get());
282 DomRoot::from_ref(&*Box::into_raw(window_proxy))
283 }
284 }
285
286 fn create_auxiliary_browsing_context(
288 &self,
289 name: DOMString,
290 noopener: bool,
291 ) -> Option<DomRoot<WindowProxy>> {
292 let (response_sender, response_receiver) = ipc::channel().unwrap();
293 let window = self
294 .currently_active
295 .get()
296 .and_then(ScriptThread::find_document)
297 .map(|doc| DomRoot::from_ref(doc.window()))
298 .unwrap();
299
300 let document = self
301 .currently_active
302 .get()
303 .and_then(ScriptThread::find_document)
304 .expect("A WindowProxy creating an auxiliary to have an active document");
305 let blank_url = ServoUrl::parse("about:blank").ok().unwrap();
306 let load_data = LoadData::new(
307 LoadOrigin::Script(document.origin().immutable().clone()),
308 blank_url,
309 None,
310 document.global().get_referrer(),
311 document.get_referrer_policy(),
312 None, None,
314 false,
315 );
316 let load_info = AuxiliaryWebViewCreationRequest {
317 load_data: load_data.clone(),
318 opener_webview_id: window.webview_id(),
319 opener_pipeline_id: self.currently_active.get().unwrap(),
320 response_sender,
321 };
322 let constellation_msg = ScriptToConstellationMessage::CreateAuxiliaryWebView(load_info);
323 window.send_to_constellation(constellation_msg);
324
325 let response = response_receiver.recv().unwrap()?;
326 let new_browsing_context_id = BrowsingContextId::from(response.new_webview_id);
327 let new_layout_info = NewLayoutInfo {
328 parent_info: None,
329 new_pipeline_id: response.new_pipeline_id,
330 browsing_context_id: new_browsing_context_id,
331 webview_id: response.new_webview_id,
332 opener: Some(self.browsing_context_id),
333 load_data,
334 viewport_details: window.viewport_details(),
335 theme: window.theme(),
338 };
339 ScriptThread::process_attach_layout(new_layout_info, document.origin().clone());
340 let new_window_proxy = ScriptThread::find_document(response.new_pipeline_id)
341 .and_then(|doc| doc.browsing_context())?;
342 if name.to_lowercase() != "_blank" {
343 new_window_proxy.set_name(name);
344 }
345 if noopener {
346 new_window_proxy.disown();
347 } else {
348 let (sender, receiver) = generic_channel::channel().unwrap();
353
354 let msg = StorageThreadMsg::Clone {
355 sender,
356 src: window.window_proxy().webview_id(),
357 dest: response.new_webview_id,
358 };
359
360 GenericSend::send(document.global().resource_threads(), msg).unwrap();
361 receiver.recv().unwrap();
362 }
363 Some(new_window_proxy)
364 }
365
366 pub(crate) fn is_delaying_load_events_mode(&self) -> bool {
368 self.delaying_load_events_mode.get()
369 }
370
371 pub(crate) fn start_delaying_load_events_mode(&self) {
373 self.delaying_load_events_mode.set(true);
374 }
375
376 pub(crate) fn stop_delaying_load_events_mode(&self) {
378 self.delaying_load_events_mode.set(false);
379 if let Some(document) = self.document() {
380 if !document.loader().events_inhibited() {
381 ScriptThread::mark_document_with_no_blocked_loads(&document);
382 }
383 }
384 }
385
386 pub(crate) fn disown(&self) {
388 self.disowned.set(true);
389 }
390
391 pub(crate) fn close(&self) {
394 self.is_closing.set(true);
395 }
396
397 pub(crate) fn is_closing(&self) -> bool {
399 self.is_closing.get()
400 }
401
402 pub(crate) fn creator_base_url(&self) -> Option<ServoUrl> {
404 self.creator_base_url.clone()
405 }
406
407 pub(crate) fn has_creator_base_url(&self) -> bool {
408 self.creator_base_url.is_some()
409 }
410
411 pub(crate) fn creator_url(&self) -> Option<ServoUrl> {
413 self.creator_url.clone()
414 }
415
416 pub(crate) fn has_creator_url(&self) -> bool {
417 self.creator_base_url.is_some()
418 }
419
420 pub(crate) fn creator_origin(&self) -> Option<ImmutableOrigin> {
422 self.creator_origin.clone()
423 }
424
425 pub(crate) fn has_creator_origin(&self) -> bool {
426 self.creator_origin.is_some()
427 }
428
429 #[allow(unsafe_code)]
430 pub(crate) fn opener(
432 &self,
433 cx: *mut JSContext,
434 in_realm_proof: InRealm,
435 mut retval: MutableHandleValue,
436 ) {
437 if self.disowned.get() {
438 return retval.set(NullValue());
439 }
440 let opener_id = match self.opener {
441 Some(opener_browsing_context_id) => opener_browsing_context_id,
442 None => return retval.set(NullValue()),
443 };
444 let parent_browsing_context = self.parent.as_deref();
445 let opener_proxy = match ScriptThread::find_window_proxy(opener_id) {
446 Some(window_proxy) => window_proxy,
447 None => {
448 let sender_pipeline_id = self.currently_active().unwrap();
449 match ScriptThread::get_top_level_for_browsing_context(
450 sender_pipeline_id,
451 opener_id,
452 ) {
453 Some(opener_top_id) => {
454 let global_to_clone_from =
455 unsafe { GlobalScope::from_context(cx, in_realm_proof) };
456 let creator =
457 CreatorBrowsingContextInfo::from(parent_browsing_context, None);
458 WindowProxy::new_dissimilar_origin(
459 &global_to_clone_from,
460 opener_id,
461 opener_top_id,
462 None,
463 None,
464 creator,
465 )
466 },
467 None => return retval.set(NullValue()),
468 }
469 },
470 };
471 if opener_proxy.is_browsing_context_discarded() {
472 return retval.set(NullValue());
473 }
474 unsafe { opener_proxy.to_jsval(cx, retval) };
475 }
476
477 pub(crate) fn open(
479 &self,
480 url: USVString,
481 target: DOMString,
482 features: DOMString,
483 can_gc: CanGc,
484 ) -> Fallible<Option<DomRoot<WindowProxy>>> {
485 let non_empty_target = match target.as_ref() {
487 "" => DOMString::from("_blank"),
488 _ => target,
489 };
490 let tokenized_features = tokenize_open_features(features);
492 let noreferrer = parse_open_feature_boolean(&tokenized_features, "noreferrer");
496
497 let noopener = if noreferrer {
500 true
501 } else {
502 parse_open_feature_boolean(&tokenized_features, "noopener")
503 };
504 let (chosen, new) = match self.choose_browsing_context(non_empty_target, noopener) {
514 (Some(chosen), new) => (chosen, new),
515 (None, _) => return Ok(None),
516 };
517 let target_document = match chosen.document() {
520 Some(target_document) => target_document,
521 None => return Ok(None),
522 };
523 let has_trustworthy_ancestor_origin = if new {
524 target_document.has_trustworthy_ancestor_or_current_origin()
525 } else {
526 false
527 };
528 let target_window = target_document.window();
529 if !url.is_empty() {
532 let existing_document = self
533 .currently_active
534 .get()
535 .and_then(ScriptThread::find_document)
536 .unwrap();
537 let url = match existing_document.url().join(&url) {
538 Ok(url) => url,
539 Err(_) => return Err(Error::Syntax),
540 };
541 let referrer = if noreferrer {
542 Referrer::NoReferrer
543 } else {
544 target_window.as_global_scope().get_referrer()
545 };
546 let referrer_policy = target_document.get_referrer_policy();
550 let pipeline_id = target_window.pipeline_id();
551 let secure = target_window.as_global_scope().is_secure_context();
552 let load_data = LoadData::new(
553 LoadOrigin::Script(existing_document.origin().immutable().clone()),
554 url,
555 Some(pipeline_id),
556 referrer,
557 referrer_policy,
558 Some(secure),
559 Some(target_document.insecure_requests_policy()),
560 has_trustworthy_ancestor_origin,
561 );
562 let history_handling = if new {
563 NavigationHistoryBehavior::Replace
564 } else {
565 NavigationHistoryBehavior::Push
566 };
567 target_window.load_url(history_handling, false, load_data, can_gc);
568 }
569 if noopener {
571 return Ok(None);
572 }
573 Ok(target_document.browsing_context())
575 }
576
577 pub(crate) fn choose_browsing_context(
579 &self,
580 name: DOMString,
581 noopener: bool,
582 ) -> (Option<DomRoot<WindowProxy>>, bool) {
583 match name.to_lowercase().as_ref() {
584 "" | "_self" => {
585 (Some(DomRoot::from_ref(self)), false)
587 },
588 "_parent" => {
589 if let Some(parent) = self.parent() {
591 return (Some(DomRoot::from_ref(parent)), false);
592 }
593 (None, false)
594 },
595 "_top" => {
596 (Some(DomRoot::from_ref(self.top())), false)
598 },
599 "_blank" => (self.create_auxiliary_browsing_context(name, noopener), true),
600 _ => {
601 match ScriptThread::find_window_proxy_by_name(&name) {
606 Some(proxy) => (Some(proxy), false),
607 None => (self.create_auxiliary_browsing_context(name, noopener), true),
608 }
609 },
610 }
611 }
612
613 pub(crate) fn is_auxiliary(&self) -> bool {
614 self.opener.is_some()
615 }
616
617 pub(crate) fn discard_browsing_context(&self) {
618 self.discarded.set(true);
619 }
620
621 pub(crate) fn is_browsing_context_discarded(&self) -> bool {
622 self.discarded.get()
623 }
624
625 pub(crate) fn browsing_context_id(&self) -> BrowsingContextId {
626 self.browsing_context_id
627 }
628
629 pub(crate) fn webview_id(&self) -> WebViewId {
630 self.webview_id
631 }
632
633 pub(crate) fn frame_element(&self) -> Option<&Element> {
634 self.frame_element.as_deref()
635 }
636
637 pub(crate) fn document(&self) -> Option<DomRoot<Document>> {
638 self.currently_active
639 .get()
640 .and_then(ScriptThread::find_document)
641 }
642
643 pub(crate) fn parent(&self) -> Option<&WindowProxy> {
644 self.parent.as_deref()
645 }
646
647 pub(crate) fn top(&self) -> &WindowProxy {
648 let mut result = self;
649 while let Some(parent) = result.parent() {
650 result = parent;
651 }
652 result
653 }
654
655 pub fn focus(&self) {
659 debug!(
660 "Requesting the constellation to initiate a focus operation for \
661 browsing context {}",
662 self.browsing_context_id()
663 );
664 self.global()
665 .script_to_constellation_chan()
666 .send(ScriptToConstellationMessage::FocusRemoteDocument(
667 self.browsing_context_id(),
668 ))
669 .unwrap();
670 }
671
672 #[allow(unsafe_code)]
673 fn set_window(&self, window: &GlobalScope, handler: &WindowProxyHandler, _can_gc: CanGc) {
677 unsafe {
678 debug!("Setting window of {:p}.", self);
679
680 let cx = GlobalScope::get_cx();
681 let window_jsobject = window.reflector().get_jsobject();
682 let old_js_proxy = self.reflector.get_jsobject();
683 assert!(!window_jsobject.get().is_null());
684 assert_ne!(
685 ((*get_object_class(window_jsobject.get())).flags & JSCLASS_IS_GLOBAL),
686 0
687 );
688 let _ac = enter_realm(window);
689
690 SetProxyReservedSlot(old_js_proxy.get(), 0, &PrivateValue(ptr::null_mut()));
692
693 rooted!(in(*cx) let new_js_proxy = handler.new_window_proxy(&cx, window_jsobject));
699 SetProxyReservedSlot(new_js_proxy.get(), 0, &PrivateValue(ptr::null_mut()));
702 debug!(
703 "Transplanting proxy from {:p} to {:p}.",
704 old_js_proxy.get(),
705 new_js_proxy.get()
706 );
707 rooted!(in(*cx) let new_js_proxy = JS_TransplantObject(*cx, old_js_proxy, new_js_proxy.handle()));
708 debug!("Transplanted proxy is {:p}.", new_js_proxy.get());
709
710 SetProxyReservedSlot(new_js_proxy.get(), 0, &PrivateValue(self.as_void_ptr()));
712
713 SetWindowProxy(*cx, window_jsobject, new_js_proxy.handle());
715
716 debug!(
718 "Setting reflector of {:p} to {:p}.",
719 self,
720 new_js_proxy.get()
721 );
722 self.reflector.rootable().set(new_js_proxy.get());
723 }
724 }
725
726 pub(crate) fn set_currently_active(&self, window: &Window, can_gc: CanGc) {
727 if let Some(pipeline_id) = self.currently_active() {
728 if pipeline_id == window.pipeline_id() {
729 return debug!(
730 "Attempt to set the currently active window to the currently active window."
731 );
732 }
733 }
734
735 let global_scope = window.as_global_scope();
736 self.set_window(global_scope, WindowProxyHandler::proxy_handler(), can_gc);
737 self.currently_active.set(Some(global_scope.pipeline_id()));
738 }
739
740 pub(crate) fn unset_currently_active(&self, can_gc: CanGc) {
741 if self.currently_active().is_none() {
742 return debug!(
743 "Attempt to unset the currently active window on a windowproxy that does not have one."
744 );
745 }
746 let globalscope = self.global();
747 let window = DissimilarOriginWindow::new(&globalscope, self);
748 self.set_window(
749 window.upcast(),
750 WindowProxyHandler::x_origin_proxy_handler(),
751 can_gc,
752 );
753 self.currently_active.set(None);
754 }
755
756 pub(crate) fn currently_active(&self) -> Option<PipelineId> {
757 self.currently_active.get()
758 }
759
760 pub(crate) fn get_name(&self) -> DOMString {
761 self.name.borrow().clone()
762 }
763
764 pub(crate) fn set_name(&self, name: DOMString) {
765 *self.name.borrow_mut() = name;
766 }
767}
768
769#[derive(Debug, Deserialize, Serialize)]
781pub(crate) struct CreatorBrowsingContextInfo {
782 url: Option<ServoUrl>,
784
785 base_url: Option<ServoUrl>,
787
788 origin: Option<ImmutableOrigin>,
790}
791
792impl CreatorBrowsingContextInfo {
793 pub(crate) fn from(
794 parent: Option<&WindowProxy>,
795 opener: Option<&WindowProxy>,
796 ) -> CreatorBrowsingContextInfo {
797 let creator = match (parent, opener) {
798 (Some(parent), _) => parent.document(),
799 (None, Some(opener)) => opener.document(),
800 (None, None) => None,
801 };
802
803 let base_url = creator.as_deref().map(|document| document.base_url());
804 let url = creator.as_deref().map(|document| document.url());
805 let origin = creator
806 .as_deref()
807 .map(|document| document.origin().immutable().clone());
808
809 CreatorBrowsingContextInfo {
810 base_url,
811 url,
812 origin,
813 }
814 }
815}
816
817fn tokenize_open_features(features: DOMString) -> IndexMap<String, String> {
819 let is_feature_sep = |c: char| c.is_ascii_whitespace() || ['=', ','].contains(&c);
820 let mut tokenized_features = IndexMap::new();
822 let mut iter = features.chars();
824 let mut cur = iter.next();
825
826 while cur.is_some() {
828 let mut name = String::new();
830 let mut value = String::new();
831 while let Some(cur_char) = cur {
833 if !is_feature_sep(cur_char) {
834 break;
835 }
836 cur = iter.next();
837 }
838 while let Some(cur_char) = cur {
840 if is_feature_sep(cur_char) {
841 break;
842 }
843 name.push(cur_char.to_ascii_lowercase());
844 cur = iter.next();
845 }
846 let normalized_name = String::from(match name.as_ref() {
848 "screenx" => "left",
849 "screeny" => "top",
850 "innerwidth" => "width",
851 "innerheight" => "height",
852 _ => name.as_ref(),
853 });
854 while let Some(cur_char) = cur {
856 if cur_char == '=' || cur_char == ',' || !is_feature_sep(cur_char) {
857 break;
858 }
859 cur = iter.next();
860 }
861 if cur.is_some() && is_feature_sep(cur.unwrap()) {
863 while let Some(cur_char) = cur {
865 if !is_feature_sep(cur_char) || cur_char == ',' {
866 break;
867 }
868 cur = iter.next();
869 }
870 while let Some(cur_char) = cur {
872 if is_feature_sep(cur_char) {
873 break;
874 }
875 value.push(cur_char.to_ascii_lowercase());
876 cur = iter.next();
877 }
878 }
879 if !name.is_empty() {
881 tokenized_features.insert(normalized_name, value);
882 }
883 }
884 tokenized_features
886}
887
888fn parse_open_feature_boolean(tokenized_features: &IndexMap<String, String>, name: &str) -> bool {
890 if let Some(value) = tokenized_features.get(name) {
891 if value.is_empty() || value == "yes" {
893 return true;
894 }
895 if let Ok(int) = parse_integer(value.chars()) {
897 return int != 0;
898 }
899 }
900 false
902}
903
904#[allow(unsafe_code, non_snake_case)]
908unsafe fn GetSubframeWindowProxy(
909 cx: *mut JSContext,
910 proxy: RawHandleObject,
911 id: RawHandleId,
912) -> Option<(DomRoot<WindowProxy>, u32)> {
913 let index = get_array_index_from_id(Handle::from_raw(id));
914 if let Some(index) = index {
915 let mut slot = UndefinedValue();
916 GetProxyPrivate(*proxy, &mut slot);
917 rooted!(in(cx) let target = slot.to_object());
918 if let Ok(win) = root_from_handleobject::<Window>(target.handle(), cx) {
919 let browsing_context_id = win.window_proxy().browsing_context_id();
920 let (result_sender, result_receiver) = ipc::channel().unwrap();
921
922 let _ = win.as_global_scope().script_to_constellation_chan().send(
923 ScriptToConstellationMessage::GetChildBrowsingContextId(
924 browsing_context_id,
925 index as usize,
926 result_sender,
927 ),
928 );
929 return result_receiver
930 .recv()
931 .ok()
932 .and_then(|maybe_bcid| maybe_bcid)
933 .and_then(ScriptThread::find_window_proxy)
934 .map(|proxy| (proxy, (JSPROP_ENUMERATE | JSPROP_READONLY) as u32));
935 } else if let Ok(win) =
936 root_from_handleobject::<DissimilarOriginWindow>(target.handle(), cx)
937 {
938 let browsing_context_id = win.window_proxy().browsing_context_id();
939 let (result_sender, result_receiver) = ipc::channel().unwrap();
940
941 let _ = win.global().script_to_constellation_chan().send(
942 ScriptToConstellationMessage::GetChildBrowsingContextId(
943 browsing_context_id,
944 index as usize,
945 result_sender,
946 ),
947 );
948 return result_receiver
949 .recv()
950 .ok()
951 .and_then(|maybe_bcid| maybe_bcid)
952 .and_then(ScriptThread::find_window_proxy)
953 .map(|proxy| (proxy, JSPROP_READONLY as u32));
954 }
955 }
956
957 None
958}
959
960#[allow(unsafe_code, non_snake_case)]
961unsafe extern "C" fn getOwnPropertyDescriptor(
962 cx: *mut JSContext,
963 proxy: RawHandleObject,
964 id: RawHandleId,
965 desc: RawMutableHandle<PropertyDescriptor>,
966 is_none: *mut bool,
967) -> bool {
968 let window = GetSubframeWindowProxy(cx, proxy, id);
969 if let Some((window, attrs)) = window {
970 rooted!(in(cx) let mut val = UndefinedValue());
971 window.to_jsval(cx, val.handle_mut());
972 set_property_descriptor(
973 MutableHandle::from_raw(desc),
974 val.handle(),
975 attrs,
976 &mut *is_none,
977 );
978 return true;
979 }
980
981 let mut slot = UndefinedValue();
982 GetProxyPrivate(proxy.get(), &mut slot);
983 rooted!(in(cx) let target = slot.to_object());
984 JS_GetOwnPropertyDescriptorById(cx, target.handle().into(), id, desc, is_none)
985}
986
987#[allow(unsafe_code, non_snake_case)]
988unsafe extern "C" fn defineProperty(
989 cx: *mut JSContext,
990 proxy: RawHandleObject,
991 id: RawHandleId,
992 desc: RawHandle<PropertyDescriptor>,
993 res: *mut ObjectOpResult,
994) -> bool {
995 if get_array_index_from_id(Handle::from_raw(id)).is_some() {
996 (*res).code_ = JSErrNum::JSMSG_CANT_DEFINE_WINDOW_ELEMENT as ::libc::uintptr_t;
1001 return true;
1002 }
1003
1004 let mut slot = UndefinedValue();
1005 GetProxyPrivate(*proxy.ptr, &mut slot);
1006 rooted!(in(cx) let target = slot.to_object());
1007 JS_DefinePropertyById(cx, target.handle().into(), id, desc, res)
1008}
1009
1010#[allow(unsafe_code)]
1011unsafe extern "C" fn has(
1012 cx: *mut JSContext,
1013 proxy: RawHandleObject,
1014 id: RawHandleId,
1015 bp: *mut bool,
1016) -> bool {
1017 let window = GetSubframeWindowProxy(cx, proxy, id);
1018 if window.is_some() {
1019 *bp = true;
1020 return true;
1021 }
1022
1023 let mut slot = UndefinedValue();
1024 GetProxyPrivate(*proxy.ptr, &mut slot);
1025 rooted!(in(cx) let target = slot.to_object());
1026 let mut found = false;
1027 if !JS_HasPropertyById(cx, target.handle().into(), id, &mut found) {
1028 return false;
1029 }
1030
1031 *bp = found;
1032 true
1033}
1034
1035#[allow(unsafe_code)]
1036unsafe extern "C" fn get(
1037 cx: *mut JSContext,
1038 proxy: RawHandleObject,
1039 receiver: RawHandleValue,
1040 id: RawHandleId,
1041 vp: RawMutableHandleValue,
1042) -> bool {
1043 let window = GetSubframeWindowProxy(cx, proxy, id);
1044 if let Some((window, _attrs)) = window {
1045 window.to_jsval(cx, MutableHandle::from_raw(vp));
1046 return true;
1047 }
1048
1049 let mut slot = UndefinedValue();
1050 GetProxyPrivate(*proxy.ptr, &mut slot);
1051 rooted!(in(cx) let target = slot.to_object());
1052 JS_ForwardGetPropertyTo(cx, target.handle().into(), id, receiver, vp)
1053}
1054
1055#[allow(unsafe_code)]
1056unsafe extern "C" fn set(
1057 cx: *mut JSContext,
1058 proxy: RawHandleObject,
1059 id: RawHandleId,
1060 v: RawHandleValue,
1061 receiver: RawHandleValue,
1062 res: *mut ObjectOpResult,
1063) -> bool {
1064 if get_array_index_from_id(Handle::from_raw(id)).is_some() {
1065 (*res).code_ = JSErrNum::JSMSG_READ_ONLY as ::libc::uintptr_t;
1067 return true;
1068 }
1069
1070 let mut slot = UndefinedValue();
1071 GetProxyPrivate(*proxy.ptr, &mut slot);
1072 rooted!(in(cx) let target = slot.to_object());
1073 JS_ForwardSetPropertyTo(cx, target.handle().into(), id, v, receiver, res)
1074}
1075
1076#[allow(unsafe_code)]
1077unsafe extern "C" fn get_prototype_if_ordinary(
1078 _: *mut JSContext,
1079 _: RawHandleObject,
1080 is_ordinary: *mut bool,
1081 _: RawMutableHandleObject,
1082) -> bool {
1083 *is_ordinary = false;
1096 true
1097}
1098
1099static PROXY_TRAPS: ProxyTraps = ProxyTraps {
1100 enter: None,
1103 getOwnPropertyDescriptor: Some(getOwnPropertyDescriptor),
1104 defineProperty: Some(defineProperty),
1105 ownPropertyKeys: None,
1106 delete_: None,
1107 enumerate: None,
1108 getPrototypeIfOrdinary: Some(get_prototype_if_ordinary),
1109 getPrototype: None, setPrototype: None,
1111 setImmutablePrototype: None,
1112 preventExtensions: None,
1113 isExtensible: None,
1114 has: Some(has),
1115 get: Some(get),
1116 set: Some(set),
1117 call: None,
1118 construct: None,
1119 hasOwn: None,
1120 getOwnEnumerablePropertyKeys: None,
1121 nativeCall: None,
1122 objectClassIs: None,
1123 className: None,
1124 fun_toString: None,
1125 boxedValue_unbox: None,
1126 defaultValue: None,
1127 trace: Some(trace),
1128 finalize: Some(finalize),
1129 objectMoved: None,
1130 isCallable: None,
1131 isConstructor: None,
1132};
1133
1134pub(crate) struct WindowProxyHandler(*const libc::c_void);
1137
1138impl MallocSizeOf for WindowProxyHandler {
1139 fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
1140 0
1142 }
1143}
1144
1145#[allow(unsafe_code)]
1147unsafe impl Send for WindowProxyHandler {}
1148#[allow(unsafe_code)]
1150unsafe impl Sync for WindowProxyHandler {}
1151
1152#[allow(unsafe_code)]
1153impl WindowProxyHandler {
1154 fn new(traps: &ProxyTraps) -> Self {
1155 let ptr = unsafe { CreateWrapperProxyHandler(traps) };
1157 assert!(!ptr.is_null());
1158 Self(ptr)
1159 }
1160
1161 pub(crate) fn x_origin_proxy_handler() -> &'static Self {
1163 use std::sync::OnceLock;
1164 static SINGLETON: OnceLock<WindowProxyHandler> = OnceLock::new();
1170 SINGLETON.get_or_init(|| Self::new(&XORIGIN_PROXY_TRAPS))
1171 }
1172
1173 pub(crate) fn proxy_handler() -> &'static Self {
1175 use std::sync::OnceLock;
1176 static SINGLETON: OnceLock<WindowProxyHandler> = OnceLock::new();
1182 SINGLETON.get_or_init(|| Self::new(&PROXY_TRAPS))
1183 }
1184
1185 pub(crate) fn new_window_proxy(
1188 &self,
1189 cx: &crate::script_runtime::JSContext,
1190 window_jsobject: js::gc::HandleObject,
1191 ) -> *mut JSObject {
1192 let obj = unsafe { NewWindowProxy(**cx, window_jsobject, self.0) };
1193 assert!(!obj.is_null());
1194 obj
1195 }
1196}
1197
1198#[allow(unsafe_code)]
1199impl Drop for WindowProxyHandler {
1200 fn drop(&mut self) {
1201 unsafe {
1204 DeleteWrapperProxyHandler(self.0);
1205 }
1206 }
1207}
1208
1209#[allow(unsafe_code)]
1217unsafe fn throw_security_error(cx: *mut JSContext, realm: InRealm) -> bool {
1218 if !JS_IsExceptionPending(cx) {
1219 let safe_context = SafeJSContext::from_ptr(cx);
1220 let global = GlobalScope::from_context(cx, realm);
1221 throw_dom_exception(safe_context, &global, Error::Security, CanGc::note());
1222 }
1223 false
1224}
1225
1226#[allow(unsafe_code)]
1227unsafe extern "C" fn has_xorigin(
1228 cx: *mut JSContext,
1229 proxy: RawHandleObject,
1230 id: RawHandleId,
1231 bp: *mut bool,
1232) -> bool {
1233 let mut slot = UndefinedValue();
1234 GetProxyPrivate(*proxy.ptr, &mut slot);
1235 rooted!(in(cx) let target = slot.to_object());
1236 let mut found = false;
1237 JS_HasOwnPropertyById(cx, target.handle().into(), id, &mut found);
1238 if found {
1239 *bp = true;
1240 true
1241 } else {
1242 let in_realm_proof = AlreadyInRealm::assert_for_cx(SafeJSContext::from_ptr(cx));
1243 throw_security_error(cx, InRealm::Already(&in_realm_proof))
1244 }
1245}
1246
1247#[allow(unsafe_code)]
1248unsafe extern "C" fn get_xorigin(
1249 cx: *mut JSContext,
1250 proxy: RawHandleObject,
1251 receiver: RawHandleValue,
1252 id: RawHandleId,
1253 vp: RawMutableHandleValue,
1254) -> bool {
1255 let mut found = false;
1256 has_xorigin(cx, proxy, id, &mut found);
1257 found && get(cx, proxy, receiver, id, vp)
1258}
1259
1260#[allow(unsafe_code)]
1261unsafe extern "C" fn set_xorigin(
1262 cx: *mut JSContext,
1263 _: RawHandleObject,
1264 _: RawHandleId,
1265 _: RawHandleValue,
1266 _: RawHandleValue,
1267 _: *mut ObjectOpResult,
1268) -> bool {
1269 let in_realm_proof = AlreadyInRealm::assert_for_cx(SafeJSContext::from_ptr(cx));
1270 throw_security_error(cx, InRealm::Already(&in_realm_proof))
1271}
1272
1273#[allow(unsafe_code)]
1274unsafe extern "C" fn delete_xorigin(
1275 cx: *mut JSContext,
1276 _: RawHandleObject,
1277 _: RawHandleId,
1278 _: *mut ObjectOpResult,
1279) -> bool {
1280 let in_realm_proof = AlreadyInRealm::assert_for_cx(SafeJSContext::from_ptr(cx));
1281 throw_security_error(cx, InRealm::Already(&in_realm_proof))
1282}
1283
1284#[allow(unsafe_code, non_snake_case)]
1285unsafe extern "C" fn getOwnPropertyDescriptor_xorigin(
1286 cx: *mut JSContext,
1287 proxy: RawHandleObject,
1288 id: RawHandleId,
1289 desc: RawMutableHandle<PropertyDescriptor>,
1290 is_none: *mut bool,
1291) -> bool {
1292 let mut found = false;
1293 has_xorigin(cx, proxy, id, &mut found);
1294 found && getOwnPropertyDescriptor(cx, proxy, id, desc, is_none)
1295}
1296
1297#[allow(unsafe_code, non_snake_case)]
1298unsafe extern "C" fn defineProperty_xorigin(
1299 cx: *mut JSContext,
1300 _: RawHandleObject,
1301 _: RawHandleId,
1302 _: RawHandle<PropertyDescriptor>,
1303 _: *mut ObjectOpResult,
1304) -> bool {
1305 let in_realm_proof = AlreadyInRealm::assert_for_cx(SafeJSContext::from_ptr(cx));
1306 throw_security_error(cx, InRealm::Already(&in_realm_proof))
1307}
1308
1309#[allow(unsafe_code, non_snake_case)]
1310unsafe extern "C" fn preventExtensions_xorigin(
1311 cx: *mut JSContext,
1312 _: RawHandleObject,
1313 _: *mut ObjectOpResult,
1314) -> bool {
1315 let in_realm_proof = AlreadyInRealm::assert_for_cx(SafeJSContext::from_ptr(cx));
1316 throw_security_error(cx, InRealm::Already(&in_realm_proof))
1317}
1318
1319static XORIGIN_PROXY_TRAPS: ProxyTraps = ProxyTraps {
1320 enter: None,
1321 getOwnPropertyDescriptor: Some(getOwnPropertyDescriptor_xorigin),
1322 defineProperty: Some(defineProperty_xorigin),
1323 ownPropertyKeys: None,
1324 delete_: Some(delete_xorigin),
1325 enumerate: None,
1326 getPrototypeIfOrdinary: None,
1327 getPrototype: None,
1328 setPrototype: None,
1329 setImmutablePrototype: None,
1330 preventExtensions: Some(preventExtensions_xorigin),
1331 isExtensible: None,
1332 has: Some(has_xorigin),
1333 get: Some(get_xorigin),
1334 set: Some(set_xorigin),
1335 call: None,
1336 construct: None,
1337 hasOwn: Some(has_xorigin),
1338 getOwnEnumerablePropertyKeys: None,
1339 nativeCall: None,
1340 objectClassIs: None,
1341 className: None,
1342 fun_toString: None,
1343 boxedValue_unbox: None,
1344 defaultValue: None,
1345 trace: Some(trace),
1346 finalize: Some(finalize),
1347 objectMoved: None,
1348 isCallable: None,
1349 isConstructor: None,
1350};
1351
1352#[allow(unsafe_code)]
1355unsafe extern "C" fn finalize(_fop: *mut GCContext, obj: *mut JSObject) {
1356 let mut slot = UndefinedValue();
1357 GetProxyReservedSlot(obj, 0, &mut slot);
1358 let this = slot.to_private() as *mut WindowProxy;
1359 if this.is_null() {
1360 return;
1362 }
1363 let jsobject = (*this).reflector.get_jsobject().get();
1364 debug!(
1365 "WindowProxy finalize: {:p}, with reflector {:p} from {:p}.",
1366 this, jsobject, obj
1367 );
1368 let _ = Box::from_raw(this);
1369}
1370
1371#[allow(unsafe_code)]
1372unsafe extern "C" fn trace(trc: *mut JSTracer, obj: *mut JSObject) {
1373 let mut slot = UndefinedValue();
1374 GetProxyReservedSlot(obj, 0, &mut slot);
1375 let this = slot.to_private() as *const WindowProxy;
1376 if this.is_null() {
1377 return;
1379 }
1380 (*this).trace(trc);
1381}