| /* This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ |
| |
| use std::cell::Cell; |
| use std::ptr; |
| |
| use base::generic_channel; |
| use base::generic_channel::GenericSend; |
| use base::id::{BrowsingContextId, PipelineId, WebViewId}; |
| use constellation_traits::{ |
| AuxiliaryWebViewCreationRequest, LoadData, LoadOrigin, NavigationHistoryBehavior, |
| ScriptToConstellationMessage, |
| }; |
| use dom_struct::dom_struct; |
| use html5ever::local_name; |
| use indexmap::map::IndexMap; |
| use ipc_channel::ipc; |
| use js::JSCLASS_IS_GLOBAL; |
| use js::glue::{ |
| CreateWrapperProxyHandler, DeleteWrapperProxyHandler, GetProxyPrivate, GetProxyReservedSlot, |
| ProxyTraps, SetProxyReservedSlot, |
| }; |
| use js::jsapi::{ |
| GCContext, Handle as RawHandle, HandleId as RawHandleId, HandleObject as RawHandleObject, |
| HandleValue as RawHandleValue, JS_DefinePropertyById, JS_ForwardGetPropertyTo, |
| JS_ForwardSetPropertyTo, JS_GetOwnPropertyDescriptorById, JS_HasOwnPropertyById, |
| JS_HasPropertyById, JS_IsExceptionPending, JSAutoRealm, JSContext, JSErrNum, JSObject, |
| JSPROP_ENUMERATE, JSPROP_READONLY, JSTracer, MutableHandle as RawMutableHandle, |
| MutableHandleObject as RawMutableHandleObject, MutableHandleValue as RawMutableHandleValue, |
| ObjectOpResult, PropertyDescriptor, |
| }; |
| use js::jsval::{NullValue, PrivateValue, UndefinedValue}; |
| use js::rust::wrappers::{JS_TransplantObject, NewWindowProxy, SetWindowProxy}; |
| use js::rust::{Handle, MutableHandle, MutableHandleValue, get_object_class}; |
| use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; |
| use net_traits::request::Referrer; |
| use net_traits::storage_thread::StorageThreadMsg; |
| use script_traits::NewLayoutInfo; |
| use serde::{Deserialize, Serialize}; |
| use servo_url::{ImmutableOrigin, ServoUrl}; |
| use style::attr::parse_integer; |
| |
| use crate::dom::bindings::cell::DomRefCell; |
| use crate::dom::bindings::conversions::{ToJSValConvertible, root_from_handleobject}; |
| use crate::dom::bindings::error::{Error, Fallible, throw_dom_exception}; |
| use crate::dom::bindings::inheritance::Castable; |
| use crate::dom::bindings::proxyhandler::set_property_descriptor; |
| use crate::dom::bindings::reflector::{DomGlobal, DomObject, Reflector}; |
| use crate::dom::bindings::root::{Dom, DomRoot}; |
| use crate::dom::bindings::str::{DOMString, USVString}; |
| use crate::dom::bindings::trace::JSTraceable; |
| use crate::dom::bindings::utils::{AsVoidPtr, get_array_index_from_id}; |
| use crate::dom::dissimilaroriginwindow::DissimilarOriginWindow; |
| use crate::dom::document::Document; |
| use crate::dom::element::Element; |
| use crate::dom::globalscope::GlobalScope; |
| use crate::dom::window::Window; |
| use crate::realms::{AlreadyInRealm, InRealm, enter_realm}; |
| use crate::script_runtime::{CanGc, JSContext as SafeJSContext}; |
| use crate::script_thread::ScriptThread; |
| |
| #[dom_struct] |
| // NOTE: the browsing context for a window is managed in two places: |
| // here, in script, but also in the constellation. The constellation |
| // manages the session history, which in script is accessed through |
| // History objects, messaging the constellation. |
| pub(crate) struct WindowProxy { |
| /// The JS WindowProxy object. |
| /// Unlike other reflectors, we mutate this field because |
| /// we have to brain-transplant the reflector when the WindowProxy |
| /// changes Window. |
| reflector: Reflector, |
| |
| /// The id of the browsing context. |
| /// In the case that this is a nested browsing context, this is the id |
| /// of the container. |
| #[no_trace] |
| browsing_context_id: BrowsingContextId, |
| |
| // https://html.spec.whatwg.org/multipage/#opener-browsing-context |
| #[no_trace] |
| opener: Option<BrowsingContextId>, |
| |
| /// The frame id of the top-level ancestor browsing context. |
| /// In the case that this is a top-level window, this is our id. |
| #[no_trace] |
| webview_id: WebViewId, |
| |
| /// The name of the browsing context (sometimes, but not always, |
| /// equal to the name of a container element) |
| name: DomRefCell<DOMString>, |
| /// The pipeline id of the currently active document. |
| /// May be None, when the currently active document is in another script thread. |
| /// We do not try to keep the pipeline id for documents in other threads, |
| /// as this would require the constellation notifying many script threads about |
| /// the change, which could be expensive. |
| #[no_trace] |
| currently_active: Cell<Option<PipelineId>>, |
| |
| /// Has the browsing context been discarded? |
| discarded: Cell<bool>, |
| |
| /// Has the browsing context been disowned? |
| disowned: Cell<bool>, |
| |
| /// <https://html.spec.whatwg.org/multipage/#is-closing> |
| is_closing: Cell<bool>, |
| |
| /// The containing iframe element, if this is a same-origin iframe |
| frame_element: Option<Dom<Element>>, |
| |
| /// The parent browsing context's window proxy, if this is a nested browsing context |
| parent: Option<Dom<WindowProxy>>, |
| |
| /// <https://html.spec.whatwg.org/multipage/#delaying-load-events-mode> |
| delaying_load_events_mode: Cell<bool>, |
| |
| /// The creator browsing context's base url. |
| #[no_trace] |
| creator_base_url: Option<ServoUrl>, |
| |
| /// The creator browsing context's url. |
| #[no_trace] |
| creator_url: Option<ServoUrl>, |
| |
| /// The creator browsing context's origin. |
| #[no_trace] |
| creator_origin: Option<ImmutableOrigin>, |
| } |
| |
| impl WindowProxy { |
| fn new_inherited( |
| browsing_context_id: BrowsingContextId, |
| webview_id: WebViewId, |
| currently_active: Option<PipelineId>, |
| frame_element: Option<&Element>, |
| parent: Option<&WindowProxy>, |
| opener: Option<BrowsingContextId>, |
| creator: CreatorBrowsingContextInfo, |
| ) -> WindowProxy { |
| let name = frame_element.map_or(DOMString::new(), |e| { |
| e.get_string_attribute(&local_name!("name")) |
| }); |
| WindowProxy { |
| reflector: Reflector::new(), |
| browsing_context_id, |
| webview_id, |
| name: DomRefCell::new(name), |
| currently_active: Cell::new(currently_active), |
| discarded: Cell::new(false), |
| disowned: Cell::new(false), |
| is_closing: Cell::new(false), |
| frame_element: frame_element.map(Dom::from_ref), |
| parent: parent.map(Dom::from_ref), |
| delaying_load_events_mode: Cell::new(false), |
| opener, |
| creator_base_url: creator.base_url, |
| creator_url: creator.url, |
| creator_origin: creator.origin, |
| } |
| } |
| |
| #[allow(unsafe_code)] |
| pub(crate) fn new( |
| window: &Window, |
| browsing_context_id: BrowsingContextId, |
| webview_id: WebViewId, |
| frame_element: Option<&Element>, |
| parent: Option<&WindowProxy>, |
| opener: Option<BrowsingContextId>, |
| creator: CreatorBrowsingContextInfo, |
| ) -> DomRoot<WindowProxy> { |
| unsafe { |
| let handler = window.windowproxy_handler(); |
| |
| let cx = GlobalScope::get_cx(); |
| let window_jsobject = window.reflector().get_jsobject(); |
| assert!(!window_jsobject.get().is_null()); |
| assert_ne!( |
| ((*get_object_class(window_jsobject.get())).flags & JSCLASS_IS_GLOBAL), |
| 0 |
| ); |
| let _ac = JSAutoRealm::new(*cx, window_jsobject.get()); |
| |
| // Create a new window proxy. |
| rooted!(in(*cx) let js_proxy = handler.new_window_proxy(&cx, window_jsobject)); |
| assert!(!js_proxy.is_null()); |
| |
| // Create a new browsing context. |
| |
| let current = Some(window.upcast::<GlobalScope>().pipeline_id()); |
| let window_proxy = Box::new(WindowProxy::new_inherited( |
| browsing_context_id, |
| webview_id, |
| current, |
| frame_element, |
| parent, |
| opener, |
| creator, |
| )); |
| |
| // The window proxy owns the browsing context. |
| // When we finalize the window proxy, it drops the browsing context it owns. |
| SetProxyReservedSlot( |
| js_proxy.get(), |
| 0, |
| &PrivateValue((*window_proxy).as_void_ptr()), |
| ); |
| |
| // Notify the JS engine about the new window proxy binding. |
| SetWindowProxy(*cx, window_jsobject, js_proxy.handle()); |
| |
| // Set the reflector. |
| debug!( |
| "Initializing reflector of {:p} to {:p}.", |
| window_proxy, |
| js_proxy.get() |
| ); |
| window_proxy.reflector.set_jsobject(js_proxy.get()); |
| DomRoot::from_ref(&*Box::into_raw(window_proxy)) |
| } |
| } |
| |
| #[allow(unsafe_code)] |
| pub(crate) fn new_dissimilar_origin( |
| global_to_clone_from: &GlobalScope, |
| browsing_context_id: BrowsingContextId, |
| webview_id: WebViewId, |
| parent: Option<&WindowProxy>, |
| opener: Option<BrowsingContextId>, |
| creator: CreatorBrowsingContextInfo, |
| ) -> DomRoot<WindowProxy> { |
| unsafe { |
| let handler = WindowProxyHandler::x_origin_proxy_handler(); |
| |
| let cx = GlobalScope::get_cx(); |
| |
| // Create a new browsing context. |
| let window_proxy = Box::new(WindowProxy::new_inherited( |
| browsing_context_id, |
| webview_id, |
| None, |
| None, |
| parent, |
| opener, |
| creator, |
| )); |
| |
| // Create a new dissimilar-origin window. |
| let window = DissimilarOriginWindow::new(global_to_clone_from, &window_proxy); |
| let window_jsobject = window.reflector().get_jsobject(); |
| assert!(!window_jsobject.get().is_null()); |
| assert_ne!( |
| ((*get_object_class(window_jsobject.get())).flags & JSCLASS_IS_GLOBAL), |
| 0 |
| ); |
| let _ac = JSAutoRealm::new(*cx, window_jsobject.get()); |
| |
| // Create a new window proxy. |
| rooted!(in(*cx) let js_proxy = handler.new_window_proxy(&cx, window_jsobject)); |
| assert!(!js_proxy.is_null()); |
| |
| // The window proxy owns the browsing context. |
| // When we finalize the window proxy, it drops the browsing context it owns. |
| SetProxyReservedSlot( |
| js_proxy.get(), |
| 0, |
| &PrivateValue((*window_proxy).as_void_ptr()), |
| ); |
| |
| // Notify the JS engine about the new window proxy binding. |
| SetWindowProxy(*cx, window_jsobject, js_proxy.handle()); |
| |
| // Set the reflector. |
| debug!( |
| "Initializing reflector of {:p} to {:p}.", |
| window_proxy, |
| js_proxy.get() |
| ); |
| window_proxy.reflector.set_jsobject(js_proxy.get()); |
| DomRoot::from_ref(&*Box::into_raw(window_proxy)) |
| } |
| } |
| |
| // https://html.spec.whatwg.org/multipage/#auxiliary-browsing-context |
| fn create_auxiliary_browsing_context( |
| &self, |
| name: DOMString, |
| noopener: bool, |
| ) -> Option<DomRoot<WindowProxy>> { |
| let (response_sender, response_receiver) = ipc::channel().unwrap(); |
| let window = self |
| .currently_active |
| .get() |
| .and_then(ScriptThread::find_document) |
| .map(|doc| DomRoot::from_ref(doc.window())) |
| .unwrap(); |
| |
| let document = self |
| .currently_active |
| .get() |
| .and_then(ScriptThread::find_document) |
| .expect("A WindowProxy creating an auxiliary to have an active document"); |
| let blank_url = ServoUrl::parse("about:blank").ok().unwrap(); |
| let load_data = LoadData::new( |
| LoadOrigin::Script(document.origin().immutable().clone()), |
| blank_url, |
| None, |
| document.global().get_referrer(), |
| document.get_referrer_policy(), |
| None, // Doesn't inherit secure context |
| None, |
| false, |
| ); |
| let load_info = AuxiliaryWebViewCreationRequest { |
| load_data: load_data.clone(), |
| opener_webview_id: window.webview_id(), |
| opener_pipeline_id: self.currently_active.get().unwrap(), |
| response_sender, |
| }; |
| let constellation_msg = ScriptToConstellationMessage::CreateAuxiliaryWebView(load_info); |
| window.send_to_constellation(constellation_msg); |
| |
| let response = response_receiver.recv().unwrap()?; |
| let new_browsing_context_id = BrowsingContextId::from(response.new_webview_id); |
| let new_layout_info = NewLayoutInfo { |
| parent_info: None, |
| new_pipeline_id: response.new_pipeline_id, |
| browsing_context_id: new_browsing_context_id, |
| webview_id: response.new_webview_id, |
| opener: Some(self.browsing_context_id), |
| load_data, |
| viewport_details: window.viewport_details(), |
| // Use the current `WebView`'s theme initially, but the embedder may |
| // change this later. |
| theme: window.theme(), |
| }; |
| ScriptThread::process_attach_layout(new_layout_info, document.origin().clone()); |
| let new_window_proxy = ScriptThread::find_document(response.new_pipeline_id) |
| .and_then(|doc| doc.browsing_context())?; |
| if name.to_lowercase() != "_blank" { |
| new_window_proxy.set_name(name); |
| } |
| if noopener { |
| new_window_proxy.disown(); |
| } else { |
| // After creating a new auxiliary browsing context and document, |
| // the session storage is copied over. |
| // See https://html.spec.whatwg.org/multipage/#the-sessionstorage-attribute |
| |
| let (sender, receiver) = generic_channel::channel().unwrap(); |
| |
| let msg = StorageThreadMsg::Clone { |
| sender, |
| src: window.window_proxy().webview_id(), |
| dest: response.new_webview_id, |
| }; |
| |
| GenericSend::send(document.global().resource_threads(), msg).unwrap(); |
| receiver.recv().unwrap(); |
| } |
| Some(new_window_proxy) |
| } |
| |
| /// <https://html.spec.whatwg.org/multipage/#delaying-load-events-mode> |
| pub(crate) fn is_delaying_load_events_mode(&self) -> bool { |
| self.delaying_load_events_mode.get() |
| } |
| |
| /// <https://html.spec.whatwg.org/multipage/#delaying-load-events-mode> |
| pub(crate) fn start_delaying_load_events_mode(&self) { |
| self.delaying_load_events_mode.set(true); |
| } |
| |
| /// <https://html.spec.whatwg.org/multipage/#delaying-load-events-mode> |
| pub(crate) fn stop_delaying_load_events_mode(&self) { |
| self.delaying_load_events_mode.set(false); |
| if let Some(document) = self.document() { |
| if !document.loader().events_inhibited() { |
| ScriptThread::mark_document_with_no_blocked_loads(&document); |
| } |
| } |
| } |
| |
| // https://html.spec.whatwg.org/multipage/#disowned-its-opener |
| pub(crate) fn disown(&self) { |
| self.disowned.set(true); |
| } |
| |
| /// <https://html.spec.whatwg.org/multipage/#dom-window-close> |
| /// Step 3.1, set BCs `is_closing` to true. |
| pub(crate) fn close(&self) { |
| self.is_closing.set(true); |
| } |
| |
| /// <https://html.spec.whatwg.org/multipage/#is-closing> |
| pub(crate) fn is_closing(&self) -> bool { |
| self.is_closing.get() |
| } |
| |
| /// <https://html.spec.whatwg.org/multipage/#creator-base-url> |
| pub(crate) fn creator_base_url(&self) -> Option<ServoUrl> { |
| self.creator_base_url.clone() |
| } |
| |
| pub(crate) fn has_creator_base_url(&self) -> bool { |
| self.creator_base_url.is_some() |
| } |
| |
| /// <https://html.spec.whatwg.org/multipage/#creator-url> |
| pub(crate) fn creator_url(&self) -> Option<ServoUrl> { |
| self.creator_url.clone() |
| } |
| |
| pub(crate) fn has_creator_url(&self) -> bool { |
| self.creator_base_url.is_some() |
| } |
| |
| /// <https://html.spec.whatwg.org/multipage/#creator-origin> |
| pub(crate) fn creator_origin(&self) -> Option<ImmutableOrigin> { |
| self.creator_origin.clone() |
| } |
| |
| pub(crate) fn has_creator_origin(&self) -> bool { |
| self.creator_origin.is_some() |
| } |
| |
| #[allow(unsafe_code)] |
| // https://html.spec.whatwg.org/multipage/#dom-opener |
| pub(crate) fn opener( |
| &self, |
| cx: *mut JSContext, |
| in_realm_proof: InRealm, |
| mut retval: MutableHandleValue, |
| ) { |
| if self.disowned.get() { |
| return retval.set(NullValue()); |
| } |
| let opener_id = match self.opener { |
| Some(opener_browsing_context_id) => opener_browsing_context_id, |
| None => return retval.set(NullValue()), |
| }; |
| let parent_browsing_context = self.parent.as_deref(); |
| let opener_proxy = match ScriptThread::find_window_proxy(opener_id) { |
| Some(window_proxy) => window_proxy, |
| None => { |
| let sender_pipeline_id = self.currently_active().unwrap(); |
| match ScriptThread::get_top_level_for_browsing_context( |
| sender_pipeline_id, |
| opener_id, |
| ) { |
| Some(opener_top_id) => { |
| let global_to_clone_from = |
| unsafe { GlobalScope::from_context(cx, in_realm_proof) }; |
| let creator = |
| CreatorBrowsingContextInfo::from(parent_browsing_context, None); |
| WindowProxy::new_dissimilar_origin( |
| &global_to_clone_from, |
| opener_id, |
| opener_top_id, |
| None, |
| None, |
| creator, |
| ) |
| }, |
| None => return retval.set(NullValue()), |
| } |
| }, |
| }; |
| if opener_proxy.is_browsing_context_discarded() { |
| return retval.set(NullValue()); |
| } |
| unsafe { opener_proxy.to_jsval(cx, retval) }; |
| } |
| |
| // https://html.spec.whatwg.org/multipage/#window-open-steps |
| pub(crate) fn open( |
| &self, |
| url: USVString, |
| target: DOMString, |
| features: DOMString, |
| can_gc: CanGc, |
| ) -> Fallible<Option<DomRoot<WindowProxy>>> { |
| // Step 5. If target is the empty string, then set target to "_blank". |
| let non_empty_target = match target.as_ref() { |
| "" => DOMString::from("_blank"), |
| _ => target, |
| }; |
| // Step 6. Let tokenizedFeatures be the result of tokenizing features. |
| let tokenized_features = tokenize_open_features(features); |
| // Step 7 - 8. |
| // If tokenizedFeatures["noreferrer"] exists, then set noreferrer to |
| // the result of parsing tokenizedFeatures["noreferrer"] as a boolean feature. |
| let noreferrer = parse_open_feature_boolean(&tokenized_features, "noreferrer"); |
| |
| // Step 9. Let noopener be the result of getting noopener for window |
| // open with sourceDocument, tokenizedFeatures, and urlRecord. |
| let noopener = if noreferrer { |
| true |
| } else { |
| parse_open_feature_boolean(&tokenized_features, "noopener") |
| }; |
| // (TODO) Step 10. Remove tokenizedFeatures["noopener"] and tokenizedFeatures["noreferrer"]. |
| |
| // (TODO) Step 11. Let referrerPolicy be the empty string. |
| // (TODO) Step 12. If noreferrer is true, then set referrerPolicy to "no-referrer". |
| |
| // Step 13 - 14 |
| // Let targetNavigable and windowType be the result of applying the rules for |
| // choosing a navigable given target, sourceDocument's node navigable, and noopener. |
| // If targetNavigable is null, then return null. |
| let (chosen, new) = match self.choose_browsing_context(non_empty_target, noopener) { |
| (Some(chosen), new) => (chosen, new), |
| (None, _) => return Ok(None), |
| }; |
| // TODO Step 15.2, Set up browsing context features for targetNavigable's |
| // active browsing context given tokenizedFeatures. |
| let target_document = match chosen.document() { |
| Some(target_document) => target_document, |
| None => return Ok(None), |
| }; |
| let has_trustworthy_ancestor_origin = if new { |
| target_document.has_trustworthy_ancestor_or_current_origin() |
| } else { |
| false |
| }; |
| let target_window = target_document.window(); |
| // Step 15.3 and 15.4 will have happened elsewhere, |
| // since we've created a new browsing context and loaded it with about:blank. |
| if !url.is_empty() { |
| let existing_document = self |
| .currently_active |
| .get() |
| .and_then(ScriptThread::find_document) |
| .unwrap(); |
| let url = match existing_document.url().join(&url) { |
| Ok(url) => url, |
| Err(_) => return Err(Error::Syntax(None)), |
| }; |
| let referrer = if noreferrer { |
| Referrer::NoReferrer |
| } else { |
| target_window.as_global_scope().get_referrer() |
| }; |
| // Step 15.5 Otherwise, navigate targetNavigable to urlRecord using sourceDocument, |
| // with referrerPolicy set to referrerPolicy and exceptionsEnabled set to true. |
| // FIXME: referrerPolicy may not be used properly here. exceptionsEnabled not used. |
| let referrer_policy = target_document.get_referrer_policy(); |
| let pipeline_id = target_window.pipeline_id(); |
| let secure = target_window.as_global_scope().is_secure_context(); |
| let load_data = LoadData::new( |
| LoadOrigin::Script(existing_document.origin().immutable().clone()), |
| url, |
| Some(pipeline_id), |
| referrer, |
| referrer_policy, |
| Some(secure), |
| Some(target_document.insecure_requests_policy()), |
| has_trustworthy_ancestor_origin, |
| ); |
| let history_handling = if new { |
| NavigationHistoryBehavior::Replace |
| } else { |
| NavigationHistoryBehavior::Push |
| }; |
| target_window.load_url(history_handling, false, load_data, can_gc); |
| } |
| // Step 17 (Dis-owning has been done in create_auxiliary_browsing_context). |
| if noopener { |
| return Ok(None); |
| } |
| // Step 18 |
| Ok(target_document.browsing_context()) |
| } |
| |
| // https://html.spec.whatwg.org/multipage/#the-rules-for-choosing-a-browsing-context-given-a-browsing-context-name |
| pub(crate) fn choose_browsing_context( |
| &self, |
| name: DOMString, |
| noopener: bool, |
| ) -> (Option<DomRoot<WindowProxy>>, bool) { |
| match name.to_lowercase().as_ref() { |
| "" | "_self" => { |
| // Step 3. |
| (Some(DomRoot::from_ref(self)), false) |
| }, |
| "_parent" => { |
| // Step 4 |
| if let Some(parent) = self.parent() { |
| return (Some(DomRoot::from_ref(parent)), false); |
| } |
| (None, false) |
| }, |
| "_top" => { |
| // Step 5 |
| (Some(DomRoot::from_ref(self.top())), false) |
| }, |
| "_blank" => (self.create_auxiliary_browsing_context(name, noopener), true), |
| _ => { |
| // Step 6. |
| // TODO: expand the search to all 'familiar' bc, |
| // including auxiliaries familiar by way of their opener. |
| // See https://html.spec.whatwg.org/multipage/#familiar-with |
| match ScriptThread::find_window_proxy_by_name(&name) { |
| Some(proxy) => (Some(proxy), false), |
| None => (self.create_auxiliary_browsing_context(name, noopener), true), |
| } |
| }, |
| } |
| } |
| |
| pub(crate) fn is_auxiliary(&self) -> bool { |
| self.opener.is_some() |
| } |
| |
| pub(crate) fn discard_browsing_context(&self) { |
| self.discarded.set(true); |
| } |
| |
| pub(crate) fn is_browsing_context_discarded(&self) -> bool { |
| self.discarded.get() |
| } |
| |
| pub(crate) fn browsing_context_id(&self) -> BrowsingContextId { |
| self.browsing_context_id |
| } |
| |
| pub(crate) fn webview_id(&self) -> WebViewId { |
| self.webview_id |
| } |
| |
| pub(crate) fn frame_element(&self) -> Option<&Element> { |
| self.frame_element.as_deref() |
| } |
| |
| pub(crate) fn document(&self) -> Option<DomRoot<Document>> { |
| self.currently_active |
| .get() |
| .and_then(ScriptThread::find_document) |
| } |
| |
| pub(crate) fn parent(&self) -> Option<&WindowProxy> { |
| self.parent.as_deref() |
| } |
| |
| pub(crate) fn top(&self) -> &WindowProxy { |
| let mut result = self; |
| while let Some(parent) = result.parent() { |
| result = parent; |
| } |
| result |
| } |
| |
| /// Run [the focusing steps] with this browsing context. |
| /// |
| /// [the focusing steps]: https://html.spec.whatwg.org/multipage/#focusing-steps |
| pub fn focus(&self) { |
| debug!( |
| "Requesting the constellation to initiate a focus operation for \ |
| browsing context {}", |
| self.browsing_context_id() |
| ); |
| self.global() |
| .script_to_constellation_chan() |
| .send(ScriptToConstellationMessage::FocusRemoteDocument( |
| self.browsing_context_id(), |
| )) |
| .unwrap(); |
| } |
| |
| #[allow(unsafe_code)] |
| /// Change the Window that this WindowProxy resolves to. |
| // TODO: support setting the window proxy to a dummy value, |
| // to handle the case when the active document is in another script thread. |
| fn set_window(&self, window: &GlobalScope, handler: &WindowProxyHandler, _can_gc: CanGc) { |
| unsafe { |
| debug!("Setting window of {:p}.", self); |
| |
| let cx = GlobalScope::get_cx(); |
| let window_jsobject = window.reflector().get_jsobject(); |
| let old_js_proxy = self.reflector.get_jsobject(); |
| assert!(!window_jsobject.get().is_null()); |
| assert_ne!( |
| ((*get_object_class(window_jsobject.get())).flags & JSCLASS_IS_GLOBAL), |
| 0 |
| ); |
| let _ac = enter_realm(window); |
| |
| // The old window proxy no longer owns this browsing context. |
| SetProxyReservedSlot(old_js_proxy.get(), 0, &PrivateValue(ptr::null_mut())); |
| |
| // Brain transplant the window proxy. Brain transplantation is |
| // usually done to move a window proxy between compartments, but |
| // that's not what we are doing here. We need to do this just |
| // because we want to replace the wrapper's `ProxyTraps`, but we |
| // don't want to update its identity. |
| rooted!(in(*cx) let new_js_proxy = handler.new_window_proxy(&cx, window_jsobject)); |
| // Explicitly set this slot to a null pointer in case a GC occurs before we |
| // are ready to set it to a real value. |
| SetProxyReservedSlot(new_js_proxy.get(), 0, &PrivateValue(ptr::null_mut())); |
| debug!( |
| "Transplanting proxy from {:p} to {:p}.", |
| old_js_proxy.get(), |
| new_js_proxy.get() |
| ); |
| rooted!(in(*cx) let new_js_proxy = JS_TransplantObject(*cx, old_js_proxy, new_js_proxy.handle())); |
| debug!("Transplanted proxy is {:p}.", new_js_proxy.get()); |
| |
| // Transfer ownership of this browsing context from the old window proxy to the new one. |
| SetProxyReservedSlot(new_js_proxy.get(), 0, &PrivateValue(self.as_void_ptr())); |
| |
| // Notify the JS engine about the new window proxy binding. |
| SetWindowProxy(*cx, window_jsobject, new_js_proxy.handle()); |
| |
| // Update the reflector. |
| debug!( |
| "Setting reflector of {:p} to {:p}.", |
| self, |
| new_js_proxy.get() |
| ); |
| self.reflector.rootable().set(new_js_proxy.get()); |
| } |
| } |
| |
| pub(crate) fn set_currently_active(&self, window: &Window, can_gc: CanGc) { |
| if let Some(pipeline_id) = self.currently_active() { |
| if pipeline_id == window.pipeline_id() { |
| return debug!( |
| "Attempt to set the currently active window to the currently active window." |
| ); |
| } |
| } |
| |
| let global_scope = window.as_global_scope(); |
| self.set_window(global_scope, WindowProxyHandler::proxy_handler(), can_gc); |
| self.currently_active.set(Some(global_scope.pipeline_id())); |
| } |
| |
| pub(crate) fn unset_currently_active(&self, can_gc: CanGc) { |
| if self.currently_active().is_none() { |
| return debug!( |
| "Attempt to unset the currently active window on a windowproxy that does not have one." |
| ); |
| } |
| let globalscope = self.global(); |
| let window = DissimilarOriginWindow::new(&globalscope, self); |
| self.set_window( |
| window.upcast(), |
| WindowProxyHandler::x_origin_proxy_handler(), |
| can_gc, |
| ); |
| self.currently_active.set(None); |
| } |
| |
| pub(crate) fn currently_active(&self) -> Option<PipelineId> { |
| self.currently_active.get() |
| } |
| |
| pub(crate) fn get_name(&self) -> DOMString { |
| self.name.borrow().clone() |
| } |
| |
| pub(crate) fn set_name(&self, name: DOMString) { |
| *self.name.borrow_mut() = name; |
| } |
| } |
| |
| /// A browsing context can have a creator browsing context, the browsing context that |
| /// was responsible for its creation. If a browsing context has a parent browsing context, |
| /// then that is its creator browsing context. Otherwise, if the browsing context has an |
| /// opener browsing context, then that is its creator browsing context. Otherwise, the |
| /// browsing context has no creator browsing context. |
| /// |
| /// If a browsing context A has a creator browsing context, then the Document that was the |
| /// active document of that creator browsing context at the time A was created is the creator |
| /// Document. |
| /// |
| /// See: <https://html.spec.whatwg.org/multipage/#creating-browsing-contexts> |
| #[derive(Debug, Deserialize, Serialize)] |
| pub(crate) struct CreatorBrowsingContextInfo { |
| /// Creator document URL. |
| url: Option<ServoUrl>, |
| |
| /// Creator document base URL. |
| base_url: Option<ServoUrl>, |
| |
| /// Creator document origin. |
| origin: Option<ImmutableOrigin>, |
| } |
| |
| impl CreatorBrowsingContextInfo { |
| pub(crate) fn from( |
| parent: Option<&WindowProxy>, |
| opener: Option<&WindowProxy>, |
| ) -> CreatorBrowsingContextInfo { |
| let creator = match (parent, opener) { |
| (Some(parent), _) => parent.document(), |
| (None, Some(opener)) => opener.document(), |
| (None, None) => None, |
| }; |
| |
| let base_url = creator.as_deref().map(|document| document.base_url()); |
| let url = creator.as_deref().map(|document| document.url()); |
| let origin = creator |
| .as_deref() |
| .map(|document| document.origin().immutable().clone()); |
| |
| CreatorBrowsingContextInfo { |
| base_url, |
| url, |
| origin, |
| } |
| } |
| } |
| |
| // https://html.spec.whatwg.org/multipage/#concept-window-open-features-tokenize |
| fn tokenize_open_features(features: DOMString) -> IndexMap<String, String> { |
| let is_feature_sep = |c: char| c.is_ascii_whitespace() || ['=', ','].contains(&c); |
| // Step 1 |
| let mut tokenized_features = IndexMap::new(); |
| // Step 2 |
| let mut iter = features.chars(); |
| let mut cur = iter.next(); |
| |
| // Step 3 |
| while cur.is_some() { |
| // Step 3.1 & 3.2 |
| let mut name = String::new(); |
| let mut value = String::new(); |
| // Step 3.3 |
| while let Some(cur_char) = cur { |
| if !is_feature_sep(cur_char) { |
| break; |
| } |
| cur = iter.next(); |
| } |
| // Step 3.4 |
| while let Some(cur_char) = cur { |
| if is_feature_sep(cur_char) { |
| break; |
| } |
| name.push(cur_char.to_ascii_lowercase()); |
| cur = iter.next(); |
| } |
| // Step 3.5 |
| let normalized_name = String::from(match name.as_ref() { |
| "screenx" => "left", |
| "screeny" => "top", |
| "innerwidth" => "width", |
| "innerheight" => "height", |
| _ => name.as_ref(), |
| }); |
| // Step 3.6 |
| while let Some(cur_char) = cur { |
| if cur_char == '=' || cur_char == ',' || !is_feature_sep(cur_char) { |
| break; |
| } |
| cur = iter.next(); |
| } |
| // Step 3.7 |
| if cur.is_some() && is_feature_sep(cur.unwrap()) { |
| // Step 3.7.1 |
| while let Some(cur_char) = cur { |
| if !is_feature_sep(cur_char) || cur_char == ',' { |
| break; |
| } |
| cur = iter.next(); |
| } |
| // Step 3.7.2 |
| while let Some(cur_char) = cur { |
| if is_feature_sep(cur_char) { |
| break; |
| } |
| value.push(cur_char.to_ascii_lowercase()); |
| cur = iter.next(); |
| } |
| } |
| // Step 3.8 |
| if !name.is_empty() { |
| tokenized_features.insert(normalized_name, value); |
| } |
| } |
| // Step 4 |
| tokenized_features |
| } |
| |
| // https://html.spec.whatwg.org/multipage/#concept-window-open-features-parse-boolean |
| fn parse_open_feature_boolean(tokenized_features: &IndexMap<String, String>, name: &str) -> bool { |
| if let Some(value) = tokenized_features.get(name) { |
| // Step 1 & 2 |
| if value.is_empty() || value == "yes" { |
| return true; |
| } |
| // Step 3 & 4 |
| if let Ok(int) = parse_integer(value.chars()) { |
| return int != 0; |
| } |
| } |
| // Step 5 |
| false |
| } |
| |
| // This is only called from extern functions, |
| // there's no use using the lifetimed handles here. |
| // https://html.spec.whatwg.org/multipage/#accessing-other-browsing-contexts |
| #[allow(unsafe_code, non_snake_case)] |
| unsafe fn GetSubframeWindowProxy( |
| cx: *mut JSContext, |
| proxy: RawHandleObject, |
| id: RawHandleId, |
| ) -> Option<(DomRoot<WindowProxy>, u32)> { |
| let index = get_array_index_from_id(Handle::from_raw(id)); |
| if let Some(index) = index { |
| let mut slot = UndefinedValue(); |
| GetProxyPrivate(*proxy, &mut slot); |
| rooted!(in(cx) let target = slot.to_object()); |
| if let Ok(win) = root_from_handleobject::<Window>(target.handle(), cx) { |
| let browsing_context_id = win.window_proxy().browsing_context_id(); |
| let (result_sender, result_receiver) = ipc::channel().unwrap(); |
| |
| let _ = win.as_global_scope().script_to_constellation_chan().send( |
| ScriptToConstellationMessage::GetChildBrowsingContextId( |
| browsing_context_id, |
| index as usize, |
| result_sender, |
| ), |
| ); |
| return result_receiver |
| .recv() |
| .ok() |
| .and_then(|maybe_bcid| maybe_bcid) |
| .and_then(ScriptThread::find_window_proxy) |
| .map(|proxy| (proxy, (JSPROP_ENUMERATE | JSPROP_READONLY) as u32)); |
| } else if let Ok(win) = |
| root_from_handleobject::<DissimilarOriginWindow>(target.handle(), cx) |
| { |
| let browsing_context_id = win.window_proxy().browsing_context_id(); |
| let (result_sender, result_receiver) = ipc::channel().unwrap(); |
| |
| let _ = win.global().script_to_constellation_chan().send( |
| ScriptToConstellationMessage::GetChildBrowsingContextId( |
| browsing_context_id, |
| index as usize, |
| result_sender, |
| ), |
| ); |
| return result_receiver |
| .recv() |
| .ok() |
| .and_then(|maybe_bcid| maybe_bcid) |
| .and_then(ScriptThread::find_window_proxy) |
| .map(|proxy| (proxy, JSPROP_READONLY as u32)); |
| } |
| } |
| |
| None |
| } |
| |
| #[allow(unsafe_code, non_snake_case)] |
| unsafe extern "C" fn getOwnPropertyDescriptor( |
| cx: *mut JSContext, |
| proxy: RawHandleObject, |
| id: RawHandleId, |
| desc: RawMutableHandle<PropertyDescriptor>, |
| is_none: *mut bool, |
| ) -> bool { |
| let window = GetSubframeWindowProxy(cx, proxy, id); |
| if let Some((window, attrs)) = window { |
| rooted!(in(cx) let mut val = UndefinedValue()); |
| window.to_jsval(cx, val.handle_mut()); |
| set_property_descriptor( |
| MutableHandle::from_raw(desc), |
| val.handle(), |
| attrs, |
| &mut *is_none, |
| ); |
| return true; |
| } |
| |
| let mut slot = UndefinedValue(); |
| GetProxyPrivate(proxy.get(), &mut slot); |
| rooted!(in(cx) let target = slot.to_object()); |
| JS_GetOwnPropertyDescriptorById(cx, target.handle().into(), id, desc, is_none) |
| } |
| |
| #[allow(unsafe_code, non_snake_case)] |
| unsafe extern "C" fn defineProperty( |
| cx: *mut JSContext, |
| proxy: RawHandleObject, |
| id: RawHandleId, |
| desc: RawHandle<PropertyDescriptor>, |
| res: *mut ObjectOpResult, |
| ) -> bool { |
| if get_array_index_from_id(Handle::from_raw(id)).is_some() { |
| // Spec says to Reject whether this is a supported index or not, |
| // since we have no indexed setter or indexed creator. That means |
| // throwing in strict mode (FIXME: Bug 828137), doing nothing in |
| // non-strict mode. |
| (*res).code_ = JSErrNum::JSMSG_CANT_DEFINE_WINDOW_ELEMENT as ::libc::uintptr_t; |
| return true; |
| } |
| |
| let mut slot = UndefinedValue(); |
| GetProxyPrivate(*proxy.ptr, &mut slot); |
| rooted!(in(cx) let target = slot.to_object()); |
| JS_DefinePropertyById(cx, target.handle().into(), id, desc, res) |
| } |
| |
| #[allow(unsafe_code)] |
| unsafe extern "C" fn has( |
| cx: *mut JSContext, |
| proxy: RawHandleObject, |
| id: RawHandleId, |
| bp: *mut bool, |
| ) -> bool { |
| let window = GetSubframeWindowProxy(cx, proxy, id); |
| if window.is_some() { |
| *bp = true; |
| return true; |
| } |
| |
| let mut slot = UndefinedValue(); |
| GetProxyPrivate(*proxy.ptr, &mut slot); |
| rooted!(in(cx) let target = slot.to_object()); |
| let mut found = false; |
| if !JS_HasPropertyById(cx, target.handle().into(), id, &mut found) { |
| return false; |
| } |
| |
| *bp = found; |
| true |
| } |
| |
| #[allow(unsafe_code)] |
| unsafe extern "C" fn get( |
| cx: *mut JSContext, |
| proxy: RawHandleObject, |
| receiver: RawHandleValue, |
| id: RawHandleId, |
| vp: RawMutableHandleValue, |
| ) -> bool { |
| let window = GetSubframeWindowProxy(cx, proxy, id); |
| if let Some((window, _attrs)) = window { |
| window.to_jsval(cx, MutableHandle::from_raw(vp)); |
| return true; |
| } |
| |
| let mut slot = UndefinedValue(); |
| GetProxyPrivate(*proxy.ptr, &mut slot); |
| rooted!(in(cx) let target = slot.to_object()); |
| JS_ForwardGetPropertyTo(cx, target.handle().into(), id, receiver, vp) |
| } |
| |
| #[allow(unsafe_code)] |
| unsafe extern "C" fn set( |
| cx: *mut JSContext, |
| proxy: RawHandleObject, |
| id: RawHandleId, |
| v: RawHandleValue, |
| receiver: RawHandleValue, |
| res: *mut ObjectOpResult, |
| ) -> bool { |
| if get_array_index_from_id(Handle::from_raw(id)).is_some() { |
| // Reject (which means throw if and only if strict) the set. |
| (*res).code_ = JSErrNum::JSMSG_READ_ONLY as ::libc::uintptr_t; |
| return true; |
| } |
| |
| let mut slot = UndefinedValue(); |
| GetProxyPrivate(*proxy.ptr, &mut slot); |
| rooted!(in(cx) let target = slot.to_object()); |
| JS_ForwardSetPropertyTo(cx, target.handle().into(), id, v, receiver, res) |
| } |
| |
| #[allow(unsafe_code)] |
| unsafe extern "C" fn get_prototype_if_ordinary( |
| _: *mut JSContext, |
| _: RawHandleObject, |
| is_ordinary: *mut bool, |
| _: RawMutableHandleObject, |
| ) -> bool { |
| // Window's [[GetPrototypeOf]] trap isn't the ordinary definition: |
| // |
| // https://html.spec.whatwg.org/multipage/#windowproxy-getprototypeof |
| // |
| // We nonetheless can implement it with a static [[Prototype]], because |
| // wrapper-class handlers (particularly, XOW in FilteringWrapper.cpp) supply |
| // all non-ordinary behavior. |
| // |
| // But from a spec point of view, it's the exact same object in both cases -- |
| // only the observer's changed. So this getPrototypeIfOrdinary trap on the |
| // non-wrapper object *must* report non-ordinary, even if static [[Prototype]] |
| // usually means ordinary. |
| *is_ordinary = false; |
| true |
| } |
| |
| static PROXY_TRAPS: ProxyTraps = ProxyTraps { |
| // TODO: These traps should change their behavior depending on |
| // `IsPlatformObjectSameOrigin(this.[[Window]])` |
| enter: None, |
| getOwnPropertyDescriptor: Some(getOwnPropertyDescriptor), |
| defineProperty: Some(defineProperty), |
| ownPropertyKeys: None, |
| delete_: None, |
| enumerate: None, |
| getPrototypeIfOrdinary: Some(get_prototype_if_ordinary), |
| getPrototype: None, // TODO: return `null` if cross origin-domain |
| setPrototype: None, |
| setImmutablePrototype: None, |
| preventExtensions: None, |
| isExtensible: None, |
| has: Some(has), |
| get: Some(get), |
| set: Some(set), |
| call: None, |
| construct: None, |
| hasOwn: None, |
| getOwnEnumerablePropertyKeys: None, |
| nativeCall: None, |
| objectClassIs: None, |
| className: None, |
| fun_toString: None, |
| boxedValue_unbox: None, |
| defaultValue: None, |
| trace: Some(trace), |
| finalize: Some(finalize), |
| objectMoved: None, |
| isCallable: None, |
| isConstructor: None, |
| }; |
| |
| /// Proxy handler for a WindowProxy. |
| /// Has ownership of the inner pointer and deallocates it when it is no longer needed. |
| pub(crate) struct WindowProxyHandler(*const libc::c_void); |
| |
| impl MallocSizeOf for WindowProxyHandler { |
| fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize { |
| // FIXME(#6907) this is a pointer to memory allocated by `new` in NewProxyHandler in rust-mozjs. |
| 0 |
| } |
| } |
| |
| // Safety: Send and Sync is guaranteed since the underlying pointer and all its associated methods in C++ are const. |
| #[allow(unsafe_code)] |
| unsafe impl Send for WindowProxyHandler {} |
| // Safety: Send and Sync is guaranteed since the underlying pointer and all its associated methods in C++ are const. |
| #[allow(unsafe_code)] |
| unsafe impl Sync for WindowProxyHandler {} |
| |
| #[allow(unsafe_code)] |
| impl WindowProxyHandler { |
| fn new(traps: &ProxyTraps) -> Self { |
| // Safety: Foreign function generated by bindgen. Pointer is freed in drop to prevent memory leak. |
| let ptr = unsafe { CreateWrapperProxyHandler(traps) }; |
| assert!(!ptr.is_null()); |
| Self(ptr) |
| } |
| |
| /// Returns a single, shared WindowProxyHandler that contains XORIGIN_PROXY_TRAPS. |
| pub(crate) fn x_origin_proxy_handler() -> &'static Self { |
| use std::sync::OnceLock; |
| /// We are sharing a single instance for the entire programs here due to lifetime issues. |
| /// The pointer in self.0 is known to C++ and visited by the GC. Hence, we don't know when |
| /// it is safe to free it. |
| /// Sharing a single instance should be fine because all methods on this pointer in C++ |
| /// are const and don't modify its internal state. |
| static SINGLETON: OnceLock<WindowProxyHandler> = OnceLock::new(); |
| SINGLETON.get_or_init(|| Self::new(&XORIGIN_PROXY_TRAPS)) |
| } |
| |
| /// Returns a single, shared WindowProxyHandler that contains normal PROXY_TRAPS. |
| pub(crate) fn proxy_handler() -> &'static Self { |
| use std::sync::OnceLock; |
| /// We are sharing a single instance for the entire programs here due to lifetime issues. |
| /// The pointer in self.0 is known to C++ and visited by the GC. Hence, we don't know when |
| /// it is safe to free it. |
| /// Sharing a single instance should be fine because all methods on this pointer in C++ |
| /// are const and don't modify its internal state. |
| static SINGLETON: OnceLock<WindowProxyHandler> = OnceLock::new(); |
| SINGLETON.get_or_init(|| Self::new(&PROXY_TRAPS)) |
| } |
| |
| /// Creates a new WindowProxy object on the C++ side and returns the pointer to it. |
| /// The pointer should be owned by the GC. |
| pub(crate) fn new_window_proxy( |
| &self, |
| cx: &crate::script_runtime::JSContext, |
| window_jsobject: js::gc::HandleObject, |
| ) -> *mut JSObject { |
| let obj = unsafe { NewWindowProxy(**cx, window_jsobject, self.0) }; |
| assert!(!obj.is_null()); |
| obj |
| } |
| } |
| |
| #[allow(unsafe_code)] |
| impl Drop for WindowProxyHandler { |
| fn drop(&mut self) { |
| // Safety: Pointer is allocated by corresponding C++ function, owned by this |
| // struct and not accessible from outside. |
| unsafe { |
| DeleteWrapperProxyHandler(self.0); |
| } |
| } |
| } |
| |
| // The proxy traps for cross-origin windows. |
| // These traps often throw security errors, and only pass on calls to methods |
| // defined in the DissimilarOriginWindow IDL. |
| |
| // TODO: reuse the infrastructure in `proxyhandler.rs`. For starters, the calls |
| // to this function should be replaced with those to |
| // `report_cross_origin_denial`. |
| #[allow(unsafe_code)] |
| unsafe fn throw_security_error(cx: *mut JSContext, realm: InRealm) -> bool { |
| if !JS_IsExceptionPending(cx) { |
| let safe_context = SafeJSContext::from_ptr(cx); |
| let global = GlobalScope::from_context(cx, realm); |
| throw_dom_exception(safe_context, &global, Error::Security, CanGc::note()); |
| } |
| false |
| } |
| |
| #[allow(unsafe_code)] |
| unsafe extern "C" fn has_xorigin( |
| cx: *mut JSContext, |
| proxy: RawHandleObject, |
| id: RawHandleId, |
| bp: *mut bool, |
| ) -> bool { |
| let mut slot = UndefinedValue(); |
| GetProxyPrivate(*proxy.ptr, &mut slot); |
| rooted!(in(cx) let target = slot.to_object()); |
| let mut found = false; |
| JS_HasOwnPropertyById(cx, target.handle().into(), id, &mut found); |
| if found { |
| *bp = true; |
| true |
| } else { |
| let in_realm_proof = AlreadyInRealm::assert_for_cx(SafeJSContext::from_ptr(cx)); |
| throw_security_error(cx, InRealm::Already(&in_realm_proof)) |
| } |
| } |
| |
| #[allow(unsafe_code)] |
| unsafe extern "C" fn get_xorigin( |
| cx: *mut JSContext, |
| proxy: RawHandleObject, |
| receiver: RawHandleValue, |
| id: RawHandleId, |
| vp: RawMutableHandleValue, |
| ) -> bool { |
| let mut found = false; |
| has_xorigin(cx, proxy, id, &mut found); |
| found && get(cx, proxy, receiver, id, vp) |
| } |
| |
| #[allow(unsafe_code)] |
| unsafe extern "C" fn set_xorigin( |
| cx: *mut JSContext, |
| _: RawHandleObject, |
| _: RawHandleId, |
| _: RawHandleValue, |
| _: RawHandleValue, |
| _: *mut ObjectOpResult, |
| ) -> bool { |
| let in_realm_proof = AlreadyInRealm::assert_for_cx(SafeJSContext::from_ptr(cx)); |
| throw_security_error(cx, InRealm::Already(&in_realm_proof)) |
| } |
| |
| #[allow(unsafe_code)] |
| unsafe extern "C" fn delete_xorigin( |
| cx: *mut JSContext, |
| _: RawHandleObject, |
| _: RawHandleId, |
| _: *mut ObjectOpResult, |
| ) -> bool { |
| let in_realm_proof = AlreadyInRealm::assert_for_cx(SafeJSContext::from_ptr(cx)); |
| throw_security_error(cx, InRealm::Already(&in_realm_proof)) |
| } |
| |
| #[allow(unsafe_code, non_snake_case)] |
| unsafe extern "C" fn getOwnPropertyDescriptor_xorigin( |
| cx: *mut JSContext, |
| proxy: RawHandleObject, |
| id: RawHandleId, |
| desc: RawMutableHandle<PropertyDescriptor>, |
| is_none: *mut bool, |
| ) -> bool { |
| let mut found = false; |
| has_xorigin(cx, proxy, id, &mut found); |
| found && getOwnPropertyDescriptor(cx, proxy, id, desc, is_none) |
| } |
| |
| #[allow(unsafe_code, non_snake_case)] |
| unsafe extern "C" fn defineProperty_xorigin( |
| cx: *mut JSContext, |
| _: RawHandleObject, |
| _: RawHandleId, |
| _: RawHandle<PropertyDescriptor>, |
| _: *mut ObjectOpResult, |
| ) -> bool { |
| let in_realm_proof = AlreadyInRealm::assert_for_cx(SafeJSContext::from_ptr(cx)); |
| throw_security_error(cx, InRealm::Already(&in_realm_proof)) |
| } |
| |
| #[allow(unsafe_code, non_snake_case)] |
| unsafe extern "C" fn preventExtensions_xorigin( |
| cx: *mut JSContext, |
| _: RawHandleObject, |
| _: *mut ObjectOpResult, |
| ) -> bool { |
| let in_realm_proof = AlreadyInRealm::assert_for_cx(SafeJSContext::from_ptr(cx)); |
| throw_security_error(cx, InRealm::Already(&in_realm_proof)) |
| } |
| |
| static XORIGIN_PROXY_TRAPS: ProxyTraps = ProxyTraps { |
| enter: None, |
| getOwnPropertyDescriptor: Some(getOwnPropertyDescriptor_xorigin), |
| defineProperty: Some(defineProperty_xorigin), |
| ownPropertyKeys: None, |
| delete_: Some(delete_xorigin), |
| enumerate: None, |
| getPrototypeIfOrdinary: None, |
| getPrototype: None, |
| setPrototype: None, |
| setImmutablePrototype: None, |
| preventExtensions: Some(preventExtensions_xorigin), |
| isExtensible: None, |
| has: Some(has_xorigin), |
| get: Some(get_xorigin), |
| set: Some(set_xorigin), |
| call: None, |
| construct: None, |
| hasOwn: Some(has_xorigin), |
| getOwnEnumerablePropertyKeys: None, |
| nativeCall: None, |
| objectClassIs: None, |
| className: None, |
| fun_toString: None, |
| boxedValue_unbox: None, |
| defaultValue: None, |
| trace: Some(trace), |
| finalize: Some(finalize), |
| objectMoved: None, |
| isCallable: None, |
| isConstructor: None, |
| }; |
| |
| // How WindowProxy objects are garbage collected. |
| |
| #[allow(unsafe_code)] |
| unsafe extern "C" fn finalize(_fop: *mut GCContext, obj: *mut JSObject) { |
| let mut slot = UndefinedValue(); |
| GetProxyReservedSlot(obj, 0, &mut slot); |
| let this = slot.to_private() as *mut WindowProxy; |
| if this.is_null() { |
| // GC during obj creation or after transplanting. |
| return; |
| } |
| let jsobject = (*this).reflector.get_jsobject().get(); |
| debug!( |
| "WindowProxy finalize: {:p}, with reflector {:p} from {:p}.", |
| this, jsobject, obj |
| ); |
| let _ = Box::from_raw(this); |
| } |
| |
| #[allow(unsafe_code)] |
| unsafe extern "C" fn trace(trc: *mut JSTracer, obj: *mut JSObject) { |
| let mut slot = UndefinedValue(); |
| GetProxyReservedSlot(obj, 0, &mut slot); |
| let this = slot.to_private() as *const WindowProxy; |
| if this.is_null() { |
| // GC during obj creation or after transplanting. |
| return; |
| } |
| (*this).trace(trc); |
| } |