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 sender_pipeline_id,
463 opener_id,
464 ) {
465 Some(opener_top_id) => {
466 let global_to_clone_from =
467 unsafe { GlobalScope::from_context(cx, in_realm_proof) };
468 let creator =
469 CreatorBrowsingContextInfo::from(parent_browsing_context, None);
470 WindowProxy::new_dissimilar_origin(
471 &global_to_clone_from,
472 opener_id,
473 opener_top_id,
474 None,
475 None,
476 creator,
477 )
478 },
479 None => return retval.set(NullValue()),
480 }
481 },
482 };
483 if opener_proxy.is_browsing_context_discarded() {
484 return retval.set(NullValue());
485 }
486 unsafe { opener_proxy.to_jsval(cx, retval) };
487 }
488
489 pub(crate) fn open(
491 &self,
492 url: USVString,
493 target: DOMString,
494 features: DOMString,
495 can_gc: CanGc,
496 ) -> Fallible<Option<DomRoot<WindowProxy>>> {
497 let non_empty_target = if target.is_empty() {
499 DOMString::from("_blank")
500 } else {
501 target
502 };
503 let tokenized_features = tokenize_open_features(features);
505 let noreferrer = parse_open_feature_boolean(&tokenized_features, "noreferrer");
509
510 let noopener = if noreferrer {
513 true
514 } else {
515 parse_open_feature_boolean(&tokenized_features, "noopener")
516 };
517 let (chosen, new) = match self.choose_browsing_context(non_empty_target, noopener) {
527 (Some(chosen), new) => (chosen, new),
528 (None, _) => return Ok(None),
529 };
530 let target_document = match chosen.document() {
533 Some(target_document) => target_document,
534 None => return Ok(None),
535 };
536 let has_trustworthy_ancestor_origin = if new {
537 target_document.has_trustworthy_ancestor_or_current_origin()
538 } else {
539 false
540 };
541 let target_window = target_document.window();
542 if !url.is_empty() {
545 let existing_document = self
546 .currently_active
547 .get()
548 .and_then(ScriptThread::find_document)
549 .unwrap();
550 let url = match existing_document.url().join(&url) {
551 Ok(url) => url,
552 Err(_) => return Err(Error::Syntax(None)),
553 };
554 let referrer = if noreferrer {
555 Referrer::NoReferrer
556 } else {
557 target_window.as_global_scope().get_referrer()
558 };
559 let referrer_policy = target_document.get_referrer_policy();
563 let pipeline_id = target_window.pipeline_id();
564 let secure = target_window.as_global_scope().is_secure_context();
565 let load_data = LoadData::new(
566 LoadOrigin::Script(existing_document.origin().immutable().clone()),
567 url,
568 Some(pipeline_id),
569 referrer,
570 referrer_policy,
571 Some(secure),
572 Some(target_document.insecure_requests_policy()),
573 has_trustworthy_ancestor_origin,
574 target_document.creation_sandboxing_flag_set_considering_parent_iframe(),
575 );
576 let history_handling = if new {
577 NavigationHistoryBehavior::Replace
578 } else {
579 NavigationHistoryBehavior::Push
580 };
581 target_window.load_url(history_handling, false, load_data, can_gc);
582 }
583 if noopener {
585 return Ok(None);
586 }
587 Ok(target_document.browsing_context())
589 }
590
591 pub(crate) fn choose_browsing_context(
593 &self,
594 name: DOMString,
595 noopener: bool,
596 ) -> (Option<DomRoot<WindowProxy>>, bool) {
597 match name.to_lowercase().as_ref() {
598 "" | "_self" => {
599 (Some(DomRoot::from_ref(self)), false)
601 },
602 "_parent" => {
603 if let Some(parent) = self.parent() {
605 return (Some(DomRoot::from_ref(parent)), false);
606 }
607 (None, false)
608 },
609 "_top" => {
610 (Some(DomRoot::from_ref(self.top())), false)
612 },
613 "_blank" => (self.create_auxiliary_browsing_context(name, noopener), true),
614 _ => {
615 match ScriptThread::find_window_proxy_by_name(&name) {
620 Some(proxy) => (Some(proxy), false),
621 None => (self.create_auxiliary_browsing_context(name, noopener), true),
622 }
623 },
624 }
625 }
626
627 pub(crate) fn is_auxiliary(&self) -> bool {
628 self.opener.is_some()
629 }
630
631 pub(crate) fn discard_browsing_context(&self) {
632 self.discarded.set(true);
633 }
634
635 pub(crate) fn is_browsing_context_discarded(&self) -> bool {
636 self.discarded.get()
637 }
638
639 pub(crate) fn browsing_context_id(&self) -> BrowsingContextId {
640 self.browsing_context_id
641 }
642
643 pub(crate) fn webview_id(&self) -> WebViewId {
644 self.webview_id
645 }
646
647 pub(crate) fn frame_element(&self) -> Option<&Element> {
651 self.frame_element.as_deref()
652 }
653
654 pub(crate) fn document(&self) -> Option<DomRoot<Document>> {
655 self.currently_active
656 .get()
657 .and_then(ScriptThread::find_document)
658 }
659
660 pub(crate) fn parent(&self) -> Option<&WindowProxy> {
661 self.parent.as_deref()
662 }
663
664 pub(crate) fn top(&self) -> &WindowProxy {
665 let mut result = self;
666 while let Some(parent) = result.parent() {
667 result = parent;
668 }
669 result
670 }
671
672 pub fn focus(&self) {
676 debug!(
677 "Requesting the constellation to initiate a focus operation for \
678 browsing context {}",
679 self.browsing_context_id()
680 );
681 self.global()
682 .script_to_constellation_chan()
683 .send(ScriptToConstellationMessage::FocusRemoteDocument(
684 self.browsing_context_id(),
685 ))
686 .unwrap();
687 }
688
689 #[allow(unsafe_code)]
690 fn set_window(&self, window: &GlobalScope, handler: &WindowProxyHandler, _can_gc: CanGc) {
694 unsafe {
695 debug!("Setting window of {:p}.", self);
696
697 let cx = GlobalScope::get_cx();
698 let window_jsobject = window.reflector().get_jsobject();
699 let old_js_proxy = self.reflector.get_jsobject();
700 assert!(!window_jsobject.get().is_null());
701 assert_ne!(
702 ((*get_object_class(window_jsobject.get())).flags & JSCLASS_IS_GLOBAL),
703 0
704 );
705 let _ac = enter_realm(window);
706
707 SetProxyReservedSlot(old_js_proxy.get(), 0, &PrivateValue(ptr::null_mut()));
709
710 rooted!(in(*cx) let new_js_proxy = handler.new_window_proxy(&cx, window_jsobject));
716 SetProxyReservedSlot(new_js_proxy.get(), 0, &PrivateValue(ptr::null_mut()));
719 debug!(
720 "Transplanting proxy from {:p} to {:p}.",
721 old_js_proxy.get(),
722 new_js_proxy.get()
723 );
724 rooted!(in(*cx) let new_js_proxy = JS_TransplantObject(*cx, old_js_proxy, new_js_proxy.handle()));
725 debug!("Transplanted proxy is {:p}.", new_js_proxy.get());
726
727 SetProxyReservedSlot(new_js_proxy.get(), 0, &PrivateValue(self.as_void_ptr()));
729
730 SetWindowProxy(*cx, window_jsobject, new_js_proxy.handle());
732
733 debug!(
735 "Setting reflector of {:p} to {:p}.",
736 self,
737 new_js_proxy.get()
738 );
739 self.reflector.rootable().set(new_js_proxy.get());
740 }
741 }
742
743 pub(crate) fn set_currently_active(&self, window: &Window, can_gc: CanGc) {
744 if let Some(pipeline_id) = self.currently_active() {
745 if pipeline_id == window.pipeline_id() {
746 return debug!(
747 "Attempt to set the currently active window to the currently active window."
748 );
749 }
750 }
751
752 let global_scope = window.as_global_scope();
753 self.set_window(global_scope, WindowProxyHandler::proxy_handler(), can_gc);
754 self.currently_active.set(Some(global_scope.pipeline_id()));
755 }
756
757 pub(crate) fn unset_currently_active(&self, can_gc: CanGc) {
758 if self.currently_active().is_none() {
759 return debug!(
760 "Attempt to unset the currently active window on a windowproxy that does not have one."
761 );
762 }
763 let globalscope = self.global();
764 let window = DissimilarOriginWindow::new(&globalscope, self);
765 self.set_window(
766 window.upcast(),
767 WindowProxyHandler::x_origin_proxy_handler(),
768 can_gc,
769 );
770 self.currently_active.set(None);
771 }
772
773 pub(crate) fn currently_active(&self) -> Option<PipelineId> {
774 self.currently_active.get()
775 }
776
777 pub(crate) fn get_name(&self) -> DOMString {
778 self.name.borrow().clone()
779 }
780
781 pub(crate) fn set_name(&self, name: DOMString) {
782 *self.name.borrow_mut() = name;
783 }
784}
785
786#[derive(Debug, Deserialize, Serialize)]
798pub(crate) struct CreatorBrowsingContextInfo {
799 url: Option<ServoUrl>,
801
802 base_url: Option<ServoUrl>,
804
805 origin: Option<ImmutableOrigin>,
807}
808
809impl CreatorBrowsingContextInfo {
810 pub(crate) fn from(
811 parent: Option<&WindowProxy>,
812 opener: Option<&WindowProxy>,
813 ) -> CreatorBrowsingContextInfo {
814 let creator = match (parent, opener) {
815 (Some(parent), _) => parent.document(),
816 (None, Some(opener)) => opener.document(),
817 (None, None) => None,
818 };
819
820 let base_url = creator.as_deref().map(|document| document.base_url());
821 let url = creator.as_deref().map(|document| document.url());
822 let origin = creator
823 .as_deref()
824 .map(|document| document.origin().immutable().clone());
825
826 CreatorBrowsingContextInfo {
827 base_url,
828 url,
829 origin,
830 }
831 }
832}
833
834fn tokenize_open_features(features: DOMString) -> IndexMap<String, String> {
836 let is_feature_sep = |c: char| c.is_ascii_whitespace() || ['=', ','].contains(&c);
837 let mut tokenized_features = IndexMap::new();
839 let features = features.str();
841 let mut iter = features.chars();
842 let mut cur = iter.next();
843
844 while cur.is_some() {
846 let mut name = String::new();
848 let mut value = String::new();
849 while let Some(cur_char) = cur {
851 if !is_feature_sep(cur_char) {
852 break;
853 }
854 cur = iter.next();
855 }
856 while let Some(cur_char) = cur {
858 if is_feature_sep(cur_char) {
859 break;
860 }
861 name.push(cur_char.to_ascii_lowercase());
862 cur = iter.next();
863 }
864 let normalized_name = String::from(match name.as_ref() {
866 "screenx" => "left",
867 "screeny" => "top",
868 "innerwidth" => "width",
869 "innerheight" => "height",
870 _ => name.as_ref(),
871 });
872 while let Some(cur_char) = cur {
874 if cur_char == '=' || cur_char == ',' || !is_feature_sep(cur_char) {
875 break;
876 }
877 cur = iter.next();
878 }
879 if cur.is_some() && is_feature_sep(cur.unwrap()) {
881 while let Some(cur_char) = cur {
883 if !is_feature_sep(cur_char) || cur_char == ',' {
884 break;
885 }
886 cur = iter.next();
887 }
888 while let Some(cur_char) = cur {
890 if is_feature_sep(cur_char) {
891 break;
892 }
893 value.push(cur_char.to_ascii_lowercase());
894 cur = iter.next();
895 }
896 }
897 if !name.is_empty() {
899 tokenized_features.insert(normalized_name, value);
900 }
901 }
902 tokenized_features
904}
905
906fn parse_open_feature_boolean(tokenized_features: &IndexMap<String, String>, name: &str) -> bool {
908 if let Some(value) = tokenized_features.get(name) {
909 if value.is_empty() || value == "yes" {
911 return true;
912 }
913 if let Ok(int) = parse_integer(value.chars()) {
915 return int != 0;
916 }
917 }
918 false
920}
921
922#[allow(unsafe_code, non_snake_case)]
926unsafe fn GetSubframeWindowProxy(
927 cx: *mut JSContext,
928 proxy: RawHandleObject,
929 id: RawHandleId,
930) -> Option<(DomRoot<WindowProxy>, u32)> {
931 let index = get_array_index_from_id(Handle::from_raw(id));
932 if let Some(index) = index {
933 let mut slot = UndefinedValue();
934 GetProxyPrivate(*proxy, &mut slot);
935 rooted!(in(cx) let target = slot.to_object());
936 let script_window_proxies = ScriptThread::window_proxies();
937 if let Ok(win) = root_from_handleobject::<Window>(target.handle(), cx) {
938 let browsing_context_id = win.window_proxy().browsing_context_id();
939 let (result_sender, result_receiver) = ipc::channel().unwrap();
940
941 let _ = win.as_global_scope().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(|id| script_window_proxies.find_window_proxy(id))
953 .map(|proxy| (proxy, (JSPROP_ENUMERATE | JSPROP_READONLY) as u32));
954 } else if let Ok(win) =
955 root_from_handleobject::<DissimilarOriginWindow>(target.handle(), cx)
956 {
957 let browsing_context_id = win.window_proxy().browsing_context_id();
958 let (result_sender, result_receiver) = ipc::channel().unwrap();
959
960 let _ = win.global().script_to_constellation_chan().send(
961 ScriptToConstellationMessage::GetChildBrowsingContextId(
962 browsing_context_id,
963 index as usize,
964 result_sender,
965 ),
966 );
967 return result_receiver
968 .recv()
969 .ok()
970 .and_then(|maybe_bcid| maybe_bcid)
971 .and_then(|id| script_window_proxies.find_window_proxy(id))
972 .map(|proxy| (proxy, JSPROP_READONLY as u32));
973 }
974 }
975
976 None
977}
978
979#[allow(unsafe_code, non_snake_case)]
980unsafe extern "C" fn getOwnPropertyDescriptor(
981 cx: *mut JSContext,
982 proxy: RawHandleObject,
983 id: RawHandleId,
984 desc: RawMutableHandle<PropertyDescriptor>,
985 is_none: *mut bool,
986) -> bool {
987 let window = GetSubframeWindowProxy(cx, proxy, id);
988 if let Some((window, attrs)) = window {
989 rooted!(in(cx) let mut val = UndefinedValue());
990 window.to_jsval(cx, val.handle_mut());
991 set_property_descriptor(
992 MutableHandle::from_raw(desc),
993 val.handle(),
994 attrs,
995 &mut *is_none,
996 );
997 return true;
998 }
999
1000 let mut slot = UndefinedValue();
1001 GetProxyPrivate(proxy.get(), &mut slot);
1002 rooted!(in(cx) let target = slot.to_object());
1003 JS_GetOwnPropertyDescriptorById(cx, target.handle().into(), id, desc, is_none)
1004}
1005
1006#[allow(unsafe_code, non_snake_case)]
1007unsafe extern "C" fn defineProperty(
1008 cx: *mut JSContext,
1009 proxy: RawHandleObject,
1010 id: RawHandleId,
1011 desc: RawHandle<PropertyDescriptor>,
1012 res: *mut ObjectOpResult,
1013) -> bool {
1014 if get_array_index_from_id(Handle::from_raw(id)).is_some() {
1015 (*res).code_ = JSErrNum::JSMSG_CANT_DEFINE_WINDOW_ELEMENT as ::libc::uintptr_t;
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 JS_DefinePropertyById(cx, target.handle().into(), id, desc, res)
1027}
1028
1029#[allow(unsafe_code)]
1030unsafe extern "C" fn has(
1031 cx: *mut JSContext,
1032 proxy: RawHandleObject,
1033 id: RawHandleId,
1034 bp: *mut bool,
1035) -> bool {
1036 let window = GetSubframeWindowProxy(cx, proxy, id);
1037 if window.is_some() {
1038 *bp = true;
1039 return true;
1040 }
1041
1042 let mut slot = UndefinedValue();
1043 GetProxyPrivate(*proxy.ptr, &mut slot);
1044 rooted!(in(cx) let target = slot.to_object());
1045 let mut found = false;
1046 if !JS_HasPropertyById(cx, target.handle().into(), id, &mut found) {
1047 return false;
1048 }
1049
1050 *bp = found;
1051 true
1052}
1053
1054#[allow(unsafe_code)]
1055unsafe extern "C" fn get(
1056 cx: *mut JSContext,
1057 proxy: RawHandleObject,
1058 receiver: RawHandleValue,
1059 id: RawHandleId,
1060 vp: RawMutableHandleValue,
1061) -> bool {
1062 let window = GetSubframeWindowProxy(cx, proxy, id);
1063 if let Some((window, _attrs)) = window {
1064 window.to_jsval(cx, MutableHandle::from_raw(vp));
1065 return true;
1066 }
1067
1068 let mut slot = UndefinedValue();
1069 GetProxyPrivate(*proxy.ptr, &mut slot);
1070 rooted!(in(cx) let target = slot.to_object());
1071 JS_ForwardGetPropertyTo(cx, target.handle().into(), id, receiver, vp)
1072}
1073
1074#[allow(unsafe_code)]
1075unsafe extern "C" fn set(
1076 cx: *mut JSContext,
1077 proxy: RawHandleObject,
1078 id: RawHandleId,
1079 v: RawHandleValue,
1080 receiver: RawHandleValue,
1081 res: *mut ObjectOpResult,
1082) -> bool {
1083 if get_array_index_from_id(Handle::from_raw(id)).is_some() {
1084 (*res).code_ = JSErrNum::JSMSG_READ_ONLY as ::libc::uintptr_t;
1086 return true;
1087 }
1088
1089 let mut slot = UndefinedValue();
1090 GetProxyPrivate(*proxy.ptr, &mut slot);
1091 rooted!(in(cx) let target = slot.to_object());
1092 JS_ForwardSetPropertyTo(cx, target.handle().into(), id, v, receiver, res)
1093}
1094
1095#[allow(unsafe_code)]
1096unsafe extern "C" fn get_prototype_if_ordinary(
1097 _: *mut JSContext,
1098 _: RawHandleObject,
1099 is_ordinary: *mut bool,
1100 _: RawMutableHandleObject,
1101) -> bool {
1102 *is_ordinary = false;
1115 true
1116}
1117
1118static PROXY_TRAPS: ProxyTraps = ProxyTraps {
1119 enter: None,
1122 getOwnPropertyDescriptor: Some(getOwnPropertyDescriptor),
1123 defineProperty: Some(defineProperty),
1124 ownPropertyKeys: None,
1125 delete_: None,
1126 enumerate: None,
1127 getPrototypeIfOrdinary: Some(get_prototype_if_ordinary),
1128 getPrototype: None, setPrototype: None,
1130 setImmutablePrototype: None,
1131 preventExtensions: None,
1132 isExtensible: None,
1133 has: Some(has),
1134 get: Some(get),
1135 set: Some(set),
1136 call: None,
1137 construct: None,
1138 hasOwn: None,
1139 getOwnEnumerablePropertyKeys: None,
1140 nativeCall: None,
1141 objectClassIs: None,
1142 className: None,
1143 fun_toString: None,
1144 boxedValue_unbox: None,
1145 defaultValue: None,
1146 trace: Some(trace),
1147 finalize: Some(finalize),
1148 objectMoved: None,
1149 isCallable: None,
1150 isConstructor: None,
1151};
1152
1153pub(crate) struct WindowProxyHandler(*const libc::c_void);
1156
1157impl MallocSizeOf for WindowProxyHandler {
1158 fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
1159 0
1161 }
1162}
1163
1164#[allow(unsafe_code)]
1166unsafe impl Send for WindowProxyHandler {}
1167#[allow(unsafe_code)]
1169unsafe impl Sync for WindowProxyHandler {}
1170
1171#[allow(unsafe_code)]
1172impl WindowProxyHandler {
1173 fn new(traps: &ProxyTraps) -> Self {
1174 let ptr = unsafe { CreateWrapperProxyHandler(traps) };
1176 assert!(!ptr.is_null());
1177 Self(ptr)
1178 }
1179
1180 pub(crate) fn x_origin_proxy_handler() -> &'static Self {
1182 use std::sync::OnceLock;
1183 static SINGLETON: OnceLock<WindowProxyHandler> = OnceLock::new();
1189 SINGLETON.get_or_init(|| Self::new(&XORIGIN_PROXY_TRAPS))
1190 }
1191
1192 pub(crate) fn proxy_handler() -> &'static Self {
1194 use std::sync::OnceLock;
1195 static SINGLETON: OnceLock<WindowProxyHandler> = OnceLock::new();
1201 SINGLETON.get_or_init(|| Self::new(&PROXY_TRAPS))
1202 }
1203
1204 pub(crate) fn new_window_proxy(
1207 &self,
1208 cx: &crate::script_runtime::JSContext,
1209 window_jsobject: js::gc::HandleObject,
1210 ) -> *mut JSObject {
1211 let obj = unsafe { NewWindowProxy(**cx, window_jsobject, self.0) };
1212 assert!(!obj.is_null());
1213 obj
1214 }
1215}
1216
1217#[allow(unsafe_code)]
1218impl Drop for WindowProxyHandler {
1219 fn drop(&mut self) {
1220 unsafe {
1223 DeleteWrapperProxyHandler(self.0);
1224 }
1225 }
1226}
1227
1228#[allow(unsafe_code)]
1236unsafe fn throw_security_error(cx: *mut JSContext, realm: InRealm) -> bool {
1237 if !JS_IsExceptionPending(cx) {
1238 let safe_context = SafeJSContext::from_ptr(cx);
1239 let global = GlobalScope::from_context(cx, realm);
1240 throw_dom_exception(safe_context, &global, Error::Security, CanGc::note());
1241 }
1242 false
1243}
1244
1245#[allow(unsafe_code)]
1246unsafe extern "C" fn has_xorigin(
1247 cx: *mut JSContext,
1248 proxy: RawHandleObject,
1249 id: RawHandleId,
1250 bp: *mut bool,
1251) -> bool {
1252 let mut slot = UndefinedValue();
1253 GetProxyPrivate(*proxy.ptr, &mut slot);
1254 rooted!(in(cx) let target = slot.to_object());
1255 let mut found = false;
1256 JS_HasOwnPropertyById(cx, target.handle().into(), id, &mut found);
1257 if found {
1258 *bp = true;
1259 true
1260 } else {
1261 let in_realm_proof = AlreadyInRealm::assert_for_cx(SafeJSContext::from_ptr(cx));
1262 throw_security_error(cx, InRealm::Already(&in_realm_proof))
1263 }
1264}
1265
1266#[allow(unsafe_code)]
1267unsafe extern "C" fn get_xorigin(
1268 cx: *mut JSContext,
1269 proxy: RawHandleObject,
1270 receiver: RawHandleValue,
1271 id: RawHandleId,
1272 vp: RawMutableHandleValue,
1273) -> bool {
1274 let mut found = false;
1275 has_xorigin(cx, proxy, id, &mut found);
1276 found && get(cx, proxy, receiver, id, vp)
1277}
1278
1279#[allow(unsafe_code)]
1280unsafe extern "C" fn set_xorigin(
1281 cx: *mut JSContext,
1282 _: RawHandleObject,
1283 _: RawHandleId,
1284 _: RawHandleValue,
1285 _: RawHandleValue,
1286 _: *mut ObjectOpResult,
1287) -> bool {
1288 let in_realm_proof = AlreadyInRealm::assert_for_cx(SafeJSContext::from_ptr(cx));
1289 throw_security_error(cx, InRealm::Already(&in_realm_proof))
1290}
1291
1292#[allow(unsafe_code)]
1293unsafe extern "C" fn delete_xorigin(
1294 cx: *mut JSContext,
1295 _: RawHandleObject,
1296 _: RawHandleId,
1297 _: *mut ObjectOpResult,
1298) -> bool {
1299 let in_realm_proof = AlreadyInRealm::assert_for_cx(SafeJSContext::from_ptr(cx));
1300 throw_security_error(cx, InRealm::Already(&in_realm_proof))
1301}
1302
1303#[allow(unsafe_code, non_snake_case)]
1304unsafe extern "C" fn getOwnPropertyDescriptor_xorigin(
1305 cx: *mut JSContext,
1306 proxy: RawHandleObject,
1307 id: RawHandleId,
1308 desc: RawMutableHandle<PropertyDescriptor>,
1309 is_none: *mut bool,
1310) -> bool {
1311 let mut found = false;
1312 has_xorigin(cx, proxy, id, &mut found);
1313 found && getOwnPropertyDescriptor(cx, proxy, id, desc, is_none)
1314}
1315
1316#[allow(unsafe_code, non_snake_case)]
1317unsafe extern "C" fn defineProperty_xorigin(
1318 cx: *mut JSContext,
1319 _: RawHandleObject,
1320 _: RawHandleId,
1321 _: RawHandle<PropertyDescriptor>,
1322 _: *mut ObjectOpResult,
1323) -> bool {
1324 let in_realm_proof = AlreadyInRealm::assert_for_cx(SafeJSContext::from_ptr(cx));
1325 throw_security_error(cx, InRealm::Already(&in_realm_proof))
1326}
1327
1328#[allow(unsafe_code, non_snake_case)]
1329unsafe extern "C" fn preventExtensions_xorigin(
1330 cx: *mut JSContext,
1331 _: RawHandleObject,
1332 _: *mut ObjectOpResult,
1333) -> bool {
1334 let in_realm_proof = AlreadyInRealm::assert_for_cx(SafeJSContext::from_ptr(cx));
1335 throw_security_error(cx, InRealm::Already(&in_realm_proof))
1336}
1337
1338static XORIGIN_PROXY_TRAPS: ProxyTraps = ProxyTraps {
1339 enter: None,
1340 getOwnPropertyDescriptor: Some(getOwnPropertyDescriptor_xorigin),
1341 defineProperty: Some(defineProperty_xorigin),
1342 ownPropertyKeys: None,
1343 delete_: Some(delete_xorigin),
1344 enumerate: None,
1345 getPrototypeIfOrdinary: None,
1346 getPrototype: None,
1347 setPrototype: None,
1348 setImmutablePrototype: None,
1349 preventExtensions: Some(preventExtensions_xorigin),
1350 isExtensible: None,
1351 has: Some(has_xorigin),
1352 get: Some(get_xorigin),
1353 set: Some(set_xorigin),
1354 call: None,
1355 construct: None,
1356 hasOwn: Some(has_xorigin),
1357 getOwnEnumerablePropertyKeys: None,
1358 nativeCall: None,
1359 objectClassIs: None,
1360 className: None,
1361 fun_toString: None,
1362 boxedValue_unbox: None,
1363 defaultValue: None,
1364 trace: Some(trace),
1365 finalize: Some(finalize),
1366 objectMoved: None,
1367 isCallable: None,
1368 isConstructor: None,
1369};
1370
1371#[allow(unsafe_code)]
1374unsafe extern "C" fn finalize(_fop: *mut GCContext, obj: *mut JSObject) {
1375 let mut slot = UndefinedValue();
1376 GetProxyReservedSlot(obj, 0, &mut slot);
1377 let this = slot.to_private() as *mut WindowProxy;
1378 if this.is_null() {
1379 return;
1381 }
1382 let jsobject = (*this).reflector.get_jsobject().get();
1383 debug!(
1384 "WindowProxy finalize: {:p}, with reflector {:p} from {:p}.",
1385 this, jsobject, obj
1386 );
1387 let _ = Box::from_raw(this);
1388}
1389
1390#[allow(unsafe_code)]
1391unsafe extern "C" fn trace(trc: *mut JSTracer, obj: *mut JSObject) {
1392 let mut slot = UndefinedValue();
1393 GetProxyReservedSlot(obj, 0, &mut slot);
1394 let this = slot.to_private() as *const WindowProxy;
1395 if this.is_null() {
1396 return;
1398 }
1399 (*this).trace(trc);
1400}