diff --git a/components/script/dom/audionode.rs b/components/script/dom/audionode.rs index 422b517bc94..4e572dda437 100644 --- a/components/script/dom/audionode.rs +++ b/components/script/dom/audionode.rs @@ -5,6 +5,9 @@ use std::cell::Cell; use dom_struct::dom_struct; +use script_bindings::codegen::InheritTypes::{ + AudioNodeTypeId, AudioScheduledSourceNodeTypeId, EventTargetTypeId, +}; use servo_media::audio::graph::NodeId; use servo_media::audio::node::{ AudioNodeInit, AudioNodeMessage, ChannelCountMode as ServoMediaChannelCountMode, ChannelInfo, @@ -17,9 +20,6 @@ use crate::dom::baseaudiocontext::BaseAudioContext; use crate::dom::bindings::codegen::Bindings::AudioNodeBinding::{ AudioNodeMethods, AudioNodeOptions, ChannelCountMode, ChannelInterpretation, }; -use crate::dom::bindings::codegen::InheritTypes::{ - AudioNodeTypeId, AudioScheduledSourceNodeTypeId, EventTargetTypeId, -}; use crate::dom::bindings::error::{Error, ErrorResult, Fallible}; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::root::{Dom, DomRoot}; diff --git a/components/script/dom/bindings/callback.rs b/components/script/dom/bindings/callback.rs index 7a28451763f..6a54255822f 100644 --- a/components/script/dom/bindings/callback.rs +++ b/components/script/dom/bindings/callback.rs @@ -16,14 +16,15 @@ use js::jsval::{JSVal, ObjectValue, UndefinedValue}; use js::rust::wrappers::{JS_GetProperty, JS_WrapObject}; use js::rust::{MutableHandleValue, Runtime}; use script_bindings::interfaces::DocumentHelpers; +use script_bindings::utils::AsCCharPtrPtr; use crate::DomTypes; use crate::dom::bindings::codegen::Bindings::WindowBinding::Window_Binding::WindowMethods; -use crate::dom::bindings::error::{Error, Fallible, report_pending_exception}; +use crate::dom::bindings::error::{Error, Fallible}; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::root::{Dom, DomRoot}; use crate::dom::bindings::settings_stack::{GenericAutoEntryScript, GenericAutoIncumbentScript}; -use crate::dom::bindings::utils::AsCCharPtrPtr; +use crate::dom::bindings::utils::DomHelpers; use crate::dom::globalscope::GlobalScopeHelpers; use crate::realms::{InRealm, enter_realm}; use crate::script_runtime::{CanGc, JSContext}; @@ -281,7 +282,12 @@ impl Drop for CallSetup { } if self.handling == ExceptionHandling::Report { let ar = enter_realm(&*self.exception_global); - report_pending_exception(self.cx, true, InRealm::Entered(&ar), CanGc::note()); + >::report_pending_exception( + self.cx, + true, + InRealm::Entered(&ar), + CanGc::note(), + ); } drop(self.incumbent_script.take()); drop(self.entry_script.take().unwrap()); diff --git a/components/script/dom/bindings/import.rs b/components/script/dom/bindings/import.rs index fb893af33f4..71ba5e4c994 100644 --- a/components/script/dom/bindings/import.rs +++ b/components/script/dom/bindings/import.rs @@ -107,6 +107,7 @@ pub(crate) mod module { }; pub(crate) use script_bindings::interfaces::*; pub(crate) use script_bindings::record::Record; + pub(crate) use script_bindings::reflector::DomObject; pub(crate) use servo_config::pref; pub(crate) use super::base::*; @@ -140,7 +141,7 @@ pub(crate) mod module { define_guarded_properties, get_desired_proto, get_per_interface_object_handle, is_exposed_in, }; - pub(crate) use crate::dom::bindings::iterable::{Iterable, IteratorType}; + pub(crate) use crate::dom::bindings::iterable::{Iterable, IterableIterator, IteratorType}; pub(crate) use crate::dom::bindings::like::{Maplike, Setlike}; pub(crate) use crate::dom::bindings::namespace::{ NamespaceObjectClass, create_namespace_object, diff --git a/components/script/dom/bindings/inheritance.rs b/components/script/dom/bindings/inheritance.rs index ef60378d55b..bb68b4b08f1 100644 --- a/components/script/dom/bindings/inheritance.rs +++ b/components/script/dom/bindings/inheritance.rs @@ -3,11 +3,4 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ pub(crate) use script_bindings::codegen::InheritTypes::*; -pub(crate) use script_bindings::inheritance::Castable; - -#[allow(missing_docs)] -pub(crate) trait HasParent { - #[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)] - type Parent; - fn as_parent(&self) -> &Self::Parent; -} +pub(crate) use script_bindings::inheritance::{Castable, HasParent}; diff --git a/components/script/dom/bindings/interface.rs b/components/script/dom/bindings/interface.rs index 835d2f7f0be..d1c191be3a4 100644 --- a/components/script/dom/bindings/interface.rs +++ b/components/script/dom/bindings/interface.rs @@ -135,7 +135,7 @@ impl InterfaceConstructorBehavior { pub(crate) type TraceHook = unsafe extern "C" fn(trc: *mut JSTracer, obj: *mut JSObject); /// Create a global object with the given class. -pub(crate) unsafe fn create_global_object( +pub(crate) unsafe fn create_global_object( cx: SafeJSContext, class: &'static JSClass, private: *const libc::c_void, @@ -150,7 +150,7 @@ pub(crate) unsafe fn create_global_object( options.creationOptions_.sharedMemoryAndAtomics_ = false; select_compartment(cx, &mut options); - let principal = ServoJSPrincipals::new(origin); + let principal = ServoJSPrincipals::new::(origin); rval.set(JS_NewGlobalObject( *cx, diff --git a/components/script/dom/bindings/iterable.rs b/components/script/dom/bindings/iterable.rs index d97dfeeddbe..c81551f7960 100644 --- a/components/script/dom/bindings/iterable.rs +++ b/components/script/dom/bindings/iterable.rs @@ -26,10 +26,11 @@ use crate::dom::bindings::codegen::Bindings::IterableIteratorBinding::{ }; use crate::dom::bindings::error::Fallible; use crate::dom::bindings::reflector::{ - DomGlobalGeneric, DomObjectIteratorWrap, DomObjectWrap, Reflector, reflect_dom_object, + DomGlobalGeneric, DomObjectIteratorWrap, DomObjectWrap, Reflector, }; use crate::dom::bindings::root::{Dom, DomRoot, Root}; use crate::dom::bindings::trace::{JSTraceable, NoTrace, RootedTraceableBox}; +use crate::dom::bindings::utils::DomHelpers; use crate::realms::InRealm; use crate::script_runtime::{CanGc, JSContext}; @@ -79,7 +80,7 @@ impl + JSTraceable + Iterable + DomGlob index: Cell::new(0), _marker: NoTrace(PhantomData), }); - reflect_dom_object(iterator, &*iterable.global_(realm), CanGc::note()) + >::reflect_dom_object(iterator, &*iterable.global_(realm), CanGc::note()) } /// Return the next value from the iterable object. diff --git a/components/script/dom/bindings/mod.rs b/components/script/dom/bindings/mod.rs index 833915c0b98..b4234e0cc39 100644 --- a/components/script/dom/bindings/mod.rs +++ b/components/script/dom/bindings/mod.rs @@ -191,7 +191,6 @@ pub(crate) mod codegen { include!(concat!(env!("BINDINGS_OUT_DIR"), "/InterfaceObjectMap.rs")); pub(crate) use script_bindings::codegen::Globals::Globals; } - pub(crate) use script_bindings::codegen::InheritTypes; #[allow(dead_code)] pub(crate) mod ConcreteInheritTypes { include!(concat!( diff --git a/components/script/dom/bindings/principals.rs b/components/script/dom/bindings/principals.rs index b037fcb0709..e51a98e454a 100644 --- a/components/script/dom/bindings/principals.rs +++ b/components/script/dom/bindings/principals.rs @@ -19,16 +19,21 @@ use js::rust::Runtime; use servo_url::MutableOrigin; use super::structuredclone::StructuredCloneTags; +use crate::dom::bindings::utils::DomHelpers; +use crate::{DomTypeHolder, DomTypes}; /// An owned reference to Servo's `JSPrincipals` instance. #[repr(transparent)] pub(crate) struct ServoJSPrincipals(NonNull); impl ServoJSPrincipals { - pub(crate) fn new(origin: &MutableOrigin) -> Self { + pub(crate) fn new(origin: &MutableOrigin) -> Self { unsafe { let private: Box = Box::new(origin.clone()); - let raw = CreateRustJSPrincipals(&PRINCIPALS_CALLBACKS, Box::into_raw(private) as _); + let raw = CreateRustJSPrincipals( + >::principals_callbacks(), + Box::into_raw(private) as _, + ); // The created `JSPrincipals` object has an initial reference // count of zero, so the following code will set it to one Self::from_raw_nonnull(NonNull::new_unchecked(raw)) @@ -175,14 +180,14 @@ pub(crate) unsafe extern "C" fn read_jsprincipal( let Ok(origin) = bincode::deserialize(&bytes[..]) else { return false; }; - let principal = ServoJSPrincipals::new(&origin); + let principal = ServoJSPrincipals::new::(&origin); *principals = principal.as_raw(); // we transferred ownership of principal to the caller std::mem::forget(principal); true } -const PRINCIPALS_CALLBACKS: JSPrincipalsCallbacks = JSPrincipalsCallbacks { +pub(crate) const PRINCIPALS_CALLBACKS: JSPrincipalsCallbacks = JSPrincipalsCallbacks { write: Some(write_jsprincipal), isSystemOrAddonPrincipal: Some(principals_is_system_or_addon_principal), }; diff --git a/components/script/dom/bindings/proxyhandler.rs b/components/script/dom/bindings/proxyhandler.rs index 1ef83119e53..5dfe0839502 100644 --- a/components/script/dom/bindings/proxyhandler.rs +++ b/components/script/dom/bindings/proxyhandler.rs @@ -6,199 +6,31 @@ #![deny(missing_docs)] -use std::ffi::CStr; -use std::os::raw::c_char; use std::ptr; use js::conversions::ToJSValConvertible; -use js::glue::{ - GetProxyHandler, GetProxyHandlerFamily, GetProxyPrivate, InvokeGetOwnPropertyDescriptor, - SetProxyPrivate, -}; +use js::glue::{GetProxyHandler, InvokeGetOwnPropertyDescriptor}; use js::jsapi; use js::jsapi::{ - DOMProxyShadowsResult, GetObjectRealmOrNull, GetRealmPrincipals, GetStaticPrototype, - GetWellKnownSymbol, Handle as RawHandle, HandleId as RawHandleId, - HandleObject as RawHandleObject, HandleValue as RawHandleValue, JS_AtomizeAndPinString, - JS_DefinePropertyById, JS_GetOwnPropertyDescriptorById, JS_IsExceptionPending, JSAutoRealm, - JSContext, JSErrNum, JSFunctionSpec, JSObject, JSPropertySpec, - MutableHandle as RawMutableHandle, MutableHandleIdVector as RawMutableHandleIdVector, + GetObjectRealmOrNull, GetRealmPrincipals, HandleId as RawHandleId, + HandleObject as RawHandleObject, HandleValue as RawHandleValue, JS_IsExceptionPending, + JSAutoRealm, JSContext, JSObject, MutableHandle as RawMutableHandle, MutableHandleObject as RawMutableHandleObject, MutableHandleValue as RawMutableHandleValue, - ObjectOpResult, PropertyDescriptor, SetDOMProxyInformation, SymbolCode, jsid, -}; -use js::jsid::SymbolId; -use js::jsval::{ObjectValue, UndefinedValue}; -use js::rust::wrappers::{ - AppendToIdVector, JS_AlreadyHasOwnPropertyById, JS_NewObjectWithGivenProto, - RUST_INTERNED_STRING_TO_JSID, SetDataPropertyDescriptor, -}; -use js::rust::{ - Handle, HandleObject, HandleValue, MutableHandle, MutableHandleObject, get_context_realm, + ObjectOpResult, PropertyDescriptor, }; +use js::jsval::UndefinedValue; +use js::rust::{HandleObject, HandleValue, MutableHandle, MutableHandleObject, get_context_realm}; +pub(crate) use script_bindings::proxyhandler::*; -use crate::dom::bindings::conversions::{is_dom_proxy, jsid_to_string, jsstring_to_str}; -use crate::dom::bindings::error::{Error, throw_dom_exception}; +use crate::DomTypes; +use crate::dom::bindings::error::Error; use crate::dom::bindings::principals::ServoJSPrincipalsRef; use crate::dom::bindings::reflector::DomObject; -use crate::dom::bindings::str::DOMString; -use crate::dom::bindings::utils::delete_property_by_id; -use crate::dom::globalscope::{GlobalScope, GlobalScopeHelpers}; +use crate::dom::bindings::utils::DomHelpers; +use crate::dom::globalscope::GlobalScopeHelpers; use crate::realms::{AlreadyInRealm, InRealm}; use crate::script_runtime::{CanGc, JSContext as SafeJSContext}; -/// Determine if this id shadows any existing properties for this proxy. -pub(crate) unsafe extern "C" fn shadow_check_callback( - cx: *mut JSContext, - object: RawHandleObject, - id: RawHandleId, -) -> DOMProxyShadowsResult { - // TODO: support OverrideBuiltins when #12978 is fixed. - - rooted!(in(cx) let mut expando = ptr::null_mut::()); - get_expando_object(object, expando.handle_mut()); - if !expando.get().is_null() { - let mut has_own = false; - let raw_id = Handle::from_raw(id); - - if !JS_AlreadyHasOwnPropertyById(cx, expando.handle(), raw_id, &mut has_own) { - return DOMProxyShadowsResult::ShadowCheckFailed; - } - - if has_own { - return DOMProxyShadowsResult::ShadowsViaDirectExpando; - } - } - - // Our expando, if any, didn't shadow, so we're not shadowing at all. - DOMProxyShadowsResult::DoesntShadow -} - -/// Initialize the infrastructure for DOM proxy objects. -pub(crate) unsafe fn init() { - SetDOMProxyInformation( - GetProxyHandlerFamily(), - Some(shadow_check_callback), - ptr::null(), - ); -} - -/// Defines an expando on the given `proxy`. -pub(crate) unsafe extern "C" fn define_property( - cx: *mut JSContext, - proxy: RawHandleObject, - id: RawHandleId, - desc: RawHandle, - result: *mut ObjectOpResult, -) -> bool { - rooted!(in(cx) let mut expando = ptr::null_mut::()); - ensure_expando_object(cx, proxy, expando.handle_mut()); - JS_DefinePropertyById(cx, expando.handle().into(), id, desc, result) -} - -/// Deletes an expando off the given `proxy`. -pub(crate) unsafe extern "C" fn delete( - cx: *mut JSContext, - proxy: RawHandleObject, - id: RawHandleId, - bp: *mut ObjectOpResult, -) -> bool { - rooted!(in(cx) let mut expando = ptr::null_mut::()); - get_expando_object(proxy, expando.handle_mut()); - if expando.is_null() { - (*bp).code_ = 0 /* OkCode */; - return true; - } - - delete_property_by_id(cx, expando.handle(), Handle::from_raw(id), bp) -} - -/// Controls whether the Extensible bit can be changed -pub(crate) unsafe extern "C" fn prevent_extensions( - _cx: *mut JSContext, - _proxy: RawHandleObject, - result: *mut ObjectOpResult, -) -> bool { - (*result).code_ = JSErrNum::JSMSG_CANT_PREVENT_EXTENSIONS as ::libc::uintptr_t; - true -} - -/// Reports whether the object is Extensible -pub(crate) unsafe extern "C" fn is_extensible( - _cx: *mut JSContext, - _proxy: RawHandleObject, - succeeded: *mut bool, -) -> bool { - *succeeded = true; - true -} - -/// If `proxy` (underneath any functionally-transparent wrapper proxies) has as -/// its `[[GetPrototypeOf]]` trap the ordinary `[[GetPrototypeOf]]` behavior -/// defined for ordinary objects, set `*is_ordinary` to true and store `obj`'s -/// prototype in `proto`. Otherwise set `*isOrdinary` to false. In case of -/// error, both outparams have unspecified value. -/// -/// This implementation always handles the case of the ordinary -/// `[[GetPrototypeOf]]` behavior. An alternative implementation will be -/// necessary for maybe-cross-origin objects. -pub(crate) unsafe extern "C" fn get_prototype_if_ordinary( - _: *mut JSContext, - proxy: RawHandleObject, - is_ordinary: *mut bool, - proto: RawMutableHandleObject, -) -> bool { - *is_ordinary = true; - proto.set(GetStaticPrototype(proxy.get())); - true -} - -/// Get the expando object, or null if there is none. -pub(crate) unsafe fn get_expando_object(obj: RawHandleObject, mut expando: MutableHandleObject) { - assert!(is_dom_proxy(obj.get())); - let val = &mut UndefinedValue(); - GetProxyPrivate(obj.get(), val); - expando.set(if val.is_undefined() { - ptr::null_mut() - } else { - val.to_object() - }); -} - -/// Get the expando object, or create it if it doesn't exist yet. -/// Fails on JSAPI failure. -pub(crate) unsafe fn ensure_expando_object( - cx: *mut JSContext, - obj: RawHandleObject, - mut expando: MutableHandleObject, -) { - assert!(is_dom_proxy(obj.get())); - get_expando_object(obj, expando.reborrow()); - if expando.is_null() { - expando.set(JS_NewObjectWithGivenProto( - cx, - ptr::null_mut(), - HandleObject::null(), - )); - assert!(!expando.is_null()); - - SetProxyPrivate(obj.get(), &ObjectValue(expando.get())); - } -} - -/// Set the property descriptor's object to `obj` and set it to enumerable, -/// and writable if `readonly` is true. -pub(crate) fn set_property_descriptor( - desc: MutableHandle, - value: HandleValue, - attrs: u32, - is_none: &mut bool, -) { - unsafe { - SetDataPropertyDescriptor(desc, value, attrs); - } - *is_none = false; -} - /// pub(crate) unsafe fn is_platform_object_same_origin( cx: SafeJSContext, @@ -239,7 +71,7 @@ pub(crate) unsafe fn is_platform_object_same_origin( /// What this function does corresponds to the operations in /// denoted as /// "Throw a `SecurityError` DOMException". -pub(crate) unsafe fn report_cross_origin_denial( +pub(crate) unsafe fn report_cross_origin_denial( cx: SafeJSContext, id: RawHandleId, access: &str, @@ -251,75 +83,17 @@ pub(crate) unsafe fn report_cross_origin_denial( ); let in_realm_proof = AlreadyInRealm::assert_for_cx(cx); if !JS_IsExceptionPending(*cx) { - let global = GlobalScope::from_context(*cx, InRealm::Already(&in_realm_proof)); + let global = D::GlobalScope::from_context(*cx, InRealm::Already(&in_realm_proof)); // TODO: include `id` and `access` in the exception message - throw_dom_exception(cx, &global, Error::Security, CanGc::note()); + >::throw_dom_exception(cx, &global, Error::Security, CanGc::note()); } false } -unsafe fn id_to_source(cx: SafeJSContext, id: RawHandleId) -> Option { - rooted!(in(*cx) let mut value = UndefinedValue()); - rooted!(in(*cx) let mut jsstr = ptr::null_mut::()); - jsapi::JS_IdToValue(*cx, id.get(), value.handle_mut().into()) - .then(|| { - jsstr.set(jsapi::JS_ValueToSource(*cx, value.handle().into())); - jsstr.get() - }) - .and_then(ptr::NonNull::new) - .map(|jsstr| jsstring_to_str(*cx, jsstr)) -} - -/// Property and method specs that correspond to the elements of -/// [`CrossOriginProperties(O)`]. -/// -/// [`CrossOriginProperties(O)`]: https://html.spec.whatwg.org/multipage/#crossoriginproperties-(-o-) -pub(crate) struct CrossOriginProperties { - pub(crate) attributes: &'static [JSPropertySpec], - pub(crate) methods: &'static [JSFunctionSpec], -} - -impl CrossOriginProperties { - /// Enumerate the property keys defined by `self`. - fn keys(&self) -> impl Iterator + '_ { - // Safety: All cross-origin property keys are strings, not symbols - self.attributes - .iter() - .map(|spec| unsafe { spec.name.string_ }) - .chain(self.methods.iter().map(|spec| unsafe { spec.name.string_ })) - .filter(|ptr| !ptr.is_null()) - } -} - -/// Implementation of [`CrossOriginOwnPropertyKeys`]. -/// -/// [`CrossOriginOwnPropertyKeys`]: https://html.spec.whatwg.org/multipage/#crossoriginownpropertykeys-(-o-) -pub(crate) unsafe fn cross_origin_own_property_keys( - cx: SafeJSContext, - _proxy: RawHandleObject, - cross_origin_properties: &'static CrossOriginProperties, - props: RawMutableHandleIdVector, -) -> bool { - // > 2. For each `e` of `! CrossOriginProperties(O)`, append - // > `e.[[Property]]` to `keys`. - for key in cross_origin_properties.keys() { - rooted!(in(*cx) let rooted = JS_AtomizeAndPinString(*cx, key)); - rooted!(in(*cx) let mut rooted_jsid: jsid); - RUST_INTERNED_STRING_TO_JSID(*cx, rooted.handle().get(), rooted_jsid.handle_mut()); - AppendToIdVector(props, rooted_jsid.handle()); - } - - // > 3. Return the concatenation of `keys` and `« "then", @@toStringTag, - // > @@hasInstance, @@isConcatSpreadable »`. - append_cross_origin_allowlisted_prop_keys(cx, props); - - true -} - /// Implementation of `[[Set]]` for [`Location`]. /// /// [`Location`]: https://html.spec.whatwg.org/multipage/#location-set -pub(crate) unsafe extern "C" fn maybe_cross_origin_set_rawcx( +pub(crate) unsafe extern "C" fn maybe_cross_origin_set_rawcx( cx: *mut JSContext, proxy: RawHandleObject, id: RawHandleId, @@ -329,8 +103,8 @@ pub(crate) unsafe extern "C" fn maybe_cross_origin_set_rawcx( ) -> bool { let cx = SafeJSContext::from_ptr(cx); - if !is_platform_object_same_origin(cx, proxy) { - return cross_origin_set(cx, proxy, id, v, receiver, result); + if !>::is_platform_object_same_origin(cx, proxy) { + return cross_origin_set::(cx, proxy, id, v, receiver, result); } // Safe to enter the Realm of proxy now. @@ -362,21 +136,10 @@ pub(crate) unsafe extern "C" fn maybe_cross_origin_set_rawcx( ) } -pub(crate) unsafe extern "C" fn maybe_cross_origin_get_prototype_if_ordinary_rawcx( - _: *mut JSContext, - _proxy: RawHandleObject, - is_ordinary: *mut bool, - _proto: RawMutableHandleObject, -) -> bool { - // We have a custom `[[GetPrototypeOf]]`, so return `false` - *is_ordinary = false; - true -} - /// Implementation of `[[GetPrototypeOf]]` for [`Location`]. /// /// [`Location`]: https://html.spec.whatwg.org/multipage/#location-getprototypeof -pub(crate) unsafe fn maybe_cross_origin_get_prototype( +pub(crate) unsafe fn maybe_cross_origin_get_prototype( cx: SafeJSContext, proxy: RawHandleObject, get_proto_object: unsafe fn(cx: SafeJSContext, global: HandleObject, rval: MutableHandleObject), @@ -399,46 +162,13 @@ pub(crate) unsafe fn maybe_cross_origin_get_prototype( true } -/// Implementation of `[[SetPrototypeOf]]` for [`Location`] and [`WindowProxy`]. -/// -/// [`Location`]: https://html.spec.whatwg.org/multipage/#location-setprototypeof -/// [`WindowProxy`]: https://html.spec.whatwg.org/multipage/#windowproxy-setprototypeof -pub(crate) unsafe extern "C" fn maybe_cross_origin_set_prototype_rawcx( - cx: *mut JSContext, - proxy: RawHandleObject, - proto: RawHandleObject, - result: *mut ObjectOpResult, -) -> bool { - // > 1. Return `! SetImmutablePrototype(this, V)`. - // - // : - // - // > 1. Assert: Either `Type(V)` is Object or `Type(V)` is Null. - // - // > 2. Let current be `? O.[[GetPrototypeOf]]()`. - rooted!(in(cx) let mut current = ptr::null_mut::()); - if !jsapi::GetObjectProto(cx, proxy, current.handle_mut().into()) { - return false; - } - - // > 3. If `SameValue(V, current)` is true, return true. - if proto.get() == current.get() { - (*result).code_ = 0 /* OkCode */; - return true; - } - - // > 4. Return false. - (*result).code_ = JSErrNum::JSMSG_CANT_SET_PROTO as usize; - true -} - /// Implementation of [`CrossOriginGet`]. /// /// `cx` and `proxy` are expected to be different-Realm here. `proxy` is a proxy /// for a maybe-cross-origin object. /// /// [`CrossOriginGet`]: https://html.spec.whatwg.org/multipage/#crossoriginget-(-o,-p,-receiver-) -pub(crate) unsafe fn cross_origin_get( +pub(crate) unsafe fn cross_origin_get( cx: SafeJSContext, proxy: RawHandleObject, receiver: RawHandleValue, @@ -482,7 +212,7 @@ pub(crate) unsafe fn cross_origin_get( rooted!(in(*cx) let mut getter = ptr::null_mut::()); get_getter_object(&descriptor, getter.handle_mut().into()); if getter.get().is_null() { - return report_cross_origin_denial(cx, id, "get"); + return report_cross_origin_denial::(cx, id, "get"); } rooted!(in(*cx) let mut getter_jsval = UndefinedValue()); @@ -504,7 +234,7 @@ pub(crate) unsafe fn cross_origin_get( /// for a maybe-cross-origin object. /// /// [`CrossOriginSet`]: https://html.spec.whatwg.org/multipage/#crossoriginset-(-o,-p,-v,-receiver-) -pub(crate) unsafe fn cross_origin_set( +pub(crate) unsafe fn cross_origin_set( cx: SafeJSContext, proxy: RawHandleObject, id: RawHandleId, @@ -539,7 +269,7 @@ pub(crate) unsafe fn cross_origin_set( get_setter_object(&descriptor, setter.handle_mut().into()); if setter.get().is_null() { // > 4. Throw a "SecurityError" DOMException. - return report_cross_origin_denial(cx, id, "set"); + return report_cross_origin_denial::(cx, id, "set"); } rooted!(in(*cx) let mut setter_jsval = UndefinedValue()); @@ -568,86 +298,13 @@ pub(crate) unsafe fn cross_origin_set( true } -unsafe fn get_getter_object(d: &PropertyDescriptor, out: RawMutableHandleObject) { - if d.hasGetter_() { - out.set(d.getter_); - } -} - -unsafe fn get_setter_object(d: &PropertyDescriptor, out: RawMutableHandleObject) { - if d.hasSetter_() { - out.set(d.setter_); - } -} - -/// -fn is_accessor_descriptor(d: &PropertyDescriptor) -> bool { - d.hasSetter_() || d.hasGetter_() -} - -/// -fn is_data_descriptor(d: &PropertyDescriptor) -> bool { - d.hasWritable_() || d.hasValue_() -} - -/// Evaluate `CrossOriginGetOwnPropertyHelper(proxy, id) != null`. -/// SpiderMonkey-specific. -/// -/// `cx` and `proxy` are expected to be different-Realm here. `proxy` is a proxy -/// for a maybe-cross-origin object. -pub(crate) unsafe fn cross_origin_has_own( - cx: SafeJSContext, - _proxy: RawHandleObject, - cross_origin_properties: &'static CrossOriginProperties, - id: RawHandleId, - bp: *mut bool, -) -> bool { - // TODO: Once we have the slot for the holder, it'd be more efficient to - // use `ensure_cross_origin_property_holder`. We'll need `_proxy` to - // do that. - *bp = jsid_to_string(*cx, Handle::from_raw(id)).is_some_and(|key| { - cross_origin_properties.keys().any(|defined_key| { - let defined_key = CStr::from_ptr(defined_key); - defined_key.to_bytes() == key.as_bytes() - }) - }); - - true -} - -/// Implementation of [`CrossOriginGetOwnPropertyHelper`]. -/// -/// `cx` and `proxy` are expected to be different-Realm here. `proxy` is a proxy -/// for a maybe-cross-origin object. -/// -/// [`CrossOriginGetOwnPropertyHelper`]: https://html.spec.whatwg.org/multipage/#crossorigingetownpropertyhelper-(-o,-p-) -pub(crate) unsafe fn cross_origin_get_own_property_helper( - cx: SafeJSContext, - proxy: RawHandleObject, - cross_origin_properties: &'static CrossOriginProperties, - id: RawHandleId, - desc: RawMutableHandle, - is_none: &mut bool, -) -> bool { - rooted!(in(*cx) let mut holder = ptr::null_mut::()); - - ensure_cross_origin_property_holder( - cx, - proxy, - cross_origin_properties, - holder.handle_mut().into(), - ); - - JS_GetOwnPropertyDescriptorById(*cx, holder.handle().into(), id, desc, is_none) -} - /// Implementation of [`CrossOriginPropertyFallback`]. /// /// `cx` and `proxy` are expected to be different-Realm here. `proxy` is a proxy /// for a maybe-cross-origin object. /// /// [`CrossOriginPropertyFallback`]: https://html.spec.whatwg.org/multipage/#crossoriginpropertyfallback-(-p-) -pub(crate) unsafe fn cross_origin_property_fallback( +pub(crate) unsafe fn cross_origin_property_fallback( cx: SafeJSContext, _proxy: RawHandleObject, id: RawHandleId, @@ -671,96 +328,5 @@ pub(crate) unsafe fn cross_origin_property_fallback( } // > 2. Throw a `SecurityError` `DOMException`. - report_cross_origin_denial(cx, id, "access") -} - -const ALLOWLISTED_SYMBOL_CODES: &[SymbolCode] = &[ - SymbolCode::toStringTag, - SymbolCode::hasInstance, - SymbolCode::isConcatSpreadable, -]; - -unsafe fn is_cross_origin_allowlisted_prop(cx: SafeJSContext, id: RawHandleId) -> bool { - if jsid_to_string(*cx, Handle::from_raw(id)).is_some_and(|st| st == "then") { - return true; - } - - rooted!(in(*cx) let mut allowed_id: jsid); - ALLOWLISTED_SYMBOL_CODES.iter().any(|&allowed_code| { - allowed_id.set(SymbolId(GetWellKnownSymbol(*cx, allowed_code))); - // `jsid`s containing `JS::Symbol *` can be compared by - // referential equality - allowed_id.get().asBits_ == id.asBits_ - }) -} - -/// Append `« "then", @@toStringTag, @@hasInstance, @@isConcatSpreadable »` to -/// `props`. This is used to implement [`CrossOriginOwnPropertyKeys`]. -/// -/// [`CrossOriginOwnPropertyKeys`]: https://html.spec.whatwg.org/multipage/#crossoriginownpropertykeys-(-o-) -unsafe fn append_cross_origin_allowlisted_prop_keys( - cx: SafeJSContext, - props: RawMutableHandleIdVector, -) { - rooted!(in(*cx) let mut id: jsid); - - let jsstring = JS_AtomizeAndPinString(*cx, c"then".as_ptr()); - rooted!(in(*cx) let rooted = jsstring); - RUST_INTERNED_STRING_TO_JSID(*cx, rooted.handle().get(), id.handle_mut()); - AppendToIdVector(props, id.handle()); - - for &allowed_code in ALLOWLISTED_SYMBOL_CODES.iter() { - id.set(SymbolId(GetWellKnownSymbol(*cx, allowed_code))); - AppendToIdVector(props, id.handle()); - } -} - -/// Get the holder for cross-origin properties for the current global of the -/// `JSContext`, creating one and storing it in a slot of the proxy object if it -/// doesn't exist yet. -/// -/// This essentially creates a cache of [`CrossOriginGetOwnPropertyHelper`]'s -/// results for all property keys. -/// -/// `cx` and `proxy` are expected to be different-Realm here. `proxy` is a proxy -/// for a maybe-cross-origin object. The `out_holder` return value will always -/// be in the Realm of `cx`. -/// -/// [`CrossOriginGetOwnPropertyHelper`]: https://html.spec.whatwg.org/multipage/#crossorigingetownpropertyhelper-(-o,-p-) -unsafe fn ensure_cross_origin_property_holder( - cx: SafeJSContext, - _proxy: RawHandleObject, - cross_origin_properties: &'static CrossOriginProperties, - out_holder: RawMutableHandleObject, -) -> bool { - // TODO: We don't have the slot to store the holder yet. For now, - // the holder is constructed every time this function is called, - // which is not only inefficient but also deviates from the - // specification in a subtle yet observable way. - - // Create a holder for the current Realm - out_holder.set(jsapi::JS_NewObjectWithGivenProto( - *cx, - ptr::null_mut(), - RawHandleObject::null(), - )); - - if out_holder.get().is_null() || - !jsapi::JS_DefineProperties( - *cx, - out_holder.handle(), - cross_origin_properties.attributes.as_ptr(), - ) || - !jsapi::JS_DefineFunctions( - *cx, - out_holder.handle(), - cross_origin_properties.methods.as_ptr(), - ) - { - return false; - } - - // TODO: Store the holder in the slot that we don't have yet. - - true + report_cross_origin_denial::(cx, id, "access") } diff --git a/components/script/dom/bindings/root.rs b/components/script/dom/bindings/root.rs index 4e8cbf21882..923aa51351d 100644 --- a/components/script/dom/bindings/root.rs +++ b/components/script/dom/bindings/root.rs @@ -57,26 +57,6 @@ impl Drop for ThreadLocalStackRoots<'_> { } } -/// Get a slice of references to DOM objects. -pub(crate) trait DomSlice -where - T: JSTraceable + DomObject, -{ - /// Returns the slice of `T` references. - fn r(&self) -> &[&T]; -} - -impl DomSlice for [Dom] -where - T: JSTraceable + DomObject, -{ - #[inline] - fn r(&self) -> &[&T] { - let _ = mem::transmute::, &T>; - unsafe { &*(self as *const [Dom] as *const [&T]) } - } -} - pub(crate) trait ToLayout { /// Returns `LayoutDom` containing the same pointer. /// diff --git a/components/script/dom/bindings/utils.rs b/components/script/dom/bindings/utils.rs index f97c7188f32..5f16d3a96d2 100644 --- a/components/script/dom/bindings/utils.rs +++ b/components/script/dom/bindings/utils.rs @@ -5,29 +5,34 @@ //! Various utilities to glue JavaScript and the DOM implementation together. use std::cell::RefCell; -use std::os::raw::c_char; use std::thread::LocalKey; use std::{ptr, slice}; use js::conversions::ToJSValConvertible; -use js::glue::{IsWrapper, UnwrapObjectDynamic, UnwrapObjectStatic}; +use js::glue::{IsWrapper, JSPrincipalsCallbacks, UnwrapObjectDynamic, UnwrapObjectStatic}; use js::jsapi::{ - CallArgs, DOMCallbacks, HandleId as RawHandleId, HandleObject as RawHandleObject, Heap, + CallArgs, DOMCallbacks, HandleId as RawHandleId, HandleObject as RawHandleObject, JS_DeprecatedStringHasLatin1Chars, JS_EnumerateStandardClasses, JS_FreezeObject, JS_GetLatin1StringCharsAndLength, JS_IsGlobalObject, JS_ResolveStandardClass, JSContext, - JSObject, JSTracer, MutableHandleIdVector as RawMutableHandleIdVector, + JSObject, MutableHandleIdVector as RawMutableHandleIdVector, }; use js::rust::{Handle, HandleObject, MutableHandleValue, get_object_class, is_dom_class}; use crate::DomTypes; use crate::dom::bindings::codegen::{InterfaceObjectMap, PrototypeList}; -use crate::dom::bindings::constructor::call_html_constructor; +use crate::dom::bindings::constructor::{ + call_html_constructor, pop_current_element_queue, push_new_element_queue, +}; use crate::dom::bindings::conversions::DerivedFrom; -use crate::dom::bindings::error::{Error, throw_dom_exception}; -use crate::dom::bindings::reflector::DomObject; +use crate::dom::bindings::error::{Error, report_pending_exception, throw_dom_exception}; +use crate::dom::bindings::principals::PRINCIPALS_CALLBACKS; +use crate::dom::bindings::proxyhandler::is_platform_object_same_origin; +use crate::dom::bindings::reflector::{DomObject, DomObjectWrap, reflect_dom_object}; +use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::settings_stack::{self, StackEntry}; -use crate::dom::bindings::trace::trace_object; +use crate::dom::globalscope::GlobalScope; use crate::dom::windowproxy::WindowProxyHandler; +use crate::realms::InRealm; use crate::script_runtime::{CanGc, JSContext as SafeJSContext}; #[derive(JSTraceable, MallocSizeOf)] @@ -100,22 +105,8 @@ fn is_platform_object( } } -/// Trace the resources held by reserved slots of a global object -pub(crate) unsafe fn trace_global(tracer: *mut JSTracer, obj: *mut JSObject) { - let array = get_proto_or_iface_array(obj); - for proto in (*array).iter() { - if !proto.is_null() { - trace_object( - tracer, - "prototype", - &*(proto as *const *mut JSObject as *const Heap<*mut JSObject>), - ); - } - } -} - /// Enumerate lazy properties of a global object. -pub(crate) unsafe extern "C" fn enumerate_global( +pub(crate) unsafe extern "C" fn enumerate_global( cx: *mut JSContext, obj: RawHandleObject, _props: RawMutableHandleIdVector, @@ -125,14 +116,14 @@ pub(crate) unsafe extern "C" fn enumerate_global( if !JS_EnumerateStandardClasses(cx, obj) { return false; } - for init_fun in InterfaceObjectMap::MAP.values() { + for init_fun in >::interface_map().values() { init_fun(SafeJSContext::from_ptr(cx), Handle::from_raw(obj)); } true } /// Resolve a lazy global property, for interface objects and named constructors. -pub(crate) unsafe extern "C" fn resolve_global( +pub(crate) unsafe extern "C" fn resolve_global( cx: *mut JSContext, obj: RawHandleObject, id: RawHandleId, @@ -160,7 +151,7 @@ pub(crate) unsafe extern "C" fn resolve_global( assert!(!ptr.is_null()); let bytes = slice::from_raw_parts(ptr, length); - if let Some(init_fun) = InterfaceObjectMap::MAP.get(bytes) { + if let Some(init_fun) = >::interface_map().get(bytes) { init_fun(SafeJSContext::from_ptr(cx), Handle::from_raw(obj)); *rval = true; } else { @@ -184,30 +175,14 @@ pub(crate) const DOM_CALLBACKS: DOMCallbacks = DOMCallbacks { instanceClassMatchesProto: Some(instance_class_has_proto_at_depth), }; -// Generic method for returning libc::c_void from caller -pub(crate) trait AsVoidPtr { - fn as_void_ptr(&self) -> *const libc::c_void; -} -impl AsVoidPtr for T { - fn as_void_ptr(&self) -> *const libc::c_void { - self as *const T as *const libc::c_void - } -} - -// Generic method for returning c_char from caller -pub(crate) trait AsCCharPtrPtr { - fn as_c_char_ptr(&self) -> *const c_char; -} - -impl AsCCharPtrPtr for [u8] { - fn as_c_char_ptr(&self) -> *const c_char { - self as *const [u8] as *const c_char - } -} - /// Operations that must be invoked from the generated bindings. pub(crate) trait DomHelpers { - fn throw_dom_exception(cx: SafeJSContext, global: &D::GlobalScope, result: Error); + fn throw_dom_exception( + cx: SafeJSContext, + global: &D::GlobalScope, + result: Error, + can_gc: CanGc, + ); unsafe fn call_html_constructor + DomObject>( cx: SafeJSContext, @@ -219,6 +194,27 @@ pub(crate) trait DomHelpers { ) -> bool; fn settings_stack() -> &'static LocalKey>>>; + + fn principals_callbacks() -> &'static JSPrincipalsCallbacks; + + fn is_platform_object_same_origin(cx: SafeJSContext, obj: RawHandleObject) -> bool; + + fn interface_map() -> &'static phf::Map<&'static [u8], for<'a> fn(SafeJSContext, HandleObject)>; + + fn push_new_element_queue(); + fn pop_current_element_queue(can_gc: CanGc); + + fn reflect_dom_object(obj: Box, global: &U, can_gc: CanGc) -> DomRoot + where + T: DomObject + DomObjectWrap, + U: DerivedFrom; + + fn report_pending_exception( + cx: SafeJSContext, + dispatch_event: bool, + realm: InRealm, + can_gc: CanGc, + ); } impl DomHelpers for crate::DomTypeHolder { @@ -226,8 +222,9 @@ impl DomHelpers for crate::DomTypeHolder { cx: SafeJSContext, global: &::GlobalScope, result: Error, + can_gc: CanGc, ) { - throw_dom_exception(cx, global, result, CanGc::note()) + throw_dom_exception(cx, global, result, can_gc) } unsafe fn call_html_constructor< @@ -246,4 +243,41 @@ impl DomHelpers for crate::DomTypeHolder { fn settings_stack() -> &'static LocalKey>>> { &settings_stack::STACK } + + fn principals_callbacks() -> &'static JSPrincipalsCallbacks { + &PRINCIPALS_CALLBACKS + } + + fn is_platform_object_same_origin(cx: SafeJSContext, obj: RawHandleObject) -> bool { + unsafe { is_platform_object_same_origin(cx, obj) } + } + + fn interface_map() -> &'static phf::Map<&'static [u8], for<'a> fn(SafeJSContext, HandleObject)> + { + &InterfaceObjectMap::MAP + } + + fn push_new_element_queue() { + push_new_element_queue() + } + fn pop_current_element_queue(can_gc: CanGc) { + pop_current_element_queue(can_gc) + } + + fn reflect_dom_object(obj: Box, global: &U, can_gc: CanGc) -> DomRoot + where + T: DomObject + DomObjectWrap, + U: DerivedFrom, + { + reflect_dom_object(obj, global, can_gc) + } + + fn report_pending_exception( + cx: SafeJSContext, + dispatch_event: bool, + realm: InRealm, + can_gc: CanGc, + ) { + report_pending_exception(cx, dispatch_event, realm, can_gc) + } } diff --git a/components/script/dom/blob.rs b/components/script/dom/blob.rs index 75fa65deff0..d23cb62002e 100644 --- a/components/script/dom/blob.rs +++ b/components/script/dom/blob.rs @@ -243,7 +243,7 @@ impl BlobMethods for Blob { // https://w3c.github.io/FileAPI/#text-method-algo fn Text(&self, can_gc: CanGc) -> Rc { let global = self.global(); - let in_realm_proof = AlreadyInRealm::assert(); + let in_realm_proof = AlreadyInRealm::assert::(); let p = Promise::new_in_current_realm(InRealm::Already(&in_realm_proof), can_gc); let id = self.get_blob_url_id(); global.read_file_async( @@ -266,7 +266,7 @@ impl BlobMethods for Blob { // https://w3c.github.io/FileAPI/#arraybuffer-method-algo fn ArrayBuffer(&self, can_gc: CanGc) -> Rc { let global = self.global(); - let in_realm_proof = AlreadyInRealm::assert(); + let in_realm_proof = AlreadyInRealm::assert::(); let p = Promise::new_in_current_realm(InRealm::Already(&in_realm_proof), can_gc); let id = self.get_blob_url_id(); diff --git a/components/script/dom/bluetooth/bluetooth.rs b/components/script/dom/bluetooth/bluetooth.rs index c2732f2e9fb..91b07732630 100644 --- a/components/script/dom/bluetooth/bluetooth.rs +++ b/components/script/dom/bluetooth/bluetooth.rs @@ -300,7 +300,7 @@ where T: AsyncBluetoothListener + DomObject + 'static, F: FnOnce(StringOrUnsignedLong) -> Fallible, { - let in_realm_proof = AlreadyInRealm::assert(); + let in_realm_proof = AlreadyInRealm::assert::(); let p = Promise::new_in_current_realm(InRealm::Already(&in_realm_proof), can_gc); let result_uuid = if let Some(u) = uuid { diff --git a/components/script/dom/characterdata.rs b/components/script/dom/characterdata.rs index fe677a6b7b2..d161fddd8d8 100644 --- a/components/script/dom/characterdata.rs +++ b/components/script/dom/characterdata.rs @@ -6,12 +6,12 @@ use std::cell::LazyCell; use dom_struct::dom_struct; +use script_bindings::codegen::InheritTypes::{CharacterDataTypeId, NodeTypeId, TextTypeId}; use crate::dom::bindings::cell::{DomRefCell, Ref}; use crate::dom::bindings::codegen::Bindings::CharacterDataBinding::CharacterDataMethods; use crate::dom::bindings::codegen::Bindings::NodeBinding::Node_Binding::NodeMethods; use crate::dom::bindings::codegen::Bindings::ProcessingInstructionBinding::ProcessingInstructionMethods; -use crate::dom::bindings::codegen::InheritTypes::{CharacterDataTypeId, NodeTypeId, TextTypeId}; use crate::dom::bindings::codegen::UnionTypes::NodeOrString; use crate::dom::bindings::error::{Error, ErrorResult, Fallible}; use crate::dom::bindings::inheritance::Castable; diff --git a/components/script/dom/clipboardevent.rs b/components/script/dom/clipboardevent.rs index eccf590e792..3cacb18a28a 100644 --- a/components/script/dom/clipboardevent.rs +++ b/components/script/dom/clipboardevent.rs @@ -41,7 +41,7 @@ impl ClipboardEventType { } #[dom_struct] -pub struct ClipboardEvent { +pub(crate) struct ClipboardEvent { event: Event, clipboard_data: MutNullableDom, } diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 146ef3ffacb..dd521776702 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -4384,7 +4384,7 @@ impl Document { // https://fullscreen.spec.whatwg.org/#dom-element-requestfullscreen pub(crate) fn enter_fullscreen(&self, pending: &Element, can_gc: CanGc) -> Rc { // Step 1 - let in_realm_proof = AlreadyInRealm::assert(); + let in_realm_proof = AlreadyInRealm::assert::(); let promise = Promise::new_in_current_realm(InRealm::Already(&in_realm_proof), can_gc); let mut error = false; @@ -4452,7 +4452,7 @@ impl Document { pub(crate) fn exit_fullscreen(&self, can_gc: CanGc) -> Rc { let global = self.global(); // Step 1 - let in_realm_proof = AlreadyInRealm::assert(); + let in_realm_proof = AlreadyInRealm::assert::(); let promise = Promise::new_in_current_realm(InRealm::Already(&in_realm_proof), can_gc); // Step 2 if self.fullscreen_element.get().is_none() { diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs index cb700d7d4c2..7b8169a06a6 100644 --- a/components/script/dom/globalscope.rs +++ b/components/script/dom/globalscope.rs @@ -2696,7 +2696,7 @@ impl GlobalScope { options: &ImageBitmapOptions, can_gc: CanGc, ) -> Rc { - let in_realm_proof = AlreadyInRealm::assert(); + let in_realm_proof = AlreadyInRealm::assert::(); let p = Promise::new_in_current_realm(InRealm::Already(&in_realm_proof), can_gc); if options.resizeWidth.is_some_and(|w| w == 0) { p.reject_error(Error::InvalidState, can_gc); diff --git a/components/script/dom/htmlmediaelement.rs b/components/script/dom/htmlmediaelement.rs index 94c962968dc..31b0d5c6d5d 100644 --- a/components/script/dom/htmlmediaelement.rs +++ b/components/script/dom/htmlmediaelement.rs @@ -27,6 +27,9 @@ use net_traits::{ ResourceTimingType, }; use pixels::Image; +use script_bindings::codegen::InheritTypes::{ + ElementTypeId, HTMLElementTypeId, HTMLMediaElementTypeId, NodeTypeId, +}; use script_layout_interface::MediaFrame; use servo_config::pref; use servo_media::player::audio::AudioRenderer; @@ -60,9 +63,6 @@ use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::{ use crate::dom::bindings::codegen::Bindings::TextTrackBinding::{TextTrackKind, TextTrackMode}; use crate::dom::bindings::codegen::Bindings::URLBinding::URLMethods; use crate::dom::bindings::codegen::Bindings::WindowBinding::Window_Binding::WindowMethods; -use crate::dom::bindings::codegen::InheritTypes::{ - ElementTypeId, HTMLElementTypeId, HTMLMediaElementTypeId, NodeTypeId, -}; use crate::dom::bindings::codegen::UnionTypes::{ MediaStreamOrBlob, VideoTrackOrAudioTrackOrTextTrack, }; diff --git a/components/script/dom/htmlslotelement.rs b/components/script/dom/htmlslotelement.rs index 2deb6b068e4..022cbbf388a 100644 --- a/components/script/dom/htmlslotelement.rs +++ b/components/script/dom/htmlslotelement.rs @@ -8,6 +8,7 @@ use dom_struct::dom_struct; use html5ever::{LocalName, Prefix, local_name, namespace_url, ns}; use js::gc::RootedVec; use js::rust::HandleObject; +use script_bindings::codegen::InheritTypes::{CharacterDataTypeId, NodeTypeId}; use crate::ScriptThread; use crate::dom::attr::Attr; @@ -19,7 +20,6 @@ use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::ShadowRoot_Bindi use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::{ ShadowRootMode, SlotAssignmentMode, }; -use crate::dom::bindings::codegen::InheritTypes::{CharacterDataTypeId, NodeTypeId}; use crate::dom::bindings::codegen::UnionTypes::ElementOrText; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::root::{Dom, DomRoot}; @@ -35,7 +35,7 @@ use crate::script_runtime::CanGc; /// #[dom_struct] -pub struct HTMLSlotElement { +pub(crate) struct HTMLSlotElement { htmlelement: HTMLElement, /// diff --git a/components/script/dom/mediadevices.rs b/components/script/dom/mediadevices.rs index f2f5a45d09c..78d145103a5 100644 --- a/components/script/dom/mediadevices.rs +++ b/components/script/dom/mediadevices.rs @@ -79,7 +79,7 @@ impl MediaDevicesMethods for MediaDevices { /// fn EnumerateDevices(&self, can_gc: CanGc) -> Rc { // Step 1. - let in_realm_proof = AlreadyInRealm::assert(); + let in_realm_proof = AlreadyInRealm::assert::(); let p = Promise::new_in_current_realm(InRealm::Already(&in_realm_proof), can_gc); // Step 2. diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index 717e5a1f5b9..a34e0b44221 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -205,6 +205,7 @@ #[macro_use] pub(crate) mod macros; +#[allow(unused_imports)] pub(crate) mod types { include!(concat!(env!("BINDINGS_OUT_DIR"), "/InterfaceTypes.rs")); } diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index 5cbcbb6f43c..d89263d2bf2 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -28,6 +28,7 @@ use js::rust::HandleObject; use libc::{self, c_void, uintptr_t}; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; use pixels::{Image, ImageMetadata}; +use script_bindings::codegen::InheritTypes::DocumentFragmentTypeId; use script_layout_interface::{ GenericLayoutData, HTMLCanvasData, HTMLMediaData, LayoutElementType, LayoutNodeType, QueryMsg, SVGSVGData, StyleData, TrustedNodeAddress, @@ -71,7 +72,6 @@ use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::{ ShadowRootMode, SlotAssignmentMode, }; use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; -use crate::dom::bindings::codegen::InheritTypes::DocumentFragmentTypeId; use crate::dom::bindings::codegen::UnionTypes::NodeOrString; use crate::dom::bindings::conversions::{self, DerivedFrom}; use crate::dom::bindings::error::{Error, ErrorResult, Fallible}; diff --git a/components/script/dom/permissions.rs b/components/script/dom/permissions.rs index 0be136b8aff..ed611db79a8 100644 --- a/components/script/dom/permissions.rs +++ b/components/script/dom/permissions.rs @@ -95,7 +95,7 @@ impl Permissions { let p = match promise { Some(promise) => promise, None => { - let in_realm_proof = AlreadyInRealm::assert(); + let in_realm_proof = AlreadyInRealm::assert::(); Promise::new_in_current_realm(InRealm::Already(&in_realm_proof), can_gc) }, }; diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 0479f24fac8..4da445c20c6 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -57,6 +57,7 @@ use num_traits::ToPrimitive; use profile_traits::ipc as ProfiledIpc; use profile_traits::mem::ProfilerChan as MemProfilerChan; use profile_traits::time::ProfilerChan as TimeProfilerChan; +use script_bindings::interfaces::WindowHelpers; use script_layout_interface::{ FragmentType, Layout, PendingImageState, QueryMsg, Reflow, ReflowGoal, ReflowRequest, TrustedNodeAddress, combine_id_with_fragment_type, @@ -3122,3 +3123,13 @@ unsafe extern "C" fn dump_js_stack(cx: *mut RawJSContext) { DumpJSStack(cx, true, false, false); } } + +impl WindowHelpers for Window { + fn create_named_properties_object( + cx: JSContext, + proto: HandleObject, + object: MutableHandleObject, + ) { + Self::create_named_properties_object(cx, proto, object) + } +} diff --git a/components/script/realms.rs b/components/script/realms.rs index 434aa93a0a2..1254b604b42 100644 --- a/components/script/realms.rs +++ b/components/script/realms.rs @@ -4,17 +4,18 @@ use js::jsapi::{GetCurrentRealmOrNull, JSAutoRealm}; +use crate::DomTypes; use crate::dom::bindings::reflector::DomObject; -use crate::dom::globalscope::GlobalScope; +use crate::dom::globalscope::GlobalScopeHelpers; use crate::script_runtime::JSContext; pub(crate) struct AlreadyInRealm(()); impl AlreadyInRealm { #![allow(unsafe_code)] - pub(crate) fn assert() -> AlreadyInRealm { + pub(crate) fn assert() -> AlreadyInRealm { unsafe { - assert!(!GetCurrentRealmOrNull(*GlobalScope::get_cx()).is_null()); + assert!(!GetCurrentRealmOrNull(*D::GlobalScope::get_cx()).is_null()); } AlreadyInRealm(()) } @@ -55,9 +56,13 @@ impl InRealm<'_> { } } -pub(crate) fn enter_realm(object: &impl DomObject) -> JSAutoRealm { +pub(crate) fn enter_realm_generic(object: &impl DomObject) -> JSAutoRealm { JSAutoRealm::new( - *GlobalScope::get_cx(), + *D::GlobalScope::get_cx(), object.reflector().get_jsobject().get(), ) } + +pub(crate) fn enter_realm(object: &impl DomObject) -> JSAutoRealm { + enter_realm_generic::(object) +} diff --git a/components/script_bindings/codegen/Bindings.conf b/components/script_bindings/codegen/Bindings.conf index 3f356d5dff0..f305b74ab01 100644 --- a/components/script_bindings/codegen/Bindings.conf +++ b/components/script_bindings/codegen/Bindings.conf @@ -588,6 +588,7 @@ DOMInterfaces = { 'Window': { 'canGc': ['Stop', 'Fetch', 'Scroll', 'Scroll_','ScrollBy', 'ScrollBy_', 'Stop', 'Fetch', 'Open', 'CreateImageBitmap'], 'inRealms': ['Fetch', 'GetOpener'], + 'additionalTraits': ['script_bindings::interfaces::WindowHelpers'], }, 'WindowProxy' : { diff --git a/components/script_bindings/codegen/CodegenRust.py b/components/script_bindings/codegen/CodegenRust.py index 783590aad70..09a4bd4139f 100644 --- a/components/script_bindings/codegen/CodegenRust.py +++ b/components/script_bindings/codegen/CodegenRust.py @@ -2279,8 +2279,6 @@ class CGImports(CGWrapper): if t.isInterface() or t.isNamespace(): name = getIdentifier(t).name descriptor = descriptorProvider.getDescriptor(name) - if name != 'GlobalScope': - extras += [descriptor.path] parentName = descriptor.getParentName() while parentName: descriptor = descriptorProvider.getDescriptor(parentName) @@ -2289,7 +2287,7 @@ class CGImports(CGWrapper): elif t.isType() and t.isRecord(): extras += ['script_bindings::record::Record'] elif isinstance(t, IDLPromiseType): - extras += ['crate::dom::promise::Promise'] + pass else: if t.isEnum(): extras += [f'{getModuleFromObject(t)}::{getIdentifier(t).name}Values'] @@ -2339,15 +2337,15 @@ def DOMClassTypeId(desc): inner = "" if desc.hasDescendants(): if desc.interface.getExtendedAttribute("Abstract"): - return "crate::dom::bindings::codegen::InheritTypes::TopTypeId { abstract_: () }" + return "script_bindings::codegen::InheritTypes::TopTypeId { abstract_: () }" name = desc.interface.identifier.name - inner = f"(crate::dom::bindings::codegen::InheritTypes::{name}TypeId::{name})" + inner = f"(script_bindings::codegen::InheritTypes::{name}TypeId::{name})" elif len(protochain) == 1: - return "crate::dom::bindings::codegen::InheritTypes::TopTypeId { alone: () }" + return "script_bindings::codegen::InheritTypes::TopTypeId { alone: () }" reversed_protochain = list(reversed(protochain)) for (child, parent) in zip(reversed_protochain, reversed_protochain[1:]): - inner = f"(crate::dom::bindings::codegen::InheritTypes::{parent}TypeId::{child}{inner})" - return f"crate::dom::bindings::codegen::InheritTypes::TopTypeId {{ {protochain[0].lower()}: {inner} }}" + inner = f"(script_bindings::codegen::InheritTypes::{parent}TypeId::{child}{inner})" + return f"script_bindings::codegen::InheritTypes::TopTypeId {{ {protochain[0].lower()}: {inner} }}" def DOMClass(descriptor): @@ -2398,10 +2396,10 @@ class CGDOMJSClass(CGThing): } if self.descriptor.isGlobal(): assert not self.descriptor.weakReferenceable - args["enumerateHook"] = "Some(enumerate_global)" + args["enumerateHook"] = "Some(enumerate_global::)" args["flags"] = "JSCLASS_IS_GLOBAL | JSCLASS_DOM_GLOBAL | JSCLASS_FOREGROUND_FINALIZE" args["slots"] = "JSCLASS_GLOBAL_SLOT_COUNT + 1" - args["resolveHook"] = "Some(resolve_global)" + args["resolveHook"] = "Some(resolve_global::)" args["traceHook"] = "js::jsapi::JS_GlobalObjectTraceHook" elif self.descriptor.weakReferenceable: args["slots"] = "2" @@ -2423,7 +2421,7 @@ pub(crate) fn init_class_ops() {{ }}); }} -static Class: ThreadUnsafeOnceLock = ThreadUnsafeOnceLock::new(); +pub(crate) static Class: ThreadUnsafeOnceLock = ThreadUnsafeOnceLock::new(); pub(crate) fn init_domjs_class() {{ init_class_ops::(); @@ -3185,7 +3183,7 @@ let raw = Root::new(MaybeUnreflectedDom::from_box(object)); let origin = (*raw.as_ptr()).upcast::().origin(); rooted!(in(*cx) let mut obj = ptr::null_mut::()); -create_global_object( +create_global_object::( cx, &Class.get().base, raw.as_ptr() as *const libc::c_void, @@ -3229,14 +3227,15 @@ class CGIDLInterface(CGThing): def define(self): interface = self.descriptor.interface name = interface.identifier.name + bindingModule = f"crate::dom::bindings::codegen::GenericBindings::{toBindingPath(self.descriptor)}" if (interface.getUserData("hasConcreteDescendant", False) or interface.getUserData("hasProxyDescendant", False)): depth = self.descriptor.prototypeDepth check = f"class.interface_chain[{depth}] == PrototypeList::ID::{name}" elif self.descriptor.proxy: - check = "ptr::eq(class, unsafe { Class.get() })" + check = f"ptr::eq(class, unsafe {{ {bindingModule}::Class.get() }})" else: - check = "ptr::eq(class, unsafe { &Class.get().dom_class })" + check = f"ptr::eq(class, unsafe {{ &{bindingModule}::Class.get().dom_class }})" return f""" impl IDLInterface for {name} {{ #[inline] @@ -3279,6 +3278,7 @@ class CGDomObjectWrap(CGThing): def define(self): ifaceName = self.descriptor.interface.identifier.name + bindingModule = f"crate::dom::bindings::codegen::GenericBindings::{toBindingPath(self.descriptor)}" return f""" impl DomObjectWrap for {firstCap(ifaceName)} {{ const WRAP: unsafe fn( @@ -3287,7 +3287,7 @@ impl DomObjectWrap for {firstCap(ifaceName)} {{ Option, Box, CanGc, - ) -> Root> = Wrap::; + ) -> Root> = {bindingModule}::Wrap::; }} """ @@ -3303,6 +3303,7 @@ class CGDomObjectIteratorWrap(CGThing): def define(self): assert self.descriptor.interface.isIteratorInterface() name = self.descriptor.interface.iterableInterface.identifier.name + bindingModule = f"crate::dom::bindings::codegen::GenericBindings::{toBindingPath(self.descriptor)}" return f""" impl DomObjectIteratorWrap for {name} {{ const ITER_WRAP: unsafe fn( @@ -3311,7 +3312,7 @@ impl DomObjectIteratorWrap for {name} {{ Option, Box>, CanGc, - ) -> Root>> = Wrap::; + ) -> Root>> = {bindingModule}::Wrap::; }} """ @@ -3516,7 +3517,7 @@ assert!(!prototype_proto.is_null());""")] assert not self.haveUnscopables code.append(CGGeneric(f""" rooted!(in(*cx) let mut prototype_proto_proto = prototype_proto.get()); -dom::types::{name}::create_named_properties_object(cx, prototype_proto_proto.handle(), prototype_proto.handle_mut()); +D::{name}::create_named_properties_object(cx, prototype_proto_proto.handle(), prototype_proto.handle_mut()); assert!(!prototype_proto.is_null());""")) properties = { @@ -3797,7 +3798,7 @@ class CGDefineProxyHandler(CGAbstractMethod): # `[[Set]]` (https://heycam.github.io/webidl/#legacy-platform-object-set) (yet). assert not self.descriptor.operations['IndexedGetter'] assert not self.descriptor.operations['NamedGetter'] - customSet = 'Some(proxyhandler::maybe_cross_origin_set_rawcx)' + customSet = 'Some(proxyhandler::maybe_cross_origin_set_rawcx::)' getOwnEnumerablePropertyKeys = "own_property_keys::" if self.descriptor.interface.getExtendedAttribute("LegacyUnenumerableNamedProperties") or \ @@ -3944,7 +3945,7 @@ class CGCallGenerator(CGThing): call = CGList([call, CGWrapper(args, pre="(", post=")")]) if hasCEReactions: - self.cgRoot.append(CGGeneric("push_new_element_queue();\n")) + self.cgRoot.append(CGGeneric(">::push_new_element_queue();\n")) self.cgRoot.append(CGList([ CGGeneric("let result: "), @@ -3955,7 +3956,7 @@ class CGCallGenerator(CGThing): ])) if hasCEReactions: - self.cgRoot.append(CGGeneric("pop_current_element_queue(CanGc::note());\n")) + self.cgRoot.append(CGGeneric(">::pop_current_element_queue(CanGc::note());\n")) if isFallible: if static: @@ -3967,7 +3968,7 @@ class CGCallGenerator(CGThing): "let result = match result {\n" " Ok(result) => result,\n" " Err(e) => {\n" - f" >::throw_dom_exception(cx, {glob}, e);\n" + f" >::throw_dom_exception(cx, {glob}, e, CanGc::note());\n" f" return{errorResult};\n" " },\n" "};")) @@ -5911,14 +5912,14 @@ class CGDOMJSProxyHandler_getOwnPropertyDescriptor(CGAbstractExternMethod): if self.descriptor.isMaybeCrossOriginObject(): get += dedent( """ - if !proxyhandler::is_platform_object_same_origin(cx, proxy) { + if !>::is_platform_object_same_origin(cx, proxy) { if !proxyhandler::cross_origin_get_own_property_helper( cx, proxy, CROSS_ORIGIN_PROPERTIES.get(), id, desc, &mut *is_none ) { return false; } if *is_none { - return proxyhandler::cross_origin_property_fallback(cx, proxy, id, desc, &mut *is_none); + return proxyhandler::cross_origin_property_fallback::(cx, proxy, id, desc, &mut *is_none); } return true; } @@ -6033,8 +6034,8 @@ class CGDOMJSProxyHandler_defineProperty(CGAbstractExternMethod): if self.descriptor.isMaybeCrossOriginObject(): set += dedent( """ - if !proxyhandler::is_platform_object_same_origin(cx, proxy) { - return proxyhandler::report_cross_origin_denial(cx, id, "define"); + if !>::is_platform_object_same_origin(cx, proxy) { + return proxyhandler::report_cross_origin_denial::(cx, id, "define"); } // Safe to enter the Realm of proxy now. @@ -6092,8 +6093,8 @@ class CGDOMJSProxyHandler_delete(CGAbstractExternMethod): if self.descriptor.isMaybeCrossOriginObject(): set += dedent( """ - if !proxyhandler::is_platform_object_same_origin(cx, proxy) { - return proxyhandler::report_cross_origin_denial(cx, id, "delete"); + if !>::is_platform_object_same_origin(cx, proxy) { + return proxyhandler::report_cross_origin_denial::(cx, id, "delete"); } // Safe to enter the Realm of proxy now. @@ -6131,7 +6132,7 @@ class CGDOMJSProxyHandler_ownPropertyKeys(CGAbstractExternMethod): if self.descriptor.isMaybeCrossOriginObject(): body += dedent( """ - if !proxyhandler::is_platform_object_same_origin(cx, proxy) { + if !>::is_platform_object_same_origin(cx, proxy) { return proxyhandler::cross_origin_own_property_keys( cx, proxy, CROSS_ORIGIN_PROPERTIES.get(), props ); @@ -6204,7 +6205,7 @@ class CGDOMJSProxyHandler_getOwnEnumerablePropertyKeys(CGAbstractExternMethod): if self.descriptor.isMaybeCrossOriginObject(): body += dedent( """ - if !proxyhandler::is_platform_object_same_origin(cx, proxy) { + if !>::is_platform_object_same_origin(cx, proxy) { // There are no enumerable cross-origin props, so we're done. return true; } @@ -6255,7 +6256,7 @@ class CGDOMJSProxyHandler_hasOwn(CGAbstractExternMethod): if self.descriptor.isMaybeCrossOriginObject(): indexed += dedent( """ - if !proxyhandler::is_platform_object_same_origin(cx, proxy) { + if !>::is_platform_object_same_origin(cx, proxy) { return proxyhandler::cross_origin_has_own( cx, proxy, CROSS_ORIGIN_PROPERTIES.get(), id, bp ); @@ -6328,8 +6329,8 @@ class CGDOMJSProxyHandler_get(CGAbstractExternMethod): if self.descriptor.isMaybeCrossOriginObject(): maybeCrossOriginGet = dedent( """ - if !proxyhandler::is_platform_object_same_origin(cx, proxy) { - return proxyhandler::cross_origin_get(cx, proxy, receiver, id, vp); + if !>::is_platform_object_same_origin(cx, proxy) { + return proxyhandler::cross_origin_get::(cx, proxy, receiver, id, vp); } // Safe to enter the Realm of proxy now. @@ -6589,7 +6590,7 @@ class CGDOMJSProxyHandlerDOMClass(CGThing): def define(self): return f""" -static Class: ThreadUnsafeOnceLock = ThreadUnsafeOnceLock::new(); +pub(crate) static Class: ThreadUnsafeOnceLock = ThreadUnsafeOnceLock::new(); pub(crate) fn init_proxy_handler_dom_class() {{ Class.set({DOMClass(self.descriptor)}); @@ -6990,18 +6991,11 @@ class CGDescriptor(CGThing): pass else: cgThings.append(CGDOMJSClass(descriptor)) - if not descriptor.interface.isIteratorInterface(): - cgThings.append(CGAssertInheritance(descriptor)) - pass if descriptor.isGlobal(): cgThings.append(CGWrapGlobalMethod(descriptor, properties)) else: cgThings.append(CGWrapMethod(descriptor)) - if descriptor.interface.isIteratorInterface(): - cgThings.append(CGDomObjectIteratorWrap(descriptor)) - else: - cgThings.append(CGDomObjectWrap(descriptor)) reexports.append('Wrap') haveUnscopables = False @@ -7013,16 +7007,6 @@ class CGDescriptor(CGThing): CGIndenter(CGList([CGGeneric(str_to_cstr(name)) for name in unscopableNames], ",\n")), CGGeneric("];\n")], "\n")) - if ( - (descriptor.concrete or descriptor.hasDescendants()) - and not descriptor.interface.isIteratorInterface() - ): - cgThings.append(CGIDLInterface(descriptor)) - if descriptor.interface.isIteratorInterface(): - cgThings.append(CGIteratorDerives(descriptor)) - - if descriptor.weakReferenceable: - cgThings.append(CGWeakReferenceableTrait(descriptor)) if not descriptor.interface.isCallback(): interfaceTrait = CGInterfaceTrait(descriptor, config.getDescriptorProvider()) @@ -7590,6 +7574,27 @@ class CGConcreteBindingRoot(CGThing): f"pub(crate) use {originalBinding}::{firstCap(ifaceName)}_Binding as {firstCap(ifaceName)}_Binding;" ), ] + + if d.concrete: + if not d.interface.isIteratorInterface(): + cgthings.append(CGAssertInheritance(d)) + else: + cgthings.append(CGIteratorDerives(d)) + + if ( + (d.concrete or d.hasDescendants()) + and not d.interface.isIteratorInterface() + ): + cgthings.append(CGIDLInterface(d)) + + if d.interface.isIteratorInterface(): + cgthings.append(CGDomObjectIteratorWrap(d)) + elif d.concrete and not d.isGlobal(): + cgthings.append(CGDomObjectWrap(d)) + + if d.weakReferenceable: + cgthings.append(CGWeakReferenceableTrait(d)) + if not d.interface.isCallback(): traitName = f"{ifaceName}Methods" cgthings += [ @@ -7625,7 +7630,7 @@ pub(crate) fn GetConstructorObject( curr = CGImports(curr, descriptors=[], callbacks=[], dictionaries=[], enums=[], typedefs=[], imports=[ - 'crate::dom::bindings::import::base::*', + 'crate::dom::bindings::import::module::*', 'crate::dom::types::*'], config=config) diff --git a/components/script_bindings/inheritance.rs b/components/script_bindings/inheritance.rs index 5ddbf072215..baa70c81395 100644 --- a/components/script_bindings/inheritance.rs +++ b/components/script_bindings/inheritance.rs @@ -51,3 +51,10 @@ pub trait Castable: IDLInterface + DomObject + Sized { } } } + +#[allow(missing_docs)] +pub trait HasParent { + #[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)] + type Parent; + fn as_parent(&self) -> &Self::Parent; +} diff --git a/components/script_bindings/interfaces.rs b/components/script_bindings/interfaces.rs index 2e7f6017e75..ce8fc71f23b 100644 --- a/components/script_bindings/interfaces.rs +++ b/components/script_bindings/interfaces.rs @@ -2,7 +2,7 @@ * 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 js::rust::HandleObject; +use js::rust::{HandleObject, MutableHandleObject}; use crate::script_runtime::JSContext; @@ -22,3 +22,11 @@ pub trait TestBindingHelpers { pub trait WebGL2RenderingContextHelpers { fn is_webgl2_enabled(cx: JSContext, global: HandleObject) -> bool; } + +pub trait WindowHelpers { + fn create_named_properties_object( + cx: JSContext, + proto: HandleObject, + object: MutableHandleObject, + ); +} diff --git a/components/script_bindings/lib.rs b/components/script_bindings/lib.rs index 17dc827b872..0ecad1ca07c 100644 --- a/components/script_bindings/lib.rs +++ b/components/script_bindings/lib.rs @@ -28,6 +28,7 @@ pub mod iterable; pub mod like; pub mod lock; pub mod num; +pub mod proxyhandler; pub mod record; pub mod reflector; pub mod root; diff --git a/components/script_bindings/proxyhandler.rs b/components/script_bindings/proxyhandler.rs new file mode 100644 index 00000000000..7614479ac59 --- /dev/null +++ b/components/script_bindings/proxyhandler.rs @@ -0,0 +1,494 @@ +/* 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/. */ + +//! Utilities for the implementation of JSAPI proxy handlers. + +use std::ffi::CStr; +use std::os::raw::c_char; +use std::ptr; + +use js::glue::{GetProxyHandlerFamily, GetProxyPrivate, SetProxyPrivate}; +use js::jsapi::{ + DOMProxyShadowsResult, GetStaticPrototype, GetWellKnownSymbol, Handle as RawHandle, + HandleId as RawHandleId, HandleObject as RawHandleObject, JS_AtomizeAndPinString, + JS_DefinePropertyById, JS_GetOwnPropertyDescriptorById, JSContext, JSErrNum, JSFunctionSpec, + JSObject, JSPropertySpec, MutableHandle as RawMutableHandle, + MutableHandleIdVector as RawMutableHandleIdVector, + MutableHandleObject as RawMutableHandleObject, ObjectOpResult, PropertyDescriptor, + SetDOMProxyInformation, SymbolCode, jsid, +}; +use js::jsid::SymbolId; +use js::jsval::{ObjectValue, UndefinedValue}; +use js::rust::wrappers::{ + AppendToIdVector, JS_AlreadyHasOwnPropertyById, JS_NewObjectWithGivenProto, + RUST_INTERNED_STRING_TO_JSID, SetDataPropertyDescriptor, +}; +use js::rust::{Handle, HandleObject, HandleValue, MutableHandle, MutableHandleObject}; +use js::{jsapi, rooted}; + +use crate::conversions::{is_dom_proxy, jsid_to_string, jsstring_to_str}; +use crate::script_runtime::JSContext as SafeJSContext; +use crate::str::DOMString; +use crate::utils::delete_property_by_id; + +/// Determine if this id shadows any existing properties for this proxy. +/// +/// # Safety +/// `cx` must point to a valid, non-null JSContext. +pub unsafe extern "C" fn shadow_check_callback( + cx: *mut JSContext, + object: RawHandleObject, + id: RawHandleId, +) -> DOMProxyShadowsResult { + // TODO: support OverrideBuiltins when #12978 is fixed. + + rooted!(in(cx) let mut expando = ptr::null_mut::()); + get_expando_object(object, expando.handle_mut()); + if !expando.get().is_null() { + let mut has_own = false; + let raw_id = Handle::from_raw(id); + + if !JS_AlreadyHasOwnPropertyById(cx, expando.handle(), raw_id, &mut has_own) { + return DOMProxyShadowsResult::ShadowCheckFailed; + } + + if has_own { + return DOMProxyShadowsResult::ShadowsViaDirectExpando; + } + } + + // Our expando, if any, didn't shadow, so we're not shadowing at all. + DOMProxyShadowsResult::DoesntShadow +} + +/// Initialize the infrastructure for DOM proxy objects. +pub fn init() { + unsafe { + SetDOMProxyInformation( + GetProxyHandlerFamily(), + Some(shadow_check_callback), + ptr::null(), + ); + } +} + +/// Defines an expando on the given `proxy`. +/// +/// # Safety +/// `cx` must point to a valid, non-null JSContext. +/// `result` must point to a valid, non-null ObjectOpResult. +pub unsafe extern "C" fn define_property( + cx: *mut JSContext, + proxy: RawHandleObject, + id: RawHandleId, + desc: RawHandle, + result: *mut ObjectOpResult, +) -> bool { + rooted!(in(cx) let mut expando = ptr::null_mut::()); + ensure_expando_object(cx, proxy, expando.handle_mut()); + JS_DefinePropertyById(cx, expando.handle().into(), id, desc, result) +} + +/// Deletes an expando off the given `proxy`. +/// +/// # Safety +/// `cx` must point to a valid, non-null JSContext. +/// `bp` must point to a valid, non-null ObjectOpResult. +pub unsafe extern "C" fn delete( + cx: *mut JSContext, + proxy: RawHandleObject, + id: RawHandleId, + bp: *mut ObjectOpResult, +) -> bool { + rooted!(in(cx) let mut expando = ptr::null_mut::()); + get_expando_object(proxy, expando.handle_mut()); + if expando.is_null() { + (*bp).code_ = 0 /* OkCode */; + return true; + } + + delete_property_by_id(cx, expando.handle(), Handle::from_raw(id), bp) +} + +/// Controls whether the Extensible bit can be changed +/// +/// # Safety +/// `result` must point to a valid, non-null ObjectOpResult. +pub unsafe extern "C" fn prevent_extensions( + _cx: *mut JSContext, + _proxy: RawHandleObject, + result: *mut ObjectOpResult, +) -> bool { + (*result).code_ = JSErrNum::JSMSG_CANT_PREVENT_EXTENSIONS as ::libc::uintptr_t; + true +} + +/// Reports whether the object is Extensible +/// +/// # Safety +/// `succeeded` must point to a valid, non-null bool. +pub unsafe extern "C" fn is_extensible( + _cx: *mut JSContext, + _proxy: RawHandleObject, + succeeded: *mut bool, +) -> bool { + *succeeded = true; + true +} + +/// If `proxy` (underneath any functionally-transparent wrapper proxies) has as +/// its `[[GetPrototypeOf]]` trap the ordinary `[[GetPrototypeOf]]` behavior +/// defined for ordinary objects, set `*is_ordinary` to true and store `obj`'s +/// prototype in `proto`. Otherwise set `*isOrdinary` to false. In case of +/// error, both outparams have unspecified value. +/// +/// This implementation always handles the case of the ordinary +/// `[[GetPrototypeOf]]` behavior. An alternative implementation will be +/// necessary for maybe-cross-origin objects. +/// +/// # Safety +/// `is_ordinary` must point to a valid, non-null bool. +pub unsafe extern "C" fn get_prototype_if_ordinary( + _: *mut JSContext, + proxy: RawHandleObject, + is_ordinary: *mut bool, + proto: RawMutableHandleObject, +) -> bool { + *is_ordinary = true; + proto.set(GetStaticPrototype(proxy.get())); + true +} + +/// Get the expando object, or null if there is none. +pub fn get_expando_object(obj: RawHandleObject, mut expando: MutableHandleObject) { + unsafe { + assert!(is_dom_proxy(obj.get())); + let val = &mut UndefinedValue(); + GetProxyPrivate(obj.get(), val); + expando.set(if val.is_undefined() { + ptr::null_mut() + } else { + val.to_object() + }); + } +} + +/// Get the expando object, or create it if it doesn't exist yet. +/// Fails on JSAPI failure. +/// +/// # Safety +/// `cx` must point to a valid, non-null JSContext. +pub unsafe fn ensure_expando_object( + cx: *mut JSContext, + obj: RawHandleObject, + mut expando: MutableHandleObject, +) { + assert!(is_dom_proxy(obj.get())); + get_expando_object(obj, expando.reborrow()); + if expando.is_null() { + expando.set(JS_NewObjectWithGivenProto( + cx, + ptr::null_mut(), + HandleObject::null(), + )); + assert!(!expando.is_null()); + + SetProxyPrivate(obj.get(), &ObjectValue(expando.get())); + } +} + +/// Set the property descriptor's object to `obj` and set it to enumerable, +/// and writable if `readonly` is true. +pub fn set_property_descriptor( + desc: MutableHandle, + value: HandleValue, + attrs: u32, + is_none: &mut bool, +) { + unsafe { + SetDataPropertyDescriptor(desc, value, attrs); + } + *is_none = false; +} + +pub fn id_to_source(cx: SafeJSContext, id: RawHandleId) -> Option { + unsafe { + rooted!(in(*cx) let mut value = UndefinedValue()); + rooted!(in(*cx) let mut jsstr = ptr::null_mut::()); + jsapi::JS_IdToValue(*cx, id.get(), value.handle_mut().into()) + .then(|| { + jsstr.set(jsapi::JS_ValueToSource(*cx, value.handle().into())); + jsstr.get() + }) + .and_then(ptr::NonNull::new) + .map(|jsstr| jsstring_to_str(*cx, jsstr)) + } +} + +/// Property and method specs that correspond to the elements of +/// [`CrossOriginProperties(O)`]. +/// +/// [`CrossOriginProperties(O)`]: https://html.spec.whatwg.org/multipage/#crossoriginproperties-(-o-) +pub struct CrossOriginProperties { + pub attributes: &'static [JSPropertySpec], + pub methods: &'static [JSFunctionSpec], +} + +impl CrossOriginProperties { + /// Enumerate the property keys defined by `self`. + fn keys(&self) -> impl Iterator + '_ { + // Safety: All cross-origin property keys are strings, not symbols + self.attributes + .iter() + .map(|spec| unsafe { spec.name.string_ }) + .chain(self.methods.iter().map(|spec| unsafe { spec.name.string_ })) + .filter(|ptr| !ptr.is_null()) + } +} + +/// Implementation of [`CrossOriginOwnPropertyKeys`]. +/// +/// [`CrossOriginOwnPropertyKeys`]: https://html.spec.whatwg.org/multipage/#crossoriginownpropertykeys-(-o-) +pub fn cross_origin_own_property_keys( + cx: SafeJSContext, + _proxy: RawHandleObject, + cross_origin_properties: &'static CrossOriginProperties, + props: RawMutableHandleIdVector, +) -> bool { + // > 2. For each `e` of `! CrossOriginProperties(O)`, append + // > `e.[[Property]]` to `keys`. + for key in cross_origin_properties.keys() { + unsafe { + rooted!(in(*cx) let rooted = JS_AtomizeAndPinString(*cx, key)); + rooted!(in(*cx) let mut rooted_jsid: jsid); + RUST_INTERNED_STRING_TO_JSID(*cx, rooted.handle().get(), rooted_jsid.handle_mut()); + AppendToIdVector(props, rooted_jsid.handle()); + } + } + + // > 3. Return the concatenation of `keys` and `« "then", @@toStringTag, + // > @@hasInstance, @@isConcatSpreadable »`. + append_cross_origin_allowlisted_prop_keys(cx, props); + + true +} + +/// # Safety +/// `is_ordinary` must point to a valid, non-null bool. +pub unsafe extern "C" fn maybe_cross_origin_get_prototype_if_ordinary_rawcx( + _: *mut JSContext, + _proxy: RawHandleObject, + is_ordinary: *mut bool, + _proto: RawMutableHandleObject, +) -> bool { + // We have a custom `[[GetPrototypeOf]]`, so return `false` + *is_ordinary = false; + true +} + +/// Implementation of `[[SetPrototypeOf]]` for [`Location`] and [`WindowProxy`]. +/// +/// [`Location`]: https://html.spec.whatwg.org/multipage/#location-setprototypeof +/// [`WindowProxy`]: https://html.spec.whatwg.org/multipage/#windowproxy-setprototypeof +/// +/// # Safety +/// `result` must point to a valid, non-null ObjectOpResult. +pub unsafe extern "C" fn maybe_cross_origin_set_prototype_rawcx( + cx: *mut JSContext, + proxy: RawHandleObject, + proto: RawHandleObject, + result: *mut ObjectOpResult, +) -> bool { + // > 1. Return `! SetImmutablePrototype(this, V)`. + // + // : + // + // > 1. Assert: Either `Type(V)` is Object or `Type(V)` is Null. + // + // > 2. Let current be `? O.[[GetPrototypeOf]]()`. + rooted!(in(cx) let mut current = ptr::null_mut::()); + if !jsapi::GetObjectProto(cx, proxy, current.handle_mut().into()) { + return false; + } + + // > 3. If `SameValue(V, current)` is true, return true. + if proto.get() == current.get() { + (*result).code_ = 0 /* OkCode */; + return true; + } + + // > 4. Return false. + (*result).code_ = JSErrNum::JSMSG_CANT_SET_PROTO as usize; + true +} + +pub fn get_getter_object(d: &PropertyDescriptor, out: RawMutableHandleObject) { + if d.hasGetter_() { + out.set(d.getter_); + } +} + +pub fn get_setter_object(d: &PropertyDescriptor, out: RawMutableHandleObject) { + if d.hasSetter_() { + out.set(d.setter_); + } +} + +/// +pub fn is_accessor_descriptor(d: &PropertyDescriptor) -> bool { + d.hasSetter_() || d.hasGetter_() +} + +/// +pub fn is_data_descriptor(d: &PropertyDescriptor) -> bool { + d.hasWritable_() || d.hasValue_() +} + +/// Evaluate `CrossOriginGetOwnPropertyHelper(proxy, id) != null`. +/// SpiderMonkey-specific. +/// +/// `cx` and `proxy` are expected to be different-Realm here. `proxy` is a proxy +/// for a maybe-cross-origin object. +/// +/// # Safety +/// `bp` must point to a valid, non-null bool. +pub unsafe fn cross_origin_has_own( + cx: SafeJSContext, + _proxy: RawHandleObject, + cross_origin_properties: &'static CrossOriginProperties, + id: RawHandleId, + bp: *mut bool, +) -> bool { + // TODO: Once we have the slot for the holder, it'd be more efficient to + // use `ensure_cross_origin_property_holder`. We'll need `_proxy` to + // do that. + *bp = jsid_to_string(*cx, Handle::from_raw(id)).is_some_and(|key| { + cross_origin_properties.keys().any(|defined_key| { + let defined_key = CStr::from_ptr(defined_key); + defined_key.to_bytes() == key.as_bytes() + }) + }); + + true +} + +/// Implementation of [`CrossOriginGetOwnPropertyHelper`]. +/// +/// `cx` and `proxy` are expected to be different-Realm here. `proxy` is a proxy +/// for a maybe-cross-origin object. +/// +/// [`CrossOriginGetOwnPropertyHelper`]: https://html.spec.whatwg.org/multipage/#crossorigingetownpropertyhelper-(-o,-p-) +pub fn cross_origin_get_own_property_helper( + cx: SafeJSContext, + proxy: RawHandleObject, + cross_origin_properties: &'static CrossOriginProperties, + id: RawHandleId, + desc: RawMutableHandle, + is_none: &mut bool, +) -> bool { + rooted!(in(*cx) let mut holder = ptr::null_mut::()); + + ensure_cross_origin_property_holder( + cx, + proxy, + cross_origin_properties, + holder.handle_mut().into(), + ); + + unsafe { JS_GetOwnPropertyDescriptorById(*cx, holder.handle().into(), id, desc, is_none) } +} + +const ALLOWLISTED_SYMBOL_CODES: &[SymbolCode] = &[ + SymbolCode::toStringTag, + SymbolCode::hasInstance, + SymbolCode::isConcatSpreadable, +]; + +pub fn is_cross_origin_allowlisted_prop(cx: SafeJSContext, id: RawHandleId) -> bool { + unsafe { + if jsid_to_string(*cx, Handle::from_raw(id)).is_some_and(|st| st == "then") { + return true; + } + + rooted!(in(*cx) let mut allowed_id: jsid); + ALLOWLISTED_SYMBOL_CODES.iter().any(|&allowed_code| { + allowed_id.set(SymbolId(GetWellKnownSymbol(*cx, allowed_code))); + // `jsid`s containing `JS::Symbol *` can be compared by + // referential equality + allowed_id.get().asBits_ == id.asBits_ + }) + } +} + +/// Append `« "then", @@toStringTag, @@hasInstance, @@isConcatSpreadable »` to +/// `props`. This is used to implement [`CrossOriginOwnPropertyKeys`]. +/// +/// [`CrossOriginOwnPropertyKeys`]: https://html.spec.whatwg.org/multipage/#crossoriginownpropertykeys-(-o-) +fn append_cross_origin_allowlisted_prop_keys(cx: SafeJSContext, props: RawMutableHandleIdVector) { + unsafe { + rooted!(in(*cx) let mut id: jsid); + + let jsstring = JS_AtomizeAndPinString(*cx, c"then".as_ptr()); + rooted!(in(*cx) let rooted = jsstring); + RUST_INTERNED_STRING_TO_JSID(*cx, rooted.handle().get(), id.handle_mut()); + AppendToIdVector(props, id.handle()); + + for &allowed_code in ALLOWLISTED_SYMBOL_CODES.iter() { + id.set(SymbolId(GetWellKnownSymbol(*cx, allowed_code))); + AppendToIdVector(props, id.handle()); + } + } +} + +/// Get the holder for cross-origin properties for the current global of the +/// `JSContext`, creating one and storing it in a slot of the proxy object if it +/// doesn't exist yet. +/// +/// This essentially creates a cache of [`CrossOriginGetOwnPropertyHelper`]'s +/// results for all property keys. +/// +/// `cx` and `proxy` are expected to be different-Realm here. `proxy` is a proxy +/// for a maybe-cross-origin object. The `out_holder` return value will always +/// be in the Realm of `cx`. +/// +/// [`CrossOriginGetOwnPropertyHelper`]: https://html.spec.whatwg.org/multipage/#crossorigingetownpropertyhelper-(-o,-p-) +fn ensure_cross_origin_property_holder( + cx: SafeJSContext, + _proxy: RawHandleObject, + cross_origin_properties: &'static CrossOriginProperties, + out_holder: RawMutableHandleObject, +) -> bool { + // TODO: We don't have the slot to store the holder yet. For now, + // the holder is constructed every time this function is called, + // which is not only inefficient but also deviates from the + // specification in a subtle yet observable way. + + // Create a holder for the current Realm + unsafe { + out_holder.set(jsapi::JS_NewObjectWithGivenProto( + *cx, + ptr::null_mut(), + RawHandleObject::null(), + )); + + if out_holder.get().is_null() || + !jsapi::JS_DefineProperties( + *cx, + out_holder.handle(), + cross_origin_properties.attributes.as_ptr(), + ) || + !jsapi::JS_DefineFunctions( + *cx, + out_holder.handle(), + cross_origin_properties.methods.as_ptr(), + ) + { + return false; + } + } + + // TODO: Store the holder in the slot that we don't have yet. + + true +} diff --git a/components/script_bindings/root.rs b/components/script_bindings/root.rs index cc3be843593..51bc979908f 100644 --- a/components/script_bindings/root.rs +++ b/components/script_bindings/root.rs @@ -440,3 +440,23 @@ pub unsafe fn trace_roots(tracer: *mut JSTracer) { pub fn assert_in_script() { debug_assert!(thread_state::get().is_script()); } + +/// Get a slice of references to DOM objects. +pub trait DomSlice +where + T: JSTraceable + DomObject, +{ + /// Returns the slice of `T` references. + fn r(&self) -> &[&T]; +} + +impl DomSlice for [Dom] +where + T: JSTraceable + DomObject, +{ + #[inline] + fn r(&self) -> &[&T] { + let _ = mem::transmute::, &T>; + unsafe { &*(self as *const [Dom] as *const [&T]) } + } +} diff --git a/components/script_bindings/utils.rs b/components/script_bindings/utils.rs index 2317f80dbfa..001360e07ff 100644 --- a/components/script_bindings/utils.rs +++ b/components/script_bindings/utils.rs @@ -3,7 +3,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use std::ffi::CString; -use std::os::raw::c_void; +use std::os::raw::{c_char, c_void}; use std::ptr::{self, NonNull}; use js::conversions::ToJSValConvertible; @@ -13,9 +13,9 @@ use js::glue::{ }; use js::jsapi::{ AtomToLinearString, CallArgs, ExceptionStackBehavior, GetLinearStringCharAt, - GetLinearStringLength, GetNonCCWObjectGlobal, HandleObject as RawHandleObject, + GetLinearStringLength, GetNonCCWObjectGlobal, HandleObject as RawHandleObject, Heap, JS_ClearPendingException, JS_IsExceptionPending, JSAtom, JSContext, JSJitInfo, JSObject, - MutableHandleValue as RawMutableHandleValue, ObjectOpResult, StringIsArrayIndex, + JSTracer, MutableHandleValue as RawMutableHandleValue, ObjectOpResult, StringIsArrayIndex, }; use js::jsval::{JSVal, UndefinedValue}; use js::rust::wrappers::{ @@ -36,6 +36,7 @@ use crate::conversions::{PrototypeCheck, jsstring_to_str, private_from_proto_che use crate::error::throw_invalid_this; use crate::script_runtime::{CanGc, JSContext as SafeJSContext}; use crate::str::DOMString; +use crate::trace::trace_object; /// The struct that holds inheritance information for DOM object reflectors. #[derive(Clone, Copy)] @@ -526,3 +527,42 @@ pub unsafe fn exception_to_promise( false } } + +/// Trace the resources held by reserved slots of a global object +/// +/// # Safety +/// `tracer` must point to a valid, non-null JSTracer. +/// `obj` must point to a valid, non-null JSObject. +pub unsafe fn trace_global(tracer: *mut JSTracer, obj: *mut JSObject) { + let array = get_proto_or_iface_array(obj); + for proto in (*array).iter() { + if !proto.is_null() { + trace_object( + tracer, + "prototype", + &*(proto as *const *mut JSObject as *const Heap<*mut JSObject>), + ); + } + } +} + +// Generic method for returning libc::c_void from caller +pub trait AsVoidPtr { + fn as_void_ptr(&self) -> *const libc::c_void; +} +impl AsVoidPtr for T { + fn as_void_ptr(&self) -> *const libc::c_void { + self as *const T as *const libc::c_void + } +} + +// Generic method for returning c_char from caller +pub trait AsCCharPtrPtr { + fn as_c_char_ptr(&self) -> *const c_char; +} + +impl AsCCharPtrPtr for [u8] { + fn as_c_char_ptr(&self) -> *const c_char { + self as *const [u8] as *const c_char + } +}