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