diff options
44 files changed, 1929 insertions, 1826 deletions
diff --git a/Cargo.lock b/Cargo.lock index 413b869acd5..0a5615b0af6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6391,6 +6391,9 @@ dependencies = [ "bitflags 2.9.0", "crossbeam-channel", "cssparser", + "deny_public_fields", + "dom_struct", + "domobject_derive", "html5ever", "indexmap", "jstraceable_derive", @@ -6400,6 +6403,7 @@ dependencies = [ "mozjs", "num-traits", "parking_lot", + "phf", "phf_codegen", "phf_shared", "regex", @@ -6407,10 +6411,12 @@ dependencies = [ "servo_arc", "servo_config", "servo_malloc_size_of", + "servo_url", "smallvec", "stylo", "stylo_atoms", "tendril", + "tracing", "webxr-api", "xml5ever", ] diff --git a/components/script/Cargo.toml b/components/script/Cargo.toml index 07dacd3735f..7158c15d117 100644 --- a/components/script/Cargo.toml +++ b/components/script/Cargo.toml @@ -18,7 +18,7 @@ crown = ['js/crown'] debugmozjs = ['js/debugmozjs'] jitspew = ['js/jitspew'] profilemozjs = ['js/profilemozjs'] -tracing = ["dep:tracing"] +tracing = ["dep:tracing", "script_bindings/tracing"] webgl_backtrace = ["canvas_traits/webgl_backtrace"] js_backtrace = [] refcell_backtrace = ["accountable-refcell"] diff --git a/components/script/dom/bindings/callback.rs b/components/script/dom/bindings/callback.rs deleted file mode 100644 index 6a54255822f..00000000000 --- a/components/script/dom/bindings/callback.rs +++ /dev/null @@ -1,295 +0,0 @@ -/* 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/. */ - -//! Base classes to work with IDL callbacks. - -use std::default::Default; -use std::ffi::CString; -use std::mem::drop; -use std::rc::Rc; - -use js::jsapi::{ - AddRawValueRoot, EnterRealm, Heap, IsCallable, JSObject, LeaveRealm, Realm, RemoveRawValueRoot, -}; -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}; -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::DomHelpers; -use crate::dom::globalscope::GlobalScopeHelpers; -use crate::realms::{InRealm, enter_realm}; -use crate::script_runtime::{CanGc, JSContext}; - -/// The exception handling used for a call. -#[derive(Clone, Copy, PartialEq)] -pub(crate) enum ExceptionHandling { - /// Report any exception and don't throw it to the caller code. - Report, - /// Throw any exception to the caller code. - Rethrow, -} - -/// A common base class for representing IDL callback function and -/// callback interface types. -#[derive(JSTraceable)] -#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)] -pub(crate) struct CallbackObject<D: DomTypes> { - /// The underlying `JSObject`. - callback: Heap<*mut JSObject>, - permanent_js_root: Heap<JSVal>, - - /// The ["callback context"], that is, the global to use as incumbent - /// global when calling the callback. - /// - /// Looking at the WebIDL standard, it appears as though there would always - /// be a value here, but [sometimes] callback functions are created by - /// hand-waving without defining the value of the callback context, and - /// without any JavaScript code on the stack to grab an incumbent global - /// from. - /// - /// ["callback context"]: https://heycam.github.io/webidl/#dfn-callback-context - /// [sometimes]: https://github.com/whatwg/html/issues/2248 - incumbent: Option<Dom<D::GlobalScope>>, -} - -impl<D: DomTypes> CallbackObject<D> { - #[cfg_attr(crown, allow(crown::unrooted_must_root))] - // These are used by the bindings and do not need `default()` functions. - #[allow(clippy::new_without_default)] - fn new() -> Self { - Self { - callback: Heap::default(), - permanent_js_root: Heap::default(), - incumbent: D::GlobalScope::incumbent().map(|i| Dom::from_ref(&*i)), - } - } - - pub(crate) fn get(&self) -> *mut JSObject { - self.callback.get() - } - - #[allow(unsafe_code)] - unsafe fn init(&mut self, cx: JSContext, callback: *mut JSObject) { - self.callback.set(callback); - self.permanent_js_root.set(ObjectValue(callback)); - assert!(AddRawValueRoot( - *cx, - self.permanent_js_root.get_unsafe(), - b"CallbackObject::root\n".as_c_char_ptr() - )); - } -} - -impl<D: DomTypes> Drop for CallbackObject<D> { - #[allow(unsafe_code)] - fn drop(&mut self) { - unsafe { - if let Some(cx) = Runtime::get() { - RemoveRawValueRoot(cx.as_ptr(), self.permanent_js_root.get_unsafe()); - } - } - } -} - -impl<D: DomTypes> PartialEq for CallbackObject<D> { - fn eq(&self, other: &CallbackObject<D>) -> bool { - self.callback.get() == other.callback.get() - } -} - -/// A trait to be implemented by concrete IDL callback function and -/// callback interface types. -pub(crate) trait CallbackContainer<D: DomTypes> { - /// Create a new CallbackContainer object for the given `JSObject`. - unsafe fn new(cx: JSContext, callback: *mut JSObject) -> Rc<Self>; - /// Returns the underlying `CallbackObject`. - fn callback_holder(&self) -> &CallbackObject<D>; - /// Returns the underlying `JSObject`. - fn callback(&self) -> *mut JSObject { - self.callback_holder().get() - } - /// Returns the ["callback context"], that is, the global to use as - /// incumbent global when calling the callback. - /// - /// ["callback context"]: https://heycam.github.io/webidl/#dfn-callback-context - fn incumbent(&self) -> Option<&D::GlobalScope> { - self.callback_holder().incumbent.as_deref() - } -} - -/// A common base class for representing IDL callback function types. -#[derive(JSTraceable, PartialEq)] -#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)] -pub(crate) struct CallbackFunction<D: DomTypes> { - object: CallbackObject<D>, -} - -impl<D: DomTypes> CallbackFunction<D> { - /// Create a new `CallbackFunction` for this object. - #[cfg_attr(crown, allow(crown::unrooted_must_root))] - // These are used by the bindings and do not need `default()` functions. - #[allow(clippy::new_without_default)] - pub(crate) fn new() -> Self { - Self { - object: CallbackObject::new(), - } - } - - /// Returns the underlying `CallbackObject`. - pub(crate) fn callback_holder(&self) -> &CallbackObject<D> { - &self.object - } - - /// Initialize the callback function with a value. - /// Should be called once this object is done moving. - pub(crate) unsafe fn init(&mut self, cx: JSContext, callback: *mut JSObject) { - self.object.init(cx, callback); - } -} - -/// A common base class for representing IDL callback interface types. -#[derive(JSTraceable, PartialEq)] -#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)] -pub(crate) struct CallbackInterface<D: DomTypes> { - object: CallbackObject<D>, -} - -impl<D: DomTypes> CallbackInterface<D> { - /// Create a new CallbackInterface object for the given `JSObject`. - // These are used by the bindings and do not need `default()` functions. - #[allow(clippy::new_without_default)] - pub(crate) fn new() -> Self { - Self { - object: CallbackObject::new(), - } - } - - /// Returns the underlying `CallbackObject`. - pub(crate) fn callback_holder(&self) -> &CallbackObject<D> { - &self.object - } - - /// Initialize the callback function with a value. - /// Should be called once this object is done moving. - pub(crate) unsafe fn init(&mut self, cx: JSContext, callback: *mut JSObject) { - self.object.init(cx, callback); - } - - /// Returns the property with the given `name`, if it is a callable object, - /// or an error otherwise. - pub(crate) fn get_callable_property(&self, cx: JSContext, name: &str) -> Fallible<JSVal> { - rooted!(in(*cx) let mut callable = UndefinedValue()); - rooted!(in(*cx) let obj = self.callback_holder().get()); - unsafe { - let c_name = CString::new(name).unwrap(); - if !JS_GetProperty(*cx, obj.handle(), c_name.as_ptr(), callable.handle_mut()) { - return Err(Error::JSFailed); - } - - if !callable.is_object() || !IsCallable(callable.to_object()) { - return Err(Error::Type(format!( - "The value of the {} property is not callable", - name - ))); - } - } - Ok(callable.get()) - } -} - -pub(crate) use script_bindings::callback::ThisReflector; - -/// Wraps the reflector for `p` into the realm of `cx`. -pub(crate) fn wrap_call_this_value<T: ThisReflector>( - cx: JSContext, - p: &T, - mut rval: MutableHandleValue, -) -> bool { - rooted!(in(*cx) let mut obj = p.jsobject()); - assert!(!obj.is_null()); - - unsafe { - if !JS_WrapObject(*cx, obj.handle_mut()) { - return false; - } - } - - rval.set(ObjectValue(*obj)); - true -} - -/// A class that performs whatever setup we need to safely make a call while -/// this class is on the stack. After `new` returns, the call is safe to make. -pub(crate) struct CallSetup<D: DomTypes> { - /// The global for reporting exceptions. This is the global object of the - /// (possibly wrapped) callback object. - exception_global: DomRoot<D::GlobalScope>, - /// The `JSContext` used for the call. - cx: JSContext, - /// The realm we were in before the call. - old_realm: *mut Realm, - /// The exception handling used for the call. - handling: ExceptionHandling, - /// <https://heycam.github.io/webidl/#es-invoking-callback-functions> - /// steps 8 and 18.2. - entry_script: Option<GenericAutoEntryScript<D>>, - /// <https://heycam.github.io/webidl/#es-invoking-callback-functions> - /// steps 9 and 18.1. - incumbent_script: Option<GenericAutoIncumbentScript<D>>, -} - -impl<D: DomTypes> CallSetup<D> { - /// Performs the setup needed to make a call. - #[cfg_attr(crown, allow(crown::unrooted_must_root))] - pub(crate) fn new<T: CallbackContainer<D>>(callback: &T, handling: ExceptionHandling) -> Self { - let global = unsafe { D::GlobalScope::from_object(callback.callback()) }; - if let Some(window) = global.downcast::<D::Window>() { - window.Document().ensure_safe_to_run_script_or_layout(); - } - let cx = D::GlobalScope::get_cx(); - - let aes = GenericAutoEntryScript::<D>::new(&global); - let ais = callback.incumbent().map(GenericAutoIncumbentScript::new); - CallSetup { - exception_global: global, - cx, - old_realm: unsafe { EnterRealm(*cx, callback.callback()) }, - handling, - entry_script: Some(aes), - incumbent_script: ais, - } - } - - /// Returns the `JSContext` used for the call. - pub(crate) fn get_context(&self) -> JSContext { - self.cx - } -} - -impl<D: DomTypes> Drop for CallSetup<D> { - fn drop(&mut self) { - unsafe { - LeaveRealm(*self.cx, self.old_realm); - } - if self.handling == ExceptionHandling::Report { - let ar = enter_realm(&*self.exception_global); - <D as DomHelpers<D>>::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/constructor.rs b/components/script/dom/bindings/constructor.rs index ae9305f0767..138dd4520d0 100644 --- a/components/script/dom/bindings/constructor.rs +++ b/components/script/dom/bindings/constructor.rs @@ -11,6 +11,7 @@ use js::glue::{UnwrapObjectDynamic, UnwrapObjectStatic}; use js::jsapi::{CallArgs, CurrentGlobalOrNull, JSAutoRealm, JSObject}; use js::rust::wrappers::{JS_SetPrototype, JS_WrapObject}; use js::rust::{HandleObject, MutableHandleObject, MutableHandleValue}; +use script_bindings::interface::get_desired_proto; use super::utils::ProtoOrIfaceArray; use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; @@ -40,9 +41,8 @@ use crate::dom::bindings::codegen::Bindings::{ }; use crate::dom::bindings::codegen::PrototypeList; use crate::dom::bindings::conversions::DerivedFrom; -use crate::dom::bindings::error::{Error, throw_constructor_without_new, throw_dom_exception}; +use crate::dom::bindings::error::{Error, throw_dom_exception}; use crate::dom::bindings::inheritance::Castable; -use crate::dom::bindings::interface::get_desired_proto; use crate::dom::bindings::reflector::DomObject; use crate::dom::bindings::root::DomRoot; use crate::dom::create::create_native_html_element; @@ -55,7 +55,7 @@ use crate::script_runtime::{CanGc, JSContext, JSContext as SafeJSContext}; use crate::script_thread::ScriptThread; /// <https://html.spec.whatwg.org/multipage/#htmlconstructor> -unsafe fn html_constructor( +fn html_constructor( cx: JSContext, global: &GlobalScope, call_args: &CallArgs, @@ -75,7 +75,9 @@ unsafe fn html_constructor( // The new_target might be a cross-compartment wrapper. Get the underlying object // so we can do the spec's object-identity checks. - rooted!(in(*cx) let new_target_unwrapped = UnwrapObjectDynamic(call_args.new_target().to_object(), *cx, true)); + rooted!(in(*cx) let new_target_unwrapped = unsafe { + UnwrapObjectDynamic(call_args.new_target().to_object(), *cx, true) + }); if new_target_unwrapped.is_null() { throw_dom_exception( cx, @@ -114,7 +116,7 @@ unsafe fn html_constructor( // Step 4. Let isValue be null. let mut is_value = None; - rooted!(in(*cx) let callee = UnwrapObjectStatic(call_args.callee())); + rooted!(in(*cx) let callee = unsafe { UnwrapObjectStatic(call_args.callee()) }); if callee.is_null() { throw_dom_exception(cx, global, Error::Security, can_gc); return Err(()); @@ -123,7 +125,7 @@ unsafe fn html_constructor( { let _ac = JSAutoRealm::new(*cx, callee.get()); rooted!(in(*cx) let mut constructor = ptr::null_mut::<JSObject>()); - rooted!(in(*cx) let global_object = CurrentGlobalOrNull(*cx)); + rooted!(in(*cx) let global_object = unsafe { CurrentGlobalOrNull(*cx) }); // Step 5. If definition's local name is equal to definition's name // (i.e., definition is for an autonomous custom element): @@ -233,13 +235,15 @@ unsafe fn html_constructor( }; rooted!(in(*cx) let mut element = result.reflector().get_jsobject().get()); - if !JS_WrapObject(*cx, element.handle_mut()) { - return Err(()); - } + unsafe { + if !JS_WrapObject(*cx, element.handle_mut()) { + return Err(()); + } - JS_SetPrototype(*cx, element.handle(), prototype.handle()); + JS_SetPrototype(*cx, element.handle(), prototype.handle()); - result.to_jsval(*cx, MutableHandleValue::from_raw(call_args.rval())); + result.to_jsval(*cx, MutableHandleValue::from_raw(call_args.rval())); + } Ok(()) } @@ -395,7 +399,7 @@ pub(crate) fn push_new_element_queue() { ScriptThread::push_new_element_queue(); } -pub(crate) unsafe fn call_html_constructor<T: DerivedFrom<Element> + DomObject>( +pub(crate) fn call_html_constructor<T: DerivedFrom<Element> + DomObject>( cx: JSContext, args: &CallArgs, global: &GlobalScope, @@ -418,26 +422,3 @@ pub(crate) unsafe fn call_html_constructor<T: DerivedFrom<Element> + DomObject>( ) .is_ok() } - -pub(crate) unsafe fn call_default_constructor<D: crate::DomTypes>( - cx: JSContext, - args: &CallArgs, - global: &D::GlobalScope, - proto_id: PrototypeList::ID, - ctor_name: &str, - creator: unsafe fn(JSContext, HandleObject, *mut ProtoOrIfaceArray), - constructor: impl FnOnce(JSContext, &CallArgs, &D::GlobalScope, HandleObject) -> bool, -) -> bool { - if !args.is_constructing() { - throw_constructor_without_new(cx, ctor_name); - return false; - } - - rooted!(in(*cx) let mut desired_proto = ptr::null_mut::<JSObject>()); - let proto_result = get_desired_proto(cx, args, proto_id, creator, desired_proto.handle_mut()); - if proto_result.is_err() { - return false; - } - - constructor(cx, args, global, desired_proto.handle()) -} diff --git a/components/script/dom/bindings/conversions.rs b/components/script/dom/bindings/conversions.rs index 0ea59e6ee83..f01357fda55 100644 --- a/components/script/dom/bindings/conversions.rs +++ b/components/script/dom/bindings/conversions.rs @@ -37,48 +37,15 @@ use std::ffi; pub(crate) use js::conversions::{ ConversionBehavior, ConversionResult, FromJSValConvertible, ToJSValConvertible, }; -use js::glue::GetProxyReservedSlot; -use js::jsapi::{Heap, IsWindowProxy, JS_IsExceptionPending, JSContext, JSObject}; +use js::jsapi::{JS_IsExceptionPending, JSContext, JSObject}; use js::jsval::UndefinedValue; -use js::rust::wrappers::{IsArrayObject, JS_GetProperty, JS_HasProperty}; -use js::rust::{HandleObject, HandleValue, MutableHandleValue}; -pub(crate) use script_bindings::conversions::*; +use js::rust::wrappers::{JS_GetProperty, JS_HasProperty}; +use js::rust::{HandleObject, MutableHandleValue}; +pub(crate) use script_bindings::conversions::{is_dom_proxy, *}; use crate::dom::bindings::error::{Error, Fallible}; use crate::dom::bindings::reflector::DomObject; use crate::dom::bindings::root::DomRoot; -use crate::dom::bindings::trace::{JSTraceable, RootedTraceableBox}; - -impl<T: ToJSValConvertible + JSTraceable> ToJSValConvertible for RootedTraceableBox<T> { - #[inline] - unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { - let value = &**self; - value.to_jsval(cx, rval); - } -} - -impl<T> FromJSValConvertible for RootedTraceableBox<Heap<T>> -where - T: FromJSValConvertible + js::rust::GCMethods + Copy, - Heap<T>: JSTraceable + Default, -{ - type Config = T::Config; - - unsafe fn from_jsval( - cx: *mut JSContext, - value: HandleValue, - config: Self::Config, - ) -> Result<ConversionResult<Self>, ()> { - T::from_jsval(cx, value, config).map(|result| match result { - ConversionResult::Success(inner) => { - ConversionResult::Success(RootedTraceableBox::from_box(Heap::boxed(inner))) - }, - ConversionResult::Failure(msg) => ConversionResult::Failure(msg), - }) - } -} - -pub(crate) use script_bindings::conversions::is_dom_proxy; /// Get a `DomRoot<T>` for the given DOM object, unwrapping any wrapper /// around it first, and checking if the object is of the correct type. @@ -104,43 +71,6 @@ where unsafe { root_from_object(obj.get(), cx) } } -/// Returns whether `value` is an array-like object (Array, FileList, -/// HTMLCollection, HTMLFormControlsCollection, HTMLOptionsCollection, -/// NodeList). -pub(crate) unsafe fn is_array_like<D: crate::DomTypes>( - cx: *mut JSContext, - value: HandleValue, -) -> bool { - let mut is_array = false; - assert!(IsArrayObject(cx, value, &mut is_array)); - if is_array { - return true; - } - - let object: *mut JSObject = match FromJSValConvertible::from_jsval(cx, value, ()).unwrap() { - ConversionResult::Success(object) => object, - _ => return false, - }; - - if root_from_object::<D::FileList>(object, cx).is_ok() { - return true; - } - if root_from_object::<D::HTMLCollection>(object, cx).is_ok() { - return true; - } - if root_from_object::<D::HTMLFormControlsCollection>(object, cx).is_ok() { - return true; - } - if root_from_object::<D::HTMLOptionsCollection>(object, cx).is_ok() { - return true; - } - if root_from_object::<D::NodeList>(object, cx).is_ok() { - return true; - } - - false -} - /// Get a property from a JS object. pub(crate) unsafe fn get_property_jsval( cx: *mut JSContext, @@ -191,22 +121,3 @@ where Err(()) => Err(Error::JSFailed), } } - -/// Get a `DomRoot<T>` for a WindowProxy accessible from a `HandleValue`. -/// Caller is responsible for throwing a JS exception if needed in case of error. -pub(crate) unsafe fn windowproxy_from_handlevalue<D: crate::DomTypes>( - v: HandleValue, - _cx: *mut JSContext, -) -> Result<DomRoot<D::WindowProxy>, ()> { - if !v.get().is_object() { - return Err(()); - } - let object = v.get().to_object(); - if !IsWindowProxy(object) { - return Err(()); - } - let mut value = UndefinedValue(); - GetProxyReservedSlot(object, 0, &mut value); - let ptr = value.to_private() as *const D::WindowProxy; - Ok(DomRoot::from_ref(&*ptr)) -} diff --git a/components/script/dom/bindings/import.rs b/components/script/dom/bindings/import.rs index 71ba5e4c994..5042eba1c05 100644 --- a/components/script/dom/bindings/import.rs +++ b/components/script/dom/bindings/import.rs @@ -2,169 +2,24 @@ * 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/. */ -#[allow(unused_imports)] pub(crate) mod base { pub(crate) use std::ptr; - pub(crate) use std::rc::Rc; - pub(crate) use js::error::throw_type_error; - pub(crate) use js::jsapi::{ - CurrentGlobalOrNull, HandleValue as RawHandleValue, HandleValueArray, Heap, IsCallable, - JS_NewObject, JSContext, JSObject, - }; - pub(crate) use js::jsval::{JSVal, NullValue, ObjectOrNullValue, ObjectValue, UndefinedValue}; - pub(crate) use js::panic::maybe_resume_unwind; - pub(crate) use js::rust::wrappers::{Call, JS_WrapValue}; - pub(crate) use js::rust::{HandleObject, HandleValue, MutableHandleObject, MutableHandleValue}; - pub(crate) use script_bindings::lock::ThreadUnsafeOnceLock; + pub(crate) use js::rust::{HandleObject, MutableHandleObject}; - pub(crate) use crate::dom::bindings::callback::{ - CallSetup, CallbackContainer, CallbackFunction, CallbackInterface, CallbackObject, - ExceptionHandling, ThisReflector, wrap_call_this_value, - }; - pub(crate) use crate::dom::bindings::codegen::Bindings::AudioNodeBinding::{ - ChannelCountMode, ChannelCountModeValues, ChannelInterpretation, - ChannelInterpretationValues, - }; - pub(crate) use crate::dom::bindings::codegen::DomTypes::DomTypes; - pub(crate) use crate::dom::bindings::codegen::{GenericUnionTypes, UnionTypes}; - pub(crate) use crate::dom::bindings::conversions::{ - ConversionBehavior, ConversionResult, FromJSValConvertible, StringificationBehavior, - ToJSValConvertible, root_from_handlevalue, - }; - pub(crate) use crate::dom::bindings::error::Error::JSFailed; - pub(crate) use crate::dom::bindings::error::{Fallible, throw_dom_exception}; - pub(crate) use crate::dom::bindings::num::Finite; - pub(crate) use crate::dom::bindings::proxyhandler::CrossOriginProperties; - pub(crate) use crate::dom::bindings::reflector::{DomGlobalGeneric, DomObject}; - pub(crate) use crate::dom::bindings::root::DomRoot; - pub(crate) use crate::dom::bindings::str::{ByteString, DOMString, USVString}; - pub(crate) use crate::dom::bindings::trace::RootedTraceableBox; - pub(crate) use crate::dom::bindings::utils::{ - DomHelpers, get_dictionary_property, set_dictionary_property, - }; - pub(crate) use crate::dom::globalscope::{GlobalScope, GlobalScopeHelpers}; - pub(crate) use crate::dom::promise::PromiseHelpers; - pub(crate) use crate::realms::{AlreadyInRealm, InRealm}; pub(crate) use crate::script_runtime::{CanGc, JSContext as SafeJSContext}; } -#[allow(unused_imports)] pub(crate) mod module { - pub(crate) use std::cmp; - pub(crate) use std::ffi::CString; - pub(crate) use std::ptr::NonNull; - - pub(crate) use js::glue::{ - CreateProxyHandler, GetProxyReservedSlot, JS_GetReservedSlot, ProxyTraps, - SetProxyReservedSlot, - }; - pub(crate) use js::jsapi::{ - __BindgenBitfieldUnit, CallArgs, GCContext, GetRealmErrorPrototype, - GetRealmFunctionPrototype, GetRealmIteratorPrototype, GetRealmObjectPrototype, - GetWellKnownSymbol, Handle as RawHandle, HandleId as RawHandleId, - HandleObject as RawHandleObject, JS_AtomizeAndPinString, JS_ForwardGetPropertyTo, - JS_GetPropertyDescriptorById, JS_HasPropertyById, JS_NewPlainObject, JS_SetReservedSlot, - JSAutoRealm, JSCLASS_FOREGROUND_FINALIZE, JSCLASS_RESERVED_SLOTS_SHIFT, JSClass, - JSClassOps, JSFunctionSpec, JSITER_HIDDEN, JSITER_OWNONLY, JSITER_SYMBOLS, - JSJitGetterCallArgs, JSJitInfo, JSJitInfo__bindgen_ty_1, JSJitInfo__bindgen_ty_2, - JSJitInfo__bindgen_ty_3, JSJitInfo_AliasSet, JSJitInfo_ArgType, JSJitInfo_OpType, - JSJitMethodCallArgs, JSJitSetterCallArgs, JSNativeWrapper, JSPROP_ENUMERATE, - JSPROP_PERMANENT, JSPROP_READONLY, JSPropertySpec, JSPropertySpec_Accessor, - JSPropertySpec_AccessorsOrValue, JSPropertySpec_AccessorsOrValue_Accessors, - JSPropertySpec_Kind, JSPropertySpec_Name, JSPropertySpec_ValueWrapper, - JSPropertySpec_ValueWrapper__bindgen_ty_1, JSPropertySpec_ValueWrapper_Type, JSTracer, - JSTypedMethodJitInfo, JSValueType, MutableHandle as RawMutableHandle, - MutableHandleIdVector as RawMutableHandleIdVector, - MutableHandleObject as RawMutableHandleObject, MutableHandleValue as RawMutableHandleValue, - ObjectOpResult, PropertyDescriptor, SymbolCode, UndefinedHandleValue, jsid, - }; - pub(crate) use js::jsval::PrivateValue; - pub(crate) use js::panic::wrap_panic; - pub(crate) use js::rust::wrappers::{ - AppendToIdVector, Call, GetPropertyKeys, JS_CopyOwnPropertiesAndPrivateFields, - JS_DefineProperty, JS_DefinePropertyById2, JS_GetProperty, - JS_InitializePropertiesFromCompatibleNativeObject, JS_NewObjectWithGivenProto, - JS_NewObjectWithoutMetadata, JS_SetImmutablePrototype, JS_SetProperty, JS_SetPrototype, - JS_WrapObject, NewProxyObject, RUST_INTERNED_STRING_TO_JSID, RUST_SYMBOL_TO_JSID, - int_to_jsid, - }; - pub(crate) use js::rust::{ - CustomAutoRooterGuard, GCMethods, Handle, MutableHandle, get_context_realm, - get_object_class, get_object_realm, - }; - pub(crate) use js::typedarray::{ - ArrayBuffer, ArrayBufferView, Float32Array, Float64Array, Uint8Array, Uint8ClampedArray, - }; - pub(crate) use js::{ - JS_CALLEE, JSCLASS_GLOBAL_SLOT_COUNT, JSCLASS_IS_DOMJSCLASS, JSCLASS_IS_GLOBAL, - JSCLASS_RESERVED_SLOTS_MASK, jsapi, typedarray, - }; - pub(crate) use script_bindings::codegen::Globals::Globals; - pub(crate) use script_bindings::constant::{ConstantSpec, ConstantVal}; - pub(crate) use script_bindings::finalize::{ - finalize_common, finalize_global, finalize_weak_referenceable, - }; - 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 script_bindings::codegen::PrototypeList; + pub(crate) use script_bindings::conversions::IDLInterface; + pub(crate) use script_bindings::utils::DOMClass; pub(crate) use super::base::*; - pub(crate) use crate::dom::bindings::codegen::Bindings::AnalyserNodeBinding::AnalyserOptions; - pub(crate) use crate::dom::bindings::codegen::Bindings::AudioNodeBinding::{ - AudioNode_Binding, ChannelCountMode, ChannelCountModeValues, ChannelInterpretation, - ChannelInterpretationValues, - }; - pub(crate) use crate::dom::bindings::codegen::Bindings::EventTargetBinding::EventTarget_Binding; - pub(crate) use crate::dom::bindings::codegen::{ - InterfaceObjectMap, PrototypeList, RegisterBindings, - }; - pub(crate) use crate::dom::bindings::constructor::{ - call_default_constructor, call_html_constructor, pop_current_element_queue, - push_new_element_queue, - }; - pub(crate) use crate::dom::bindings::conversions::{ - DOM_OBJECT_SLOT, IDLInterface, StringificationBehavior, ToJSValConvertible, is_array_like, - jsid_to_string, native_from_handlevalue, native_from_object_static, - }; - pub(crate) use crate::dom::bindings::error::{ - Error, ErrorResult, throw_constructor_without_new, - }; - pub(crate) use crate::dom::bindings::guard::{Condition, Guard}; - pub(crate) use crate::dom::bindings::inheritance::Castable; - pub(crate) use crate::dom::bindings::interface::{ - ConstructorClassHook, InterfaceConstructorBehavior, NonCallbackInterfaceObjectClass, - ProtoOrIfaceIndex, create_callback_interface_object, create_global_object, - create_interface_prototype_object, create_named_constructors, - create_noncallback_interface_object, define_dom_interface, define_guarded_methods, - define_guarded_properties, get_desired_proto, get_per_interface_object_handle, - is_exposed_in, - }; - 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, - }; - pub(crate) use crate::dom::bindings::proxyhandler; - pub(crate) use crate::dom::bindings::proxyhandler::{ - ensure_expando_object, get_expando_object, set_property_descriptor, - }; + pub(crate) use crate::dom::bindings::iterable::IterableIterator; pub(crate) use crate::dom::bindings::reflector::{ DomObjectIteratorWrap, DomObjectWrap, Reflector, }; - pub(crate) use crate::dom::bindings::root::{Dom, DomSlice, MaybeUnreflectedDom, Root}; - pub(crate) use crate::dom::bindings::trace::JSTraceable; - pub(crate) use crate::dom::bindings::utils::{ - AsVoidPtr, DOM_PROTO_UNFORGEABLE_HOLDER_SLOT, DOMClass, DOMJSClass, JSCLASS_DOM_GLOBAL, - ProtoOrIfaceArray, enumerate_global, exception_to_promise, generic_getter, - generic_lenient_getter, generic_lenient_setter, generic_method, generic_setter, - generic_static_promise_method, get_array_index_from_id, get_property_on_prototype, - has_property_on_prototype, resolve_global, trace_global, - }; - pub(crate) use crate::dom::bindings::weakref::{DOM_WEAK_SLOT, WeakReferenceable}; - pub(crate) use crate::dom::types::{AnalyserNode, AudioNode, BaseAudioContext, EventTarget}; - pub(crate) use crate::mem::malloc_size_of_including_raw_self; - pub(crate) use crate::realms::{AlreadyInRealm, InRealm}; - pub(crate) use crate::script_runtime::CanGc; + pub(crate) use crate::dom::bindings::root::{Dom, Root}; + pub(crate) use crate::dom::bindings::weakref::WeakReferenceable; } diff --git a/components/script/dom/bindings/iterable.rs b/components/script/dom/bindings/iterable.rs deleted file mode 100644 index c81551f7960..00000000000 --- a/components/script/dom/bindings/iterable.rs +++ /dev/null @@ -1,180 +0,0 @@ -/* 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/. */ - -#![allow(unsafe_code)] - -//! Implementation of `iterable<...>` and `iterable<..., ...>` WebIDL declarations. - -use std::cell::Cell; -use std::marker::PhantomData; -use std::ptr; -use std::ptr::NonNull; - -use dom_struct::dom_struct; -use js::conversions::ToJSValConvertible; -use js::jsapi::{Heap, JSObject}; -use js::jsval::UndefinedValue; -use js::rust::{HandleObject, HandleValue, MutableHandleObject}; -use script_bindings::conversions::IDLInterface; -pub(crate) use script_bindings::iterable::*; -use script_bindings::utils::DOMClass; - -use crate::DomTypes; -use crate::dom::bindings::codegen::Bindings::IterableIteratorBinding::{ - IterableKeyAndValueResult, IterableKeyOrValueResult, -}; -use crate::dom::bindings::error::Fallible; -use crate::dom::bindings::reflector::{ - 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}; - -/// An iterator over the iterable entries of a given DOM interface. -#[dom_struct] -pub(crate) struct IterableIterator< - D: DomTypes, - T: DomObjectIteratorWrap<D> + JSTraceable + Iterable + DomGlobalGeneric<D>, -> { - reflector: Reflector, - iterable: Dom<T>, - type_: IteratorType, - index: Cell<u32>, - _marker: NoTrace<PhantomData<D>>, -} - -impl<D: DomTypes, T: DomObjectIteratorWrap<D> + JSTraceable + Iterable> IterableIterator<D, T> { - pub fn global_(&self, realm: InRealm) -> DomRoot<D::GlobalScope> { - <Self as DomGlobalGeneric<D>>::global_(self, realm) - } -} - -impl< - D: DomTypes, - T: DomObjectIteratorWrap<D> - + JSTraceable - + Iterable - + DomGlobalGeneric<D> - + IDLInterface - + IteratorDerives, -> IDLInterface for IterableIterator<D, T> -{ - fn derives(class: &'static DOMClass) -> bool { - <T as IteratorDerives>::derives(class) - } -} - -impl<D: DomTypes, T: DomObjectIteratorWrap<D> + JSTraceable + Iterable + DomGlobalGeneric<D>> - IterableIterator<D, T> -{ - /// Create a new iterator instance for the provided iterable DOM interface. - pub(crate) fn new(iterable: &T, type_: IteratorType, realm: InRealm) -> DomRoot<Self> { - let iterator = Box::new(IterableIterator { - reflector: Reflector::new(), - type_, - iterable: Dom::from_ref(iterable), - index: Cell::new(0), - _marker: NoTrace(PhantomData), - }); - <D as DomHelpers<D>>::reflect_dom_object(iterator, &*iterable.global_(realm), CanGc::note()) - } - - /// Return the next value from the iterable object. - #[allow(non_snake_case)] - pub(crate) fn Next(&self, cx: JSContext) -> Fallible<NonNull<JSObject>> { - let index = self.index.get(); - rooted!(in(*cx) let mut value = UndefinedValue()); - rooted!(in(*cx) let mut rval = ptr::null_mut::<JSObject>()); - let result = if index >= self.iterable.get_iterable_length() { - dict_return(cx, rval.handle_mut(), true, value.handle()) - } else { - match self.type_ { - IteratorType::Keys => { - unsafe { - self.iterable - .get_key_at_index(index) - .to_jsval(*cx, value.handle_mut()); - } - dict_return(cx, rval.handle_mut(), false, value.handle()) - }, - IteratorType::Values => { - unsafe { - self.iterable - .get_value_at_index(index) - .to_jsval(*cx, value.handle_mut()); - } - dict_return(cx, rval.handle_mut(), false, value.handle()) - }, - IteratorType::Entries => { - rooted!(in(*cx) let mut key = UndefinedValue()); - unsafe { - self.iterable - .get_key_at_index(index) - .to_jsval(*cx, key.handle_mut()); - self.iterable - .get_value_at_index(index) - .to_jsval(*cx, value.handle_mut()); - } - key_and_value_return(cx, rval.handle_mut(), key.handle(), value.handle()) - }, - } - }; - self.index.set(index + 1); - result.map(|_| NonNull::new(rval.get()).expect("got a null pointer")) - } -} - -impl<D: DomTypes, T: DomObjectIteratorWrap<D> + JSTraceable + Iterable + DomGlobalGeneric<D>> - DomObjectWrap<D> for IterableIterator<D, T> -{ - const WRAP: unsafe fn( - JSContext, - &D::GlobalScope, - Option<HandleObject>, - Box<Self>, - CanGc, - ) -> Root<Dom<Self>> = T::ITER_WRAP; -} - -fn dict_return( - cx: JSContext, - mut result: MutableHandleObject, - done: bool, - value: HandleValue, -) -> Fallible<()> { - let mut dict = IterableKeyOrValueResult::empty(); - dict.done = done; - dict.value.set(value.get()); - rooted!(in(*cx) let mut dict_value = UndefinedValue()); - unsafe { - dict.to_jsval(*cx, dict_value.handle_mut()); - } - result.set(dict_value.to_object()); - Ok(()) -} - -fn key_and_value_return( - cx: JSContext, - mut result: MutableHandleObject, - key: HandleValue, - value: HandleValue, -) -> Fallible<()> { - let mut dict = IterableKeyAndValueResult::empty(); - dict.done = false; - dict.value = Some( - vec![key, value] - .into_iter() - .map(|handle| RootedTraceableBox::from_box(Heap::boxed(handle.get()))) - .collect(), - ); - rooted!(in(*cx) let mut dict_value = UndefinedValue()); - unsafe { - dict.to_jsval(*cx, dict_value.handle_mut()); - } - result.set(dict_value.to_object()); - Ok(()) -} diff --git a/components/script/dom/bindings/mod.rs b/components/script/dom/bindings/mod.rs index b4234e0cc39..ae9e26e8a54 100644 --- a/components/script/dom/bindings/mod.rs +++ b/components/script/dom/bindings/mod.rs @@ -135,7 +135,6 @@ #![deny(non_snake_case)] pub(crate) mod buffer_source; -pub(crate) mod callback; #[allow(dead_code)] pub(crate) mod cell; pub(crate) mod constructor; @@ -143,13 +142,9 @@ pub(crate) mod conversions; pub(crate) mod error; pub(crate) mod frozenarray; pub(crate) mod function; -pub(crate) mod guard; pub(crate) mod import; pub(crate) mod inheritance; -pub(crate) mod interface; -pub(crate) mod iterable; pub(crate) mod like; -pub(crate) mod namespace; pub(crate) mod principals; pub(crate) mod proxyhandler; pub(crate) mod refcounted; @@ -165,7 +160,7 @@ pub(crate) mod utils; pub(crate) mod weakref; pub(crate) mod xmlname; -pub(crate) use script_bindings::num; +pub(crate) use script_bindings::{callback, iterable, num}; /// Generated JS-Rust bindings. #[allow(missing_docs, non_snake_case)] @@ -173,13 +168,7 @@ pub(crate) mod codegen { pub(crate) mod DomTypeHolder { include!(concat!(env!("BINDINGS_OUT_DIR"), "/DomTypeHolder.rs")); } - pub(crate) mod DomTypes { - include!(concat!(env!("BINDINGS_OUT_DIR"), "/DomTypes.rs")); - } - #[allow(dead_code)] - pub(crate) mod GenericBindings { - include!(concat!(env!("BINDINGS_OUT_DIR"), "/Bindings/mod.rs")); - } + pub(crate) use script_bindings::codegen::GenericBindings; #[allow(dead_code)] pub(crate) mod Bindings { include!(concat!( @@ -189,30 +178,14 @@ pub(crate) mod codegen { } pub(crate) mod InterfaceObjectMap { include!(concat!(env!("BINDINGS_OUT_DIR"), "/InterfaceObjectMap.rs")); - pub(crate) use script_bindings::codegen::Globals::Globals; } - #[allow(dead_code)] pub(crate) mod ConcreteInheritTypes { include!(concat!( env!("BINDINGS_OUT_DIR"), "/ConcreteInheritTypes.rs" )); } - pub(crate) use script_bindings::codegen::PrototypeList; - pub(crate) mod RegisterBindings { - include!(concat!(env!("BINDINGS_OUT_DIR"), "/RegisterBindings.rs")); - } - #[allow( - non_camel_case_types, - unused_imports, - unused_variables, - clippy::large_enum_variant, - clippy::upper_case_acronyms, - clippy::enum_variant_names - )] - pub(crate) mod GenericUnionTypes { - include!(concat!(env!("BINDINGS_OUT_DIR"), "/GenericUnionTypes.rs")); - } + pub(crate) use script_bindings::codegen::{PrototypeList, RegisterBindings}; #[allow(dead_code)] pub(crate) mod UnionTypes { include!(concat!(env!("BINDINGS_OUT_DIR"), "/UnionTypes.rs")); diff --git a/components/script/dom/bindings/principals.rs b/components/script/dom/bindings/principals.rs index e51a98e454a..0594b77022c 100644 --- a/components/script/dom/bindings/principals.rs +++ b/components/script/dom/bindings/principals.rs @@ -2,132 +2,17 @@ * 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::marker::PhantomData; -use std::mem::ManuallyDrop; -use std::ops::Deref; use std::ptr::NonNull; -use js::glue::{ - CreateRustJSPrincipals, DestroyRustJSPrincipals, GetRustJSPrincipalsPrivate, - JSPrincipalsCallbacks, -}; +use js::glue::{DestroyRustJSPrincipals, GetRustJSPrincipalsPrivate, JSPrincipalsCallbacks}; use js::jsapi::{ - JS_DropPrincipals, JS_HoldPrincipals, JS_ReadUint32Pair, JSContext, JSPrincipals, - JSStructuredCloneReader, JSStructuredCloneWriter, + JS_ReadUint32Pair, JSContext, JSPrincipals, JSStructuredCloneReader, JSStructuredCloneWriter, }; -use js::rust::Runtime; +use script_bindings::principals::{ServoJSPrincipals, ServoJSPrincipalsRef}; 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<JSPrincipals>); - -impl ServoJSPrincipals { - pub(crate) fn new<D: DomTypes>(origin: &MutableOrigin) -> Self { - unsafe { - let private: Box<MutableOrigin> = Box::new(origin.clone()); - let raw = CreateRustJSPrincipals( - <D as DomHelpers<D>>::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)) - } - } - - /// Construct `Self` from a raw `*mut JSPrincipals`, incrementing its - /// reference count. - #[inline] - pub(crate) unsafe fn from_raw_nonnull(raw: NonNull<JSPrincipals>) -> Self { - JS_HoldPrincipals(raw.as_ptr()); - Self(raw) - } - - #[inline] - pub(crate) unsafe fn origin(&self) -> MutableOrigin { - let origin = GetRustJSPrincipalsPrivate(self.0.as_ptr()) as *mut MutableOrigin; - (*origin).clone() - } - - #[inline] - pub(crate) fn as_raw_nonnull(&self) -> NonNull<JSPrincipals> { - self.0 - } - - #[inline] - pub(crate) fn as_raw(&self) -> *mut JSPrincipals { - self.0.as_ptr() - } -} - -impl Clone for ServoJSPrincipals { - #[inline] - fn clone(&self) -> Self { - unsafe { Self::from_raw_nonnull(self.as_raw_nonnull()) } - } -} - -impl Drop for ServoJSPrincipals { - #[inline] - fn drop(&mut self) { - if let Some(cx) = Runtime::get() { - unsafe { JS_DropPrincipals(cx.as_ptr(), self.as_raw()) }; - } - } -} - -/// A borrowed reference to Servo's `JSPrincipals` instance. Does not update the -/// reference count on creation and deletion. -pub(crate) struct ServoJSPrincipalsRef<'a>(ManuallyDrop<ServoJSPrincipals>, PhantomData<&'a ()>); - -impl ServoJSPrincipalsRef<'_> { - /// Construct `Self` from a raw `NonNull<JSPrincipals>`. - /// - /// # Safety - /// - /// `ServoJSPrincipalsRef` does not update the reference count of the - /// wrapped `JSPrincipals` object. It's up to the caller to ensure the - /// returned `ServoJSPrincipalsRef` object or any clones are not used past - /// the lifetime of the wrapped object. - #[inline] - pub(crate) unsafe fn from_raw_nonnull(raw: NonNull<JSPrincipals>) -> Self { - // Don't use `ServoJSPrincipals::from_raw_nonnull`; we don't want to - // update the reference count - Self(ManuallyDrop::new(ServoJSPrincipals(raw)), PhantomData) - } - - /// Construct `Self` from a raw `*mut JSPrincipals`. - /// - /// # Safety - /// - /// The behavior is undefined if `raw` is null. See also - /// [`Self::from_raw_nonnull`]. - #[inline] - pub(crate) unsafe fn from_raw_unchecked(raw: *mut JSPrincipals) -> Self { - Self::from_raw_nonnull(NonNull::new_unchecked(raw)) - } -} - -impl Clone for ServoJSPrincipalsRef<'_> { - #[inline] - fn clone(&self) -> Self { - Self(ManuallyDrop::new(ServoJSPrincipals(self.0.0)), PhantomData) - } -} - -impl Deref for ServoJSPrincipalsRef<'_> { - type Target = ServoJSPrincipals; - - #[inline] - fn deref(&self) -> &Self::Target { - &self.0 - } -} +use crate::DomTypeHolder; #[allow(unused)] pub(crate) unsafe extern "C" fn destroy_servo_jsprincipal(principals: *mut JSPrincipals) { diff --git a/components/script/dom/bindings/proxyhandler.rs b/components/script/dom/bindings/proxyhandler.rs index 5dfe0839502..2e203a81fa9 100644 --- a/components/script/dom/bindings/proxyhandler.rs +++ b/components/script/dom/bindings/proxyhandler.rs @@ -6,30 +6,12 @@ #![deny(missing_docs)] -use std::ptr; - -use js::conversions::ToJSValConvertible; -use js::glue::{GetProxyHandler, InvokeGetOwnPropertyDescriptor}; -use js::jsapi; -use js::jsapi::{ - 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, -}; -use js::jsval::UndefinedValue; -use js::rust::{HandleObject, HandleValue, MutableHandle, MutableHandleObject, get_context_realm}; +use js::jsapi::{GetObjectRealmOrNull, GetRealmPrincipals, HandleObject as RawHandleObject}; +use js::rust::get_context_realm; +use script_bindings::principals::ServoJSPrincipalsRef; pub(crate) use script_bindings::proxyhandler::*; -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::utils::DomHelpers; -use crate::dom::globalscope::GlobalScopeHelpers; -use crate::realms::{AlreadyInRealm, InRealm}; -use crate::script_runtime::{CanGc, JSContext as SafeJSContext}; +use crate::script_runtime::JSContext as SafeJSContext; /// <https://html.spec.whatwg.org/multipage/#isplatformobjectsameorigin-(-o-)> pub(crate) unsafe fn is_platform_object_same_origin( @@ -64,269 +46,3 @@ pub(crate) unsafe fn is_platform_object_same_origin( result } - -/// Report a cross-origin denial for a property, Always returns `false`, so it -/// can be used as `return report_cross_origin_denial(...);`. -/// -/// What this function does corresponds to the operations in -/// <https://html.spec.whatwg.org/multipage/#the-location-interface> denoted as -/// "Throw a `SecurityError` DOMException". -pub(crate) unsafe fn report_cross_origin_denial<D: DomTypes>( - cx: SafeJSContext, - id: RawHandleId, - access: &str, -) -> bool { - debug!( - "permission denied to {} property {} on cross-origin object", - access, - id_to_source(cx, id).as_deref().unwrap_or("< error >"), - ); - let in_realm_proof = AlreadyInRealm::assert_for_cx(cx); - if !JS_IsExceptionPending(*cx) { - let global = D::GlobalScope::from_context(*cx, InRealm::Already(&in_realm_proof)); - // TODO: include `id` and `access` in the exception message - <D as DomHelpers<D>>::throw_dom_exception(cx, &global, Error::Security, CanGc::note()); - } - false -} - -/// 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<D: DomTypes>( - cx: *mut JSContext, - proxy: RawHandleObject, - id: RawHandleId, - v: RawHandleValue, - receiver: RawHandleValue, - result: *mut ObjectOpResult, -) -> bool { - let cx = SafeJSContext::from_ptr(cx); - - if !<D as DomHelpers<D>>::is_platform_object_same_origin(cx, proxy) { - return cross_origin_set::<D>(cx, proxy, id, v, receiver, result); - } - - // Safe to enter the Realm of proxy now. - let _ac = JSAutoRealm::new(*cx, proxy.get()); - - // OrdinarySet - // <https://tc39.es/ecma262/#sec-ordinaryset> - rooted!(in(*cx) let mut own_desc = PropertyDescriptor::default()); - let mut is_none = false; - if !InvokeGetOwnPropertyDescriptor( - GetProxyHandler(*proxy), - *cx, - proxy, - id, - own_desc.handle_mut().into(), - &mut is_none, - ) { - return false; - } - - js::jsapi::SetPropertyIgnoringNamedGetter( - *cx, - proxy, - id, - v, - receiver, - own_desc.handle().into(), - result, - ) -} - -/// Implementation of `[[GetPrototypeOf]]` for [`Location`]. -/// -/// [`Location`]: https://html.spec.whatwg.org/multipage/#location-getprototypeof -pub(crate) unsafe fn maybe_cross_origin_get_prototype<D: DomTypes>( - cx: SafeJSContext, - proxy: RawHandleObject, - get_proto_object: unsafe fn(cx: SafeJSContext, global: HandleObject, rval: MutableHandleObject), - proto: RawMutableHandleObject, -) -> bool { - // > 1. If ! IsPlatformObjectSameOrigin(this) is true, then return ! OrdinaryGetPrototypeOf(this). - if is_platform_object_same_origin(cx, proxy) { - let ac = JSAutoRealm::new(*cx, proxy.get()); - let global = D::GlobalScope::from_context(*cx, InRealm::Entered(&ac)); - get_proto_object( - cx, - global.reflector().get_jsobject(), - MutableHandleObject::from_raw(proto), - ); - return !proto.is_null(); - } - - // > 2. Return null. - proto.set(ptr::null_mut()); - 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<D: DomTypes>( - cx: SafeJSContext, - proxy: RawHandleObject, - receiver: RawHandleValue, - id: RawHandleId, - vp: RawMutableHandleValue, -) -> bool { - // > 1. Let `desc` be `? O.[[GetOwnProperty]](P)`. - rooted!(in(*cx) let mut descriptor = PropertyDescriptor::default()); - let mut is_none = false; - if !InvokeGetOwnPropertyDescriptor( - GetProxyHandler(*proxy), - *cx, - proxy, - id, - descriptor.handle_mut().into(), - &mut is_none, - ) { - return false; - } - - // > 2. Assert: `desc` is not undefined. - assert!( - !is_none, - "Callees should throw in all cases when they are not finding \ - a property decriptor" - ); - - // > 3. If `! IsDataDescriptor(desc)` is true, then return `desc.[[Value]]`. - if is_data_descriptor(&descriptor) { - vp.set(descriptor.value_); - return true; - } - - // > 4. Assert: `IsAccessorDescriptor(desc)` is `true`. - assert!(is_accessor_descriptor(&descriptor)); - - // > 5. Let `getter` be `desc.[[Get]]`. - // > - // > 6. If `getter` is `undefined`, then throw a `SecurityError` - // > `DOMException`. - rooted!(in(*cx) let mut getter = ptr::null_mut::<JSObject>()); - get_getter_object(&descriptor, getter.handle_mut().into()); - if getter.get().is_null() { - return report_cross_origin_denial::<D>(cx, id, "get"); - } - - rooted!(in(*cx) let mut getter_jsval = UndefinedValue()); - getter.get().to_jsval(*cx, getter_jsval.handle_mut()); - - // > 7. Return `? Call(getter, Receiver)`. - jsapi::Call( - *cx, - receiver, - getter_jsval.handle().into(), - &jsapi::HandleValueArray::empty(), - vp, - ) -} - -/// Implementation of [`CrossOriginSet`]. -/// -/// `cx` and `proxy` are expected to be different-Realm here. `proxy` is a proxy -/// 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<D: DomTypes>( - cx: SafeJSContext, - proxy: RawHandleObject, - id: RawHandleId, - v: RawHandleValue, - receiver: RawHandleValue, - result: *mut ObjectOpResult, -) -> bool { - // > 1. Let desc be ? O.[[GetOwnProperty]](P). - rooted!(in(*cx) let mut descriptor = PropertyDescriptor::default()); - let mut is_none = false; - if !InvokeGetOwnPropertyDescriptor( - GetProxyHandler(*proxy), - *cx, - proxy, - id, - descriptor.handle_mut().into(), - &mut is_none, - ) { - return false; - } - - // > 2. Assert: desc is not undefined. - assert!( - !is_none, - "Callees should throw in all cases when they are not finding \ - a property decriptor" - ); - - // > 3. If desc.[[Set]] is present and its value is not undefined, - // > then: [...] - rooted!(in(*cx) let mut setter = ptr::null_mut::<JSObject>()); - get_setter_object(&descriptor, setter.handle_mut().into()); - if setter.get().is_null() { - // > 4. Throw a "SecurityError" DOMException. - return report_cross_origin_denial::<D>(cx, id, "set"); - } - - rooted!(in(*cx) let mut setter_jsval = UndefinedValue()); - setter.get().to_jsval(*cx, setter_jsval.handle_mut()); - - // > 3.1. Perform ? Call(setter, Receiver, «V»). - // > - // > 3.2. Return true. - rooted!(in(*cx) let mut ignored = UndefinedValue()); - if !jsapi::Call( - *cx, - receiver, - setter_jsval.handle().into(), - // FIXME: Our binding lacks `HandleValueArray(Handle<Value>)` - // <https://searchfox.org/mozilla-central/rev/072710086ddfe25aa2962c8399fefb2304e8193b/js/public/ValueArray.h#54-55> - &jsapi::HandleValueArray { - length_: 1, - elements_: v.ptr, - }, - ignored.handle_mut().into(), - ) { - return false; - } - - (*result).code_ = 0 /* OkCode */; - true -} - -/// 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<D: DomTypes>( - cx: SafeJSContext, - _proxy: RawHandleObject, - id: RawHandleId, - desc: RawMutableHandle<PropertyDescriptor>, - is_none: &mut bool, -) -> bool { - assert!(*is_none, "why are we being called?"); - - // > 1. If P is `then`, `@@toStringTag`, `@@hasInstance`, or - // > `@@isConcatSpreadable`, then return `PropertyDescriptor{ [[Value]]: - // > undefined, [[Writable]]: false, [[Enumerable]]: false, - // > [[Configurable]]: true }`. - if is_cross_origin_allowlisted_prop(cx, id) { - set_property_descriptor( - MutableHandle::from_raw(desc), - HandleValue::undefined(), - jsapi::JSPROP_READONLY as u32, - is_none, - ); - return true; - } - - // > 2. Throw a `SecurityError` `DOMException`. - report_cross_origin_denial::<D>(cx, id, "access") -} diff --git a/components/script/dom/bindings/reflector.rs b/components/script/dom/bindings/reflector.rs index 3593578a66f..0a5afbce487 100644 --- a/components/script/dom/bindings/reflector.rs +++ b/components/script/dom/bindings/reflector.rs @@ -5,15 +5,14 @@ //! The `Reflector` struct. use js::rust::HandleObject; +use script_bindings::interfaces::GlobalScopeHelpers; use crate::DomTypes; use crate::dom::bindings::conversions::DerivedFrom; -use crate::dom::bindings::iterable::{Iterable, IterableIterator}; -use crate::dom::bindings::root::{Dom, DomRoot, Root}; -use crate::dom::bindings::trace::JSTraceable; -use crate::dom::globalscope::{GlobalScope, GlobalScopeHelpers}; -use crate::realms::{AlreadyInRealm, InRealm}; -use crate::script_runtime::{CanGc, JSContext}; +use crate::dom::bindings::root::DomRoot; +use crate::dom::globalscope::GlobalScope; +use crate::realms::InRealm; +use crate::script_runtime::CanGc; /// Create the reflector for a new DOM object and yield ownership to the /// reflector. @@ -42,31 +41,6 @@ where unsafe { T::WRAP(D::GlobalScope::get_cx(), global_scope, proto, obj, can_gc) } } -pub(crate) trait DomGlobalGeneric<D: DomTypes>: DomObject { - /// Returns the [`GlobalScope`] of the realm that the [`DomObject`] was created in. If this - /// object is a `Node`, this will be different from it's owning `Document` if adopted by. For - /// `Node`s it's almost always better to use `NodeTraits::owning_global`. - fn global_(&self, realm: InRealm) -> DomRoot<D::GlobalScope> - where - Self: Sized, - { - D::GlobalScope::from_reflector(self, realm) - } - - /// Returns the [`GlobalScope`] of the realm that the [`DomObject`] was created in. If this - /// object is a `Node`, this will be different from it's owning `Document` if adopted by. For - /// `Node`s it's almost always better to use `NodeTraits::owning_global`. - fn global(&self) -> DomRoot<D::GlobalScope> - where - Self: Sized, - { - let realm = AlreadyInRealm::assert_for_cx(D::GlobalScope::get_cx()); - D::GlobalScope::from_reflector(self, InRealm::already(&realm)) - } -} - -impl<D: DomTypes, T: DomObject> DomGlobalGeneric<D> for T {} - pub(crate) trait DomGlobal { fn global_(&self, realm: InRealm) -> DomRoot<GlobalScope>; fn global(&self) -> DomRoot<GlobalScope>; @@ -81,35 +55,4 @@ impl<T: DomGlobalGeneric<crate::DomTypeHolder>> DomGlobal for T { } } -pub(crate) use script_bindings::reflector::{DomObject, MutDomObject, Reflector}; - -/// A trait to provide a function pointer to wrap function for DOM objects. -pub(crate) trait DomObjectWrap<D: DomTypes>: - Sized + DomObject + DomGlobalGeneric<D> -{ - /// Function pointer to the general wrap function type - #[allow(clippy::type_complexity)] - const WRAP: unsafe fn( - JSContext, - &D::GlobalScope, - Option<HandleObject>, - Box<Self>, - CanGc, - ) -> Root<Dom<Self>>; -} - -/// A trait to provide a function pointer to wrap function for -/// DOM iterator interfaces. -pub(crate) trait DomObjectIteratorWrap<D: DomTypes>: - DomObjectWrap<D> + JSTraceable + Iterable -{ - /// Function pointer to the wrap function for `IterableIterator<T>` - #[allow(clippy::type_complexity)] - const ITER_WRAP: unsafe fn( - JSContext, - &D::GlobalScope, - Option<HandleObject>, - Box<IterableIterator<D, Self>>, - CanGc, - ) -> Root<Dom<IterableIterator<D, Self>>>; -} +pub(crate) use script_bindings::reflector::*; diff --git a/components/script/dom/bindings/settings_stack.rs b/components/script/dom/bindings/settings_stack.rs index 1284931c349..8e00d03a953 100644 --- a/components/script/dom/bindings/settings_stack.rs +++ b/components/script/dom/bindings/settings_stack.rs @@ -3,36 +3,20 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use std::cell::RefCell; -use std::marker::PhantomData; -use std::thread; -use js::jsapi::{GetScriptedCallerGlobal, HideScriptedCaller, JSTracer, UnhideScriptedCaller}; +use js::jsapi::{GetScriptedCallerGlobal, JSTracer}; use js::rust::Runtime; +use script_bindings::settings_stack::*; -use crate::DomTypes; -use crate::dom::bindings::root::{Dom, DomRoot}; +//use script_bindings::interfaces::{DomHelpers, GlobalScopeHelpers}; +use crate::dom::bindings::root::DomRoot; use crate::dom::bindings::trace::JSTraceable; -use crate::dom::bindings::utils::DomHelpers; -use crate::dom::globalscope::{GlobalScope, GlobalScopeHelpers}; -use crate::script_runtime::CanGc; +use crate::dom::globalscope::GlobalScope; thread_local!(pub(super) static STACK: RefCell<Vec<StackEntry<crate::DomTypeHolder>>> = const { RefCell::new(Vec::new()) }); -#[derive(Debug, Eq, JSTraceable, PartialEq)] -enum StackEntryKind { - Incumbent, - Entry, -} - -#[cfg_attr(crown, allow(crown::unrooted_must_root))] -#[derive(JSTraceable)] -pub(crate) struct StackEntry<D: DomTypes> { - global: Dom<D::GlobalScope>, - kind: StackEntryKind, -} - /// Traces the script settings stack. pub(crate) unsafe fn trace(tracer: *mut JSTracer) { STACK.with(|stack| { @@ -46,61 +30,6 @@ pub(crate) fn is_execution_stack_empty() -> bool { pub(crate) type AutoEntryScript = GenericAutoEntryScript<crate::DomTypeHolder>; -/// RAII struct that pushes and pops entries from the script settings stack. -pub(crate) struct GenericAutoEntryScript<D: DomTypes> { - global: DomRoot<D::GlobalScope>, - #[cfg(feature = "tracing")] - #[allow(dead_code)] - span: tracing::span::EnteredSpan, -} - -impl<D: DomTypes> GenericAutoEntryScript<D> { - /// <https://html.spec.whatwg.org/multipage/#prepare-to-run-script> - pub(crate) fn new(global: &D::GlobalScope) -> Self { - let settings_stack = <D as DomHelpers<D>>::settings_stack(); - settings_stack.with(|stack| { - trace!("Prepare to run script with {:p}", global); - let mut stack = stack.borrow_mut(); - stack.push(StackEntry { - global: Dom::from_ref(global), - kind: StackEntryKind::Entry, - }); - Self { - global: DomRoot::from_ref(global), - #[cfg(feature = "tracing")] - span: tracing::info_span!( - "ScriptEvaluate", - servo_profiling = true, - url = global.get_url().to_string(), - ) - .entered(), - } - }) - } -} - -impl<D: DomTypes> Drop for GenericAutoEntryScript<D> { - /// <https://html.spec.whatwg.org/multipage/#clean-up-after-running-script> - fn drop(&mut self) { - let settings_stack = <D as DomHelpers<D>>::settings_stack(); - settings_stack.with(|stack| { - let mut stack = stack.borrow_mut(); - let entry = stack.pop().unwrap(); - assert_eq!( - &*entry.global as *const D::GlobalScope, &*self.global as *const D::GlobalScope, - "Dropped AutoEntryScript out of order." - ); - assert_eq!(entry.kind, StackEntryKind::Entry); - trace!("Clean up after running script with {:p}", &*entry.global); - }); - - // Step 5 - if !thread::panicking() && incumbent_global().is_none() { - self.global.perform_a_microtask_checkpoint(CanGc::note()); - } - } -} - /// Returns the ["entry"] global object. /// /// ["entry"]: https://html.spec.whatwg.org/multipage/#entry @@ -117,67 +46,7 @@ pub(crate) fn entry_global() -> DomRoot<GlobalScope> { .unwrap() } -/// RAII struct that pushes and pops entries from the script settings stack. -pub(crate) struct GenericAutoIncumbentScript<D: DomTypes> { - global: usize, - _marker: PhantomData<D>, -} - -pub(crate) type AutoIncumbentScript = GenericAutoIncumbentScript<crate::DomTypeHolder>; - -impl<D: DomTypes> GenericAutoIncumbentScript<D> { - /// <https://html.spec.whatwg.org/multipage/#prepare-to-run-a-callback> - pub(crate) fn new(global: &D::GlobalScope) -> Self { - // Step 2-3. - unsafe { - let cx = - Runtime::get().expect("Creating a new incumbent script after runtime shutdown"); - HideScriptedCaller(cx.as_ptr()); - } - let settings_stack = <D as DomHelpers<D>>::settings_stack(); - settings_stack.with(|stack| { - trace!("Prepare to run a callback with {:p}", global); - // Step 1. - let mut stack = stack.borrow_mut(); - stack.push(StackEntry { - global: Dom::from_ref(global), - kind: StackEntryKind::Incumbent, - }); - Self { - global: global as *const _ as usize, - _marker: PhantomData, - } - }) - } -} - -impl<D: DomTypes> Drop for GenericAutoIncumbentScript<D> { - /// <https://html.spec.whatwg.org/multipage/#clean-up-after-running-a-callback> - fn drop(&mut self) { - let settings_stack = <D as DomHelpers<D>>::settings_stack(); - settings_stack.with(|stack| { - // Step 4. - let mut stack = stack.borrow_mut(); - let entry = stack.pop().unwrap(); - // Step 3. - assert_eq!( - &*entry.global as *const D::GlobalScope as usize, self.global, - "Dropped AutoIncumbentScript out of order." - ); - assert_eq!(entry.kind, StackEntryKind::Incumbent); - trace!( - "Clean up after running a callback with {:p}", - &*entry.global - ); - }); - unsafe { - // Step 1-2. - if let Some(cx) = Runtime::get() { - UnhideScriptedCaller(cx.as_ptr()); - } - } - } -} +pub type AutoIncumbentScript = GenericAutoIncumbentScript<crate::DomTypeHolder>; /// Returns the ["incumbent"] global object. /// diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index b0c65b9fd9d..5d48e4c4e0f 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -33,15 +33,12 @@ use std::collections::HashMap; use std::collections::hash_map::RandomState; use std::fmt::Display; use std::hash::{BuildHasher, Hash}; -use std::mem; -use std::ops::{Deref, DerefMut}; /// A trait to allow tracing (only) DOM objects. pub(crate) use js::gc::Traceable as JSTraceable; use js::glue::{CallScriptTracer, CallStringTracer, CallValueTracer}; use js::jsapi::{GCTraceKindToAscii, Heap, JSScript, JSString, JSTracer, TraceKind}; use js::jsval::JSVal; -use js::rust::{GCMethods, Handle}; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; pub(crate) use script_bindings::trace::*; @@ -267,70 +264,3 @@ unsafe impl<T: DomObject> JSTraceable for Trusted<T> { // Do nothing } } - -/// Roots any JSTraceable thing -/// -/// If you have a valid DomObject, use DomRoot. -/// If you have GC things like *mut JSObject or JSVal, use rooted!. -/// If you have an arbitrary number of DomObjects to root, use rooted_vec!. -/// If you know what you're doing, use this. -#[cfg_attr(crown, crown::unrooted_must_root_lint::allow_unrooted_interior)] -pub(crate) struct RootedTraceableBox<T: JSTraceable + 'static>(js::gc::RootedTraceableBox<T>); - -unsafe impl<T: JSTraceable + 'static> JSTraceable for RootedTraceableBox<T> { - unsafe fn trace(&self, tracer: *mut JSTracer) { - self.0.trace(tracer); - } -} - -impl<T: JSTraceable + 'static> RootedTraceableBox<T> { - /// DomRoot a JSTraceable thing for the life of this RootedTraceableBox - pub(crate) fn new(traceable: T) -> RootedTraceableBox<T> { - Self(js::gc::RootedTraceableBox::new(traceable)) - } - - /// Consumes a boxed JSTraceable and roots it for the life of this RootedTraceableBox. - pub(crate) fn from_box(boxed_traceable: Box<T>) -> RootedTraceableBox<T> { - Self(js::gc::RootedTraceableBox::from_box(boxed_traceable)) - } -} - -impl<T> RootedTraceableBox<Heap<T>> -where - Heap<T>: JSTraceable + 'static, - T: GCMethods + Copy, -{ - pub(crate) fn handle(&self) -> Handle<T> { - self.0.handle() - } -} - -impl<T: JSTraceable + MallocSizeOf> MallocSizeOf for RootedTraceableBox<T> { - fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { - // Briefly resurrect the real Box value so we can rely on the existing calculations. - // Then immediately forget about it again to avoid dropping the box. - let inner = unsafe { Box::from_raw(self.0.ptr()) }; - let size = inner.size_of(ops); - mem::forget(inner); - size - } -} - -impl<T: JSTraceable + Default> Default for RootedTraceableBox<T> { - fn default() -> RootedTraceableBox<T> { - RootedTraceableBox::new(T::default()) - } -} - -impl<T: JSTraceable> Deref for RootedTraceableBox<T> { - type Target = T; - fn deref(&self) -> &T { - self.0.deref() - } -} - -impl<T: JSTraceable> DerefMut for RootedTraceableBox<T> { - fn deref_mut(&mut self) -> &mut T { - self.0.deref_mut() - } -} diff --git a/components/script/dom/bindings/utils.rs b/components/script/dom/bindings/utils.rs index 5f16d3a96d2..f97a7d9a130 100644 --- a/components/script/dom/bindings/utils.rs +++ b/components/script/dom/bindings/utils.rs @@ -6,17 +6,15 @@ use std::cell::RefCell; use std::thread::LocalKey; -use std::{ptr, slice}; use js::conversions::ToJSValConvertible; use js::glue::{IsWrapper, JSPrincipalsCallbacks, UnwrapObjectDynamic, UnwrapObjectStatic}; use js::jsapi::{ - CallArgs, DOMCallbacks, HandleId as RawHandleId, HandleObject as RawHandleObject, - JS_DeprecatedStringHasLatin1Chars, JS_EnumerateStandardClasses, JS_FreezeObject, - JS_GetLatin1StringCharsAndLength, JS_IsGlobalObject, JS_ResolveStandardClass, JSContext, - JSObject, MutableHandleIdVector as RawMutableHandleIdVector, + CallArgs, DOMCallbacks, HandleObject as RawHandleObject, JS_FreezeObject, JSContext, JSObject, }; -use js::rust::{Handle, HandleObject, MutableHandleValue, get_object_class, is_dom_class}; +use js::rust::{HandleObject, MutableHandleValue, get_object_class, is_dom_class}; +use script_bindings::interfaces::DomHelpers; +use script_bindings::settings_stack::StackEntry; use crate::DomTypes; use crate::dom::bindings::codegen::{InterfaceObjectMap, PrototypeList}; @@ -29,7 +27,7 @@ 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::settings_stack; use crate::dom::globalscope::GlobalScope; use crate::dom::windowproxy::WindowProxyHandler; use crate::realms::InRealm; @@ -105,61 +103,6 @@ fn is_platform_object( } } -/// Enumerate lazy properties of a global object. -pub(crate) unsafe extern "C" fn enumerate_global<D: DomTypes>( - cx: *mut JSContext, - obj: RawHandleObject, - _props: RawMutableHandleIdVector, - _enumerable_only: bool, -) -> bool { - assert!(JS_IsGlobalObject(obj.get())); - if !JS_EnumerateStandardClasses(cx, obj) { - return false; - } - for init_fun in <D as DomHelpers<D>>::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<D: DomTypes>( - cx: *mut JSContext, - obj: RawHandleObject, - id: RawHandleId, - rval: *mut bool, -) -> bool { - assert!(JS_IsGlobalObject(obj.get())); - if !JS_ResolveStandardClass(cx, obj, id, rval) { - return false; - } - if *rval { - return true; - } - if !id.is_string() { - *rval = false; - return true; - } - - let string = id.to_string(); - if !JS_DeprecatedStringHasLatin1Chars(string) { - *rval = false; - return true; - } - let mut length = 0; - let ptr = JS_GetLatin1StringCharsAndLength(cx, ptr::null(), string, &mut length); - assert!(!ptr.is_null()); - let bytes = slice::from_raw_parts(ptr, length); - - if let Some(init_fun) = <D as DomHelpers<D>>::interface_map().get(bytes) { - init_fun(SafeJSContext::from_ptr(cx), Handle::from_raw(obj)); - *rval = true; - } else { - *rval = false; - } - true -} - unsafe extern "C" fn instance_class_has_proto_at_depth( clasp: *const js::jsapi::JSClass, proto_id: u32, @@ -175,48 +118,6 @@ pub(crate) const DOM_CALLBACKS: DOMCallbacks = DOMCallbacks { instanceClassMatchesProto: Some(instance_class_has_proto_at_depth), }; -/// Operations that must be invoked from the generated bindings. -pub(crate) trait DomHelpers<D: DomTypes> { - fn throw_dom_exception( - cx: SafeJSContext, - global: &D::GlobalScope, - result: Error, - can_gc: CanGc, - ); - - unsafe fn call_html_constructor<T: DerivedFrom<D::Element> + DomObject>( - cx: SafeJSContext, - args: &CallArgs, - global: &D::GlobalScope, - proto_id: crate::dom::bindings::codegen::PrototypeList::ID, - creator: unsafe fn(SafeJSContext, HandleObject, *mut ProtoOrIfaceArray), - can_gc: CanGc, - ) -> bool; - - fn settings_stack() -> &'static LocalKey<RefCell<Vec<StackEntry<D>>>>; - - 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<T, U>(obj: Box<T>, global: &U, can_gc: CanGc) -> DomRoot<T> - where - T: DomObject + DomObjectWrap<D>, - U: DerivedFrom<D::GlobalScope>; - - fn report_pending_exception( - cx: SafeJSContext, - dispatch_event: bool, - realm: InRealm, - can_gc: CanGc, - ); -} - impl DomHelpers<crate::DomTypeHolder> for crate::DomTypeHolder { fn throw_dom_exception( cx: SafeJSContext, @@ -227,7 +128,7 @@ impl DomHelpers<crate::DomTypeHolder> for crate::DomTypeHolder { throw_dom_exception(cx, global, result, can_gc) } - unsafe fn call_html_constructor< + fn call_html_constructor< T: DerivedFrom<<crate::DomTypeHolder as DomTypes>::Element> + DomObject, >( cx: SafeJSContext, diff --git a/components/script/dom/bindings/weakref.rs b/components/script/dom/bindings/weakref.rs index 1c86b68c706..c1cb012b7c2 100644 --- a/components/script/dom/bindings/weakref.rs +++ b/components/script/dom/bindings/weakref.rs @@ -8,7 +8,7 @@ use std::ops::{Deref, DerefMut, Drop}; use js::jsapi::JSTracer; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; -pub use script_bindings::weakref::*; +pub(crate) use script_bindings::weakref::*; use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::root::DomRoot; diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs index 11ffdc752d0..6cce4b0f0e7 100644 --- a/components/script/dom/globalscope.rs +++ b/components/script/dom/globalscope.rs @@ -54,6 +54,7 @@ use net_traits::{ ResourceThreads, fetch_async, }; use profile_traits::{ipc as profile_ipc, mem as profile_mem, time as profile_time}; +use script_bindings::interfaces::GlobalScopeHelpers; use script_traits::serializable::{BlobData, BlobImpl, FileBlob}; use script_traits::transferable::MessagePortImpl; use script_traits::{ @@ -3318,30 +3319,6 @@ unsafe fn global_scope_from_global_static(global: *mut JSObject) -> DomRoot<Glob root_from_object_static(global).unwrap() } -/// Operations that must be invoked from the generated bindings. -#[allow(unsafe_code)] -pub(crate) trait GlobalScopeHelpers<D: crate::DomTypes> { - unsafe fn from_context(cx: *mut JSContext, realm: InRealm) -> DomRoot<D::GlobalScope>; - fn get_cx() -> SafeJSContext; - unsafe fn from_object(obj: *mut JSObject) -> DomRoot<D::GlobalScope>; - fn from_reflector(reflector: &impl DomObject, realm: InRealm) -> DomRoot<D::GlobalScope>; - - unsafe fn from_object_maybe_wrapped( - obj: *mut JSObject, - cx: *mut JSContext, - ) -> DomRoot<D::GlobalScope>; - - fn origin(&self) -> &MutableOrigin; - - fn incumbent() -> Option<DomRoot<D::GlobalScope>>; - - fn perform_a_microtask_checkpoint(&self, can_gc: CanGc); - - fn get_url(&self) -> ServoUrl; - - fn is_secure_context(&self) -> bool; -} - #[allow(unsafe_code)] impl GlobalScopeHelpers<crate::DomTypeHolder> for GlobalScope { unsafe fn from_context(cx: *mut JSContext, realm: InRealm) -> DomRoot<Self> { diff --git a/components/script/dom/promise.rs b/components/script/dom/promise.rs index b970e7f3342..1ac1aa52a45 100644 --- a/components/script/dom/promise.rs +++ b/components/script/dom/promise.rs @@ -29,6 +29,7 @@ use js::rust::wrappers::{ ResolvePromise, SetAnyPromiseIsHandled, SetPromiseUserInputEventHandlingState, }; use js::rust::{HandleObject, HandleValue, MutableHandleObject, Runtime}; +use script_bindings::interfaces::PromiseHelpers; use crate::dom::bindings::conversions::root_from_object; use crate::dom::bindings::error::{Error, ErrorToJsval}; @@ -387,15 +388,6 @@ fn create_native_handler_function( } } -/// Operations that must be invoked from the generated bindings. -pub(crate) trait PromiseHelpers<D: crate::DomTypes> { - fn new_resolved( - global: &D::GlobalScope, - cx: SafeJSContext, - value: impl ToJSValConvertible, - ) -> Rc<D::Promise>; -} - impl PromiseHelpers<crate::DomTypeHolder> for Promise { fn new_resolved( global: &GlobalScope, diff --git a/components/script/lib.rs b/components/script/lib.rs index b2ade0330c9..7a866f1c7b2 100644 --- a/components/script/lib.rs +++ b/components/script/lib.rs @@ -45,13 +45,11 @@ pub(crate) mod document_collection; pub(crate) mod iframe_collection; pub(crate) mod image_animation; pub mod layout_dom; -mod mem; #[allow(unsafe_code)] pub(crate) mod messaging; mod microtask; mod navigation; mod network_listener; -#[allow(dead_code)] mod realms; mod routed_promise; #[allow(dead_code)] @@ -82,12 +80,12 @@ mod xpath; mod svgpath; pub use init::init; +pub(crate) use script_bindings::DomTypes; pub use script_runtime::JSEngineSetup; pub use script_thread::ScriptThread; pub use serviceworker_manager::ServiceWorkerManager; pub(crate) use crate::dom::bindings::codegen::DomTypeHolder::DomTypeHolder; -pub(crate) use crate::dom::bindings::codegen::DomTypes::DomTypes; // These trait exports are public, because they are used in the DOM bindings. // Since they are used in derive macros, // it is useful that they are accessible at the root of the crate. diff --git a/components/script/realms.rs b/components/script/realms.rs index 1254b604b42..9a392e7f6af 100644 --- a/components/script/realms.rs +++ b/components/script/realms.rs @@ -2,67 +2,10 @@ * 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::jsapi::{GetCurrentRealmOrNull, JSAutoRealm}; - -use crate::DomTypes; -use crate::dom::bindings::reflector::DomObject; -use crate::dom::globalscope::GlobalScopeHelpers; -use crate::script_runtime::JSContext; - -pub(crate) struct AlreadyInRealm(()); - -impl AlreadyInRealm { - #![allow(unsafe_code)] - pub(crate) fn assert<D: DomTypes>() -> AlreadyInRealm { - unsafe { - assert!(!GetCurrentRealmOrNull(*D::GlobalScope::get_cx()).is_null()); - } - AlreadyInRealm(()) - } - - pub(crate) fn assert_for_cx(cx: JSContext) -> AlreadyInRealm { - unsafe { - assert!(!GetCurrentRealmOrNull(*cx).is_null()); - } - AlreadyInRealm(()) - } -} - -#[derive(Clone, Copy)] -pub(crate) enum InRealm<'a> { - Already(&'a AlreadyInRealm), - Entered(&'a JSAutoRealm), -} - -impl<'a> From<&'a AlreadyInRealm> for InRealm<'a> { - fn from(token: &'a AlreadyInRealm) -> InRealm<'a> { - InRealm::already(token) - } -} - -impl<'a> From<&'a JSAutoRealm> for InRealm<'a> { - fn from(token: &'a JSAutoRealm) -> InRealm<'a> { - InRealm::entered(token) - } -} - -impl InRealm<'_> { - pub(crate) fn already(token: &AlreadyInRealm) -> InRealm { - InRealm::Already(token) - } - - pub(crate) fn entered(token: &JSAutoRealm) -> InRealm { - InRealm::Entered(token) - } -} - -pub(crate) fn enter_realm_generic<D: DomTypes>(object: &impl DomObject) -> JSAutoRealm { - JSAutoRealm::new( - *D::GlobalScope::get_cx(), - object.reflector().get_jsobject().get(), - ) -} +use js::jsapi::JSAutoRealm; +pub(crate) use script_bindings::realms::{AlreadyInRealm, InRealm}; +use script_bindings::reflector::DomObject; pub(crate) fn enter_realm(object: &impl DomObject) -> JSAutoRealm { - enter_realm_generic::<crate::DomTypeHolder>(object) + script_bindings::realms::enter_realm::<crate::DomTypeHolder>(object) } diff --git a/components/script/webdriver_handlers.rs b/components/script/webdriver_handlers.rs index 17c13e3c0b9..956ab4791da 100644 --- a/components/script/webdriver_handlers.rs +++ b/components/script/webdriver_handlers.rs @@ -27,6 +27,7 @@ use net_traits::CoreResourceMsg::{ DeleteCookie, DeleteCookies, GetCookiesDataForUrl, SetCookieForUrl, }; use net_traits::IpcSend; +use script_bindings::conversions::is_array_like; use servo_url::ServoUrl; use webdriver::common::{WebElement, WebFrame, WebWindow}; use webdriver::error::ErrorStatus; @@ -45,8 +46,7 @@ use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; use crate::dom::bindings::codegen::Bindings::XMLSerializerBinding::XMLSerializerMethods; use crate::dom::bindings::conversions::{ ConversionBehavior, ConversionResult, FromJSValConvertible, StringificationBehavior, - get_property, get_property_jsval, is_array_like, jsid_to_string, jsstring_to_str, - root_from_object, + get_property, get_property_jsval, jsid_to_string, jsstring_to_str, root_from_object, }; use crate::dom::bindings::error::{Error, throw_dom_exception}; use crate::dom::bindings::inheritance::Castable; diff --git a/components/script_bindings/Cargo.toml b/components/script_bindings/Cargo.toml index 7d19ab39c25..7e4e751a2a8 100644 --- a/components/script_bindings/Cargo.toml +++ b/components/script_bindings/Cargo.toml @@ -19,6 +19,9 @@ path = "lib.rs" bitflags = { workspace = true } crossbeam-channel = { workspace = true } cssparser = { workspace = true } +deny_public_fields = { path = "../deny_public_fields" } +dom_struct = { path = "../dom_struct" } +domobject_derive = { path = "../domobject_derive" } html5ever = { workspace = true } indexmap = { workspace = true } js = { workspace = true } @@ -29,13 +32,16 @@ malloc_size_of = { workspace = true } malloc_size_of_derive = { workspace = true } num-traits = { workspace = true } parking_lot = { workspace = true } +phf = "0.11" regex = { workspace = true } servo_arc = { workspace = true } servo_config = { path = "../config" } +servo_url = { path = "../url" } smallvec = { workspace = true } stylo = { workspace = true } stylo_atoms = { workspace = true } tendril = { version = "0.4.1", features = ["encoding_rs"] } +tracing = { workspace = true, optional = true } webxr-api = { workspace = true, optional = true } xml5ever = { workspace = true } @@ -46,6 +52,7 @@ serde_json = { workspace = true } [features] bluetooth = [] +tracing = ["dep:tracing"] webgpu = [] webxr = ["webxr-api"] diff --git a/components/script_bindings/callback.rs b/components/script_bindings/callback.rs index d891535e671..a31b57f677f 100644 --- a/components/script_bindings/callback.rs +++ b/components/script_bindings/callback.rs @@ -2,10 +2,31 @@ * 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::jsapi::JSObject; -use js::rust::HandleObject; +//! Base classes to work with IDL callbacks. +use std::default::Default; +use std::ffi::CString; +use std::mem::drop; +use std::rc::Rc; + +use js::jsapi::{ + AddRawValueRoot, EnterRealm, Heap, IsCallable, JSObject, LeaveRealm, Realm, RemoveRawValueRoot, +}; +use js::jsval::{JSVal, ObjectValue, UndefinedValue}; +use js::rust::wrappers::{JS_GetProperty, JS_WrapObject}; +use js::rust::{HandleObject, MutableHandleValue, Runtime}; + +use crate::DomTypes; +use crate::codegen::GenericBindings::WindowBinding::Window_Binding::WindowMethods; +use crate::error::{Error, Fallible}; +use crate::inheritance::Castable; +use crate::interfaces::{DocumentHelpers, DomHelpers, GlobalScopeHelpers}; +use crate::realms::{InRealm, enter_realm}; use crate::reflector::DomObject; +use crate::root::{Dom, DomRoot}; +use crate::script_runtime::{CanGc, JSContext}; +use crate::settings_stack::{GenericAutoEntryScript, GenericAutoIncumbentScript}; +use crate::utils::AsCCharPtrPtr; pub trait ThisReflector { fn jsobject(&self) -> *mut JSObject; @@ -22,3 +43,275 @@ impl ThisReflector for HandleObject<'_> { self.get() } } + +/// The exception handling used for a call. +#[derive(Clone, Copy, PartialEq)] +pub enum ExceptionHandling { + /// Report any exception and don't throw it to the caller code. + Report, + /// Throw any exception to the caller code. + Rethrow, +} + +/// A common base class for representing IDL callback function and +/// callback interface types. +#[derive(JSTraceable)] +#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)] +pub struct CallbackObject<D: DomTypes> { + /// The underlying `JSObject`. + callback: Heap<*mut JSObject>, + permanent_js_root: Heap<JSVal>, + + /// The ["callback context"], that is, the global to use as incumbent + /// global when calling the callback. + /// + /// Looking at the WebIDL standard, it appears as though there would always + /// be a value here, but [sometimes] callback functions are created by + /// hand-waving without defining the value of the callback context, and + /// without any JavaScript code on the stack to grab an incumbent global + /// from. + /// + /// ["callback context"]: https://heycam.github.io/webidl/#dfn-callback-context + /// [sometimes]: https://github.com/whatwg/html/issues/2248 + incumbent: Option<Dom<D::GlobalScope>>, +} + +impl<D: DomTypes> CallbackObject<D> { + #[cfg_attr(crown, allow(crown::unrooted_must_root))] + // These are used by the bindings and do not need `default()` functions. + #[allow(clippy::new_without_default)] + fn new() -> Self { + Self { + callback: Heap::default(), + permanent_js_root: Heap::default(), + incumbent: D::GlobalScope::incumbent().map(|i| Dom::from_ref(&*i)), + } + } + + pub fn get(&self) -> *mut JSObject { + self.callback.get() + } + + #[allow(unsafe_code)] + unsafe fn init(&mut self, cx: JSContext, callback: *mut JSObject) { + self.callback.set(callback); + self.permanent_js_root.set(ObjectValue(callback)); + assert!(AddRawValueRoot( + *cx, + self.permanent_js_root.get_unsafe(), + b"CallbackObject::root\n".as_c_char_ptr() + )); + } +} + +impl<D: DomTypes> Drop for CallbackObject<D> { + #[allow(unsafe_code)] + fn drop(&mut self) { + unsafe { + if let Some(cx) = Runtime::get() { + RemoveRawValueRoot(cx.as_ptr(), self.permanent_js_root.get_unsafe()); + } + } + } +} + +impl<D: DomTypes> PartialEq for CallbackObject<D> { + fn eq(&self, other: &CallbackObject<D>) -> bool { + self.callback.get() == other.callback.get() + } +} + +/// A trait to be implemented by concrete IDL callback function and +/// callback interface types. +pub trait CallbackContainer<D: DomTypes> { + /// Create a new CallbackContainer object for the given `JSObject`. + /// + /// # Safety + /// `callback` must point to a valid, non-null JSObject. + unsafe fn new(cx: JSContext, callback: *mut JSObject) -> Rc<Self>; + /// Returns the underlying `CallbackObject`. + fn callback_holder(&self) -> &CallbackObject<D>; + /// Returns the underlying `JSObject`. + fn callback(&self) -> *mut JSObject { + self.callback_holder().get() + } + /// Returns the ["callback context"], that is, the global to use as + /// incumbent global when calling the callback. + /// + /// ["callback context"]: https://heycam.github.io/webidl/#dfn-callback-context + fn incumbent(&self) -> Option<&D::GlobalScope> { + self.callback_holder().incumbent.as_deref() + } +} + +/// A common base class for representing IDL callback function types. +#[derive(JSTraceable, PartialEq)] +#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)] +pub struct CallbackFunction<D: DomTypes> { + object: CallbackObject<D>, +} + +impl<D: DomTypes> CallbackFunction<D> { + /// Create a new `CallbackFunction` for this object. + #[cfg_attr(crown, allow(crown::unrooted_must_root))] + // These are used by the bindings and do not need `default()` functions. + #[allow(clippy::new_without_default)] + pub fn new() -> Self { + Self { + object: CallbackObject::new(), + } + } + + /// Returns the underlying `CallbackObject`. + pub fn callback_holder(&self) -> &CallbackObject<D> { + &self.object + } + + /// Initialize the callback function with a value. + /// Should be called once this object is done moving. + /// + /// # Safety + /// `callback` must point to a valid, non-null JSObject. + pub unsafe fn init(&mut self, cx: JSContext, callback: *mut JSObject) { + self.object.init(cx, callback); + } +} + +/// A common base class for representing IDL callback interface types. +#[derive(JSTraceable, PartialEq)] +#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)] +pub struct CallbackInterface<D: DomTypes> { + object: CallbackObject<D>, +} + +impl<D: DomTypes> CallbackInterface<D> { + /// Create a new CallbackInterface object for the given `JSObject`. + // These are used by the bindings and do not need `default()` functions. + #[allow(clippy::new_without_default)] + pub fn new() -> Self { + Self { + object: CallbackObject::new(), + } + } + + /// Returns the underlying `CallbackObject`. + pub fn callback_holder(&self) -> &CallbackObject<D> { + &self.object + } + + /// Initialize the callback function with a value. + /// Should be called once this object is done moving. + /// + /// # Safety + /// `callback` must point to a valid, non-null JSObject. + pub unsafe fn init(&mut self, cx: JSContext, callback: *mut JSObject) { + self.object.init(cx, callback); + } + + /// Returns the property with the given `name`, if it is a callable object, + /// or an error otherwise. + pub fn get_callable_property(&self, cx: JSContext, name: &str) -> Fallible<JSVal> { + rooted!(in(*cx) let mut callable = UndefinedValue()); + rooted!(in(*cx) let obj = self.callback_holder().get()); + unsafe { + let c_name = CString::new(name).unwrap(); + if !JS_GetProperty(*cx, obj.handle(), c_name.as_ptr(), callable.handle_mut()) { + return Err(Error::JSFailed); + } + + if !callable.is_object() || !IsCallable(callable.to_object()) { + return Err(Error::Type(format!( + "The value of the {} property is not callable", + name + ))); + } + } + Ok(callable.get()) + } +} + +/// Wraps the reflector for `p` into the realm of `cx`. +pub(crate) fn wrap_call_this_value<T: ThisReflector>( + cx: JSContext, + p: &T, + mut rval: MutableHandleValue, +) -> bool { + rooted!(in(*cx) let mut obj = p.jsobject()); + assert!(!obj.is_null()); + + unsafe { + if !JS_WrapObject(*cx, obj.handle_mut()) { + return false; + } + } + + rval.set(ObjectValue(*obj)); + true +} + +/// A class that performs whatever setup we need to safely make a call while +/// this class is on the stack. After `new` returns, the call is safe to make. +pub struct CallSetup<D: DomTypes> { + /// The global for reporting exceptions. This is the global object of the + /// (possibly wrapped) callback object. + exception_global: DomRoot<D::GlobalScope>, + /// The `JSContext` used for the call. + cx: JSContext, + /// The realm we were in before the call. + old_realm: *mut Realm, + /// The exception handling used for the call. + handling: ExceptionHandling, + /// <https://heycam.github.io/webidl/#es-invoking-callback-functions> + /// steps 8 and 18.2. + entry_script: Option<GenericAutoEntryScript<D>>, + /// <https://heycam.github.io/webidl/#es-invoking-callback-functions> + /// steps 9 and 18.1. + incumbent_script: Option<GenericAutoIncumbentScript<D>>, +} + +impl<D: DomTypes> CallSetup<D> { + /// Performs the setup needed to make a call. + #[cfg_attr(crown, allow(crown::unrooted_must_root))] + pub fn new<T: CallbackContainer<D>>(callback: &T, handling: ExceptionHandling) -> Self { + let global = unsafe { D::GlobalScope::from_object(callback.callback()) }; + if let Some(window) = global.downcast::<D::Window>() { + window.Document().ensure_safe_to_run_script_or_layout(); + } + let cx = D::GlobalScope::get_cx(); + + let aes = GenericAutoEntryScript::<D>::new(&global); + let ais = callback.incumbent().map(GenericAutoIncumbentScript::new); + CallSetup { + exception_global: global, + cx, + old_realm: unsafe { EnterRealm(*cx, callback.callback()) }, + handling, + entry_script: Some(aes), + incumbent_script: ais, + } + } + + /// Returns the `JSContext` used for the call. + pub fn get_context(&self) -> JSContext { + self.cx + } +} + +impl<D: DomTypes> Drop for CallSetup<D> { + fn drop(&mut self) { + unsafe { + LeaveRealm(*self.cx, self.old_realm); + } + if self.handling == ExceptionHandling::Report { + let ar = enter_realm::<D>(&*self.exception_global); + <D as DomHelpers<D>>::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_bindings/codegen/Bindings.conf b/components/script_bindings/codegen/Bindings.conf index 1c888b27292..a3adf0f39ab 100644 --- a/components/script_bindings/codegen/Bindings.conf +++ b/components/script_bindings/codegen/Bindings.conf @@ -141,7 +141,7 @@ DOMInterfaces = { }, 'Document': { - 'additionalTraits': ["script_bindings::interfaces::DocumentHelpers"], + 'additionalTraits': ["crate::interfaces::DocumentHelpers"], 'canGc': ['Close', 'CreateElement', 'CreateElementNS', 'ImportNode', 'SetTitle', 'Write', 'Writeln', 'CreateEvent', 'CreateRange', 'Open', 'Open_', 'CreateComment', 'CreateAttribute', 'CreateAttributeNS', 'CreateDocumentFragment', 'CreateTextNode', 'CreateCDATASection', 'CreateProcessingInstruction', 'Prepend', 'Append', 'ReplaceChildren', 'SetBgColor', 'SetFgColor', 'Fonts', 'ElementFromPoint', 'ElementsFromPoint', 'ExitFullscreen', 'CreateExpression', 'CreateNSResolver', 'Evaluate'], }, @@ -240,7 +240,7 @@ DOMInterfaces = { }, 'GlobalScope': { - 'additionalTraits': ['crate::dom::globalscope::GlobalScopeHelpers<Self>'], + 'additionalTraits': ['crate::interfaces::GlobalScopeHelpers<Self>'], }, 'GPU': { @@ -494,7 +494,7 @@ DOMInterfaces = { 'Promise': { 'spiderMonkeyInterface': True, - 'additionalTraits': ["crate::dom::promise::PromiseHelpers<Self>", "js::conversions::FromJSValConvertibleRc"] + 'additionalTraits': ["crate::interfaces::PromiseHelpers<Self>", "js::conversions::FromJSValConvertibleRc"] }, 'Range': { @@ -531,7 +531,7 @@ DOMInterfaces = { 'ServoInternals': { 'inRealms': ['ReportMemory'], 'canGc': ['ReportMemory'], - 'additionalTraits': ['script_bindings::interfaces::ServoInternalsHelpers'], + 'additionalTraits': ['crate::interfaces::ServoInternalsHelpers'], }, 'ShadowRoot': { @@ -555,7 +555,7 @@ DOMInterfaces = { 'TestBinding': { 'inRealms': ['PromiseAttribute', 'PromiseNativeHandler'], 'canGc': ['InterfaceAttribute', 'GetInterfaceAttributeNullable', 'ReceiveInterface', 'ReceiveInterfaceSequence', 'ReceiveNullableInterface', 'PromiseAttribute', 'PromiseNativeHandler', 'PromiseResolveNative', 'PromiseRejectNative', 'PromiseRejectWithTypeError'], - 'additionalTraits': ['script_bindings::interfaces::TestBindingHelpers'], + 'additionalTraits': ['crate::interfaces::TestBindingHelpers'], }, 'TestWorklet': { @@ -586,13 +586,13 @@ DOMInterfaces = { 'WebGL2RenderingContext': { 'canGc': ['MakeXRCompatible'], - 'additionalTraits': ['script_bindings::interfaces::WebGL2RenderingContextHelpers'], + 'additionalTraits': ['crate::interfaces::WebGL2RenderingContextHelpers'], }, 'Window': { 'canGc': ['Stop', 'Fetch', 'Scroll', 'Scroll_','ScrollBy', 'ScrollBy_', 'Stop', 'Fetch', 'Open', 'CreateImageBitmap'], 'inRealms': ['Fetch', 'GetOpener'], - 'additionalTraits': ['script_bindings::interfaces::WindowHelpers'], + 'additionalTraits': ['crate::interfaces::WindowHelpers'], }, 'WindowProxy' : { diff --git a/components/script_bindings/codegen/CodegenRust.py b/components/script_bindings/codegen/CodegenRust.py index 09a4bd4139f..4f87ec792b4 100644 --- a/components/script_bindings/codegen/CodegenRust.py +++ b/components/script_bindings/codegen/CodegenRust.py @@ -2282,10 +2282,10 @@ class CGImports(CGWrapper): parentName = descriptor.getParentName() while parentName: descriptor = descriptorProvider.getDescriptor(parentName) - extras += [descriptor.path, descriptor.bindingPath] + extras += [descriptor.bindingPath] parentName = descriptor.getParentName() elif t.isType() and t.isRecord(): - extras += ['script_bindings::record::Record'] + extras += ['crate::record::Record'] elif isinstance(t, IDLPromiseType): pass else: @@ -2316,7 +2316,7 @@ class CGTemplatedType(CGWrapper): class CGNamespace(CGWrapper): def __init__(self, namespace, child, public=False): - pub = "pub(crate) " if public else "" + pub = "pub " if public else "" pre = f"{pub}mod {namespace} {{\n" post = f"}} // mod {namespace}" CGWrapper.__init__(self, child, pre=pre, post=post) @@ -2337,15 +2337,15 @@ def DOMClassTypeId(desc): inner = "" if desc.hasDescendants(): if desc.interface.getExtendedAttribute("Abstract"): - return "script_bindings::codegen::InheritTypes::TopTypeId { abstract_: () }" + return "crate::codegen::InheritTypes::TopTypeId { abstract_: () }" name = desc.interface.identifier.name - inner = f"(script_bindings::codegen::InheritTypes::{name}TypeId::{name})" + inner = f"(crate::codegen::InheritTypes::{name}TypeId::{name})" elif len(protochain) == 1: - return "script_bindings::codegen::InheritTypes::TopTypeId { alone: () }" + return "crate::codegen::InheritTypes::TopTypeId { alone: () }" reversed_protochain = list(reversed(protochain)) for (child, parent) in zip(reversed_protochain, reversed_protochain[1:]): - inner = f"(script_bindings::codegen::InheritTypes::{parent}TypeId::{child}{inner})" - return f"script_bindings::codegen::InheritTypes::TopTypeId {{ {protochain[0].lower()}: {inner} }}" + inner = f"(crate::codegen::InheritTypes::{parent}TypeId::{child}{inner})" + return f"crate::codegen::InheritTypes::TopTypeId {{ {protochain[0].lower()}: {inner} }}" def DOMClass(descriptor): @@ -2421,7 +2421,7 @@ pub(crate) fn init_class_ops<D: DomTypes>() {{ }}); }} -pub(crate) static Class: ThreadUnsafeOnceLock<DOMJSClass> = ThreadUnsafeOnceLock::new(); +pub static Class: ThreadUnsafeOnceLock<DOMJSClass> = ThreadUnsafeOnceLock::new(); pub(crate) fn init_domjs_class<D: DomTypes>() {{ init_class_ops::<D>(); @@ -2637,13 +2637,10 @@ def UnionTypes(descriptors, dictionaries, callbacks, typedefs, config): """ imports = [ - 'crate::dom', - 'crate::dom::bindings::import::base::*', - 'crate::dom::bindings::codegen::DomTypes::DomTypes', - 'crate::dom::bindings::conversions::windowproxy_from_handlevalue', - 'script_bindings::record::Record', - 'crate::dom::types::*', - 'crate::dom::windowproxy::WindowProxy', + 'crate::import::base::*', + 'crate::codegen::DomTypes::DomTypes', + 'crate::conversions::windowproxy_from_handlevalue', + 'crate::record::Record', 'js::typedarray', ] @@ -2679,13 +2676,13 @@ def UnionTypes(descriptors, dictionaries, callbacks, typedefs, config): def DomTypes(descriptors, descriptorProvider, dictionaries, callbacks, typedefs, config): traits = [ - "crate::dom::bindings::utils::DomHelpers<Self>", + "crate::interfaces::DomHelpers<Self>", "js::rust::Trace", "malloc_size_of::MallocSizeOf", "Sized", ] joinedTraits = ' + '.join(traits) - elements = [CGGeneric(f"pub(crate) trait DomTypes: {joinedTraits} where Self: 'static {{\n")] + elements = [CGGeneric(f"pub trait DomTypes: {joinedTraits} where Self: 'static {{\n")] def fixupInterfaceTypeReferences(typename): return typename.replace("D::", "Self::") @@ -2705,10 +2702,10 @@ def DomTypes(descriptors, descriptorProvider, dictionaries, callbacks, typedefs, chain = chain[:-1] if chain: - traits += ["crate::dom::bindings::inheritance::Castable"] + traits += ["crate::inheritance::Castable"] for parent in chain: - traits += [f"crate::dom::bindings::conversions::DerivedFrom<Self::{parent}>"] + traits += [f"crate::conversions::DerivedFrom<Self::{parent}>"] iterableDecl = descriptor.interface.maplikeOrSetlikeOrIterable if iterableDecl: @@ -2719,27 +2716,27 @@ def DomTypes(descriptors, descriptorProvider, dictionaries, callbacks, typedefs, valuetype = fixupInterfaceTypeReferences( getRetvalDeclarationForType(iterableDecl.valueType, descriptor).define() ) - traits += [f"crate::dom::bindings::like::Maplike<Key={keytype}, Value={valuetype}>"] + traits += [f"crate::like::Maplike<Key={keytype}, Value={valuetype}>"] if iterableDecl.isSetlike(): keytype = fixupInterfaceTypeReferences( getRetvalDeclarationForType(iterableDecl.keyType, descriptor).define() ) - traits += [f"crate::dom::bindings::like::Setlike<Key={keytype}>"] + traits += [f"crate::like::Setlike<Key={keytype}>"] if iterableDecl.hasKeyType(): traits += [ - "crate::dom::bindings::reflector::DomObjectIteratorWrap<Self>", - "crate::dom::bindings::iterable::IteratorDerives", + "crate::reflector::DomObjectIteratorWrap<Self>", + "crate::iterable::IteratorDerives", ] if descriptor.weakReferenceable: - traits += ["crate::dom::bindings::weakref::WeakReferenceable"] + traits += ["crate::weakref::WeakReferenceable"] if not descriptor.interface.isNamespace(): traits += [ "js::conversions::ToJSValConvertible", - "crate::dom::bindings::reflector::MutDomObject", - "crate::dom::bindings::reflector::DomObject", - "crate::dom::bindings::reflector::DomGlobalGeneric<Self>", + "crate::reflector::MutDomObject", + "crate::reflector::DomObject", + "crate::reflector::DomGlobalGeneric<Self>", "malloc_size_of::MallocSizeOf", ] @@ -2750,12 +2747,12 @@ def DomTypes(descriptors, descriptorProvider, dictionaries, callbacks, typedefs, and not descriptor.interface.isIteratorInterface() ): traits += [ - "crate::dom::bindings::conversions::IDLInterface", + "crate::conversions::IDLInterface", "PartialEq", ] if descriptor.concrete and not descriptor.isGlobal(): - traits += ["crate::dom::bindings::reflector::DomObjectWrap<Self>"] + traits += ["crate::reflector::DomObjectWrap<Self>"] if not descriptor.interface.isCallback() and not descriptor.interface.isIteratorInterface(): nonConstMembers = [m for m in descriptor.interface.members if not m.isConst()] @@ -2766,7 +2763,7 @@ def DomTypes(descriptors, descriptorProvider, dictionaries, callbacks, typedefs, or descriptor.interface.legacyFactoryFunctions ): namespace = f"{toBindingPath(descriptor)}" - traits += [f"crate::dom::bindings::codegen::Bindings::{namespace}::{iface_name}Methods<Self>"] + traits += [f"crate::codegen::GenericBindings::{namespace}::{iface_name}Methods<Self>"] isPromise = firstCap(iface_name) == "Promise" elements += [ CGGeneric(" #[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]\n"), @@ -2778,8 +2775,8 @@ def DomTypes(descriptors, descriptorProvider, dictionaries, callbacks, typedefs, ] elements += [CGGeneric("}\n")] imports = [ - CGGeneric("use crate::dom::bindings::root::DomRoot;\n"), - CGGeneric("use crate::dom::bindings::str::DOMString;\n"), + CGGeneric("use crate::root::DomRoot;\n"), + CGGeneric("use crate::str::DOMString;\n"), ] return CGList(imports + elements) @@ -3403,7 +3400,7 @@ class CGCollectJSONAttributesMethod(CGAbstractMethod): self.toJSONMethod = toJSONMethod def definition_body(self): - ret = """let incumbent_global = GlobalScope::incumbent().expect("no incumbent global"); + ret = """let incumbent_global = D::GlobalScope::incumbent().expect("no incumbent global"); let global = incumbent_global.reflector().get_jsobject();\n""" interface = self.descriptor.interface for m in interface.members: @@ -4322,7 +4319,7 @@ class CGDefaultToJSONMethod(CGSpecializedMethod): def definition_body(self): ret = dedent(""" - use crate::dom::bindings::inheritance::HasParent; + use crate::inheritance::HasParent; rooted!(in(cx) let result = JS_NewPlainObject(cx)); if result.is_null() { return false; @@ -4965,7 +4962,7 @@ class CGEnum(CGThing): decl = f""" #[repr(usize)] #[derive({derives})] -pub(crate) enum {ident} {{ +pub enum {ident} {{ {enums} }} """ @@ -4974,10 +4971,10 @@ pub(crate) enum {ident} {{ for val in list(enum.values())]) inner = f""" -use crate::dom::bindings::conversions::ConversionResult; -use crate::dom::bindings::conversions::FromJSValConvertible; -use crate::dom::bindings::conversions::ToJSValConvertible; -use crate::dom::bindings::utils::find_enum_value; +use crate::utils::find_enum_value; +use js::conversions::ConversionResult; +use js::conversions::FromJSValConvertible; +use js::conversions::ToJSValConvertible; use js::jsapi::JSContext; use js::rust::HandleValue; use js::rust::MutableHandleValue; @@ -4988,7 +4985,7 @@ pub(crate) const pairs: &[(&str, super::{ident})] = &[ ]; impl super::{ident} {{ - pub(crate) fn as_str(&self) -> &'static str {{ + pub fn as_str(&self) -> &'static str {{ pairs[*self as usize].0 }} }} @@ -5077,7 +5074,7 @@ class CGConstant(CGThing): elif tag == IDLType.Tags.double: const_type = "f64" - return f"pub(crate) const {name}: {const_type} = {value};\n" + return f"pub const {name}: {const_type} = {value};\n" def getUnionTypeTemplateVars(type, descriptorProvider): @@ -5208,7 +5205,7 @@ impl{self.generic} Clone for {self.type}{self.genericSuffix} {{ manualImpls = "\n".join(map(lambda t: self.manualImpl(t, templateVars), self.manualImpls)) return f""" #[derive({", ".join(derives)})] -pub(crate) enum {self.type}{self.generic} {{ +pub enum {self.type}{self.generic} {{ {joinedEnumValues} }} @@ -5589,7 +5586,7 @@ class ClassConstructor(ClassItem): name = cgClass.getNameString().replace(': DomTypes', '') return f""" -pub(crate) unsafe fn {self.getDecorators(True)}new({args}) -> Rc<{name}>{body} +pub unsafe fn {self.getDecorators(True)}new({args}) -> Rc<{name}>{body} """ def define(self, cgClass): @@ -5681,7 +5678,7 @@ class CGClass(CGThing): myself = '' if self.decorators != '': myself += f'{self.decorators}\n' - myself += f'{self.indent}pub(crate) struct {self.name}{specialization}' + myself += f'{self.indent}pub struct {self.name}{specialization}' result += myself assert len(self.bases) == 1 # XXjdm Can we support multiple inheritance? @@ -5689,7 +5686,7 @@ class CGClass(CGThing): result += ' {\n' if self.bases: - self.members = [ClassMember("parent", self.bases[0].name, "pub(crate)")] + self.members + self.members = [ClassMember("parent", self.bases[0].name, "pub")] + self.members result += CGIndenter(CGGeneric(self.extradeclarations), len(self.indent)).define() @@ -6590,7 +6587,7 @@ class CGDOMJSProxyHandlerDOMClass(CGThing): def define(self): return f""" -pub(crate) static Class: ThreadUnsafeOnceLock<DOMClass> = ThreadUnsafeOnceLock::new(); +pub static Class: ThreadUnsafeOnceLock<DOMClass> = ThreadUnsafeOnceLock::new(); pub(crate) fn init_proxy_handler_dom_class<D: DomTypes>() {{ Class.set({DOMClass(self.descriptor)}); @@ -6758,7 +6755,7 @@ class CGInterfaceTrait(CGThing): name = descriptor.interface.identifier.name self.cgRoot = CGWrapper(CGIndenter(CGList(methods, "")), - pre=f"pub(crate) trait {name}Methods<D: DomTypes> {{\n", + pre=f"pub trait {name}Methods<D: DomTypes> {{\n", post="}") self.empty = not methods @@ -6793,7 +6790,7 @@ class CGInitStatics(CGThing): ] for name in nonempty] flat_specs = [x for xs in specs for x in xs] specs = '\n'.join(flat_specs) - module = f"crate::dom::bindings::codegen::Bindings::{toBindingPath(descriptor)}" + module = f"crate::codegen::GenericBindings::{toBindingPath(descriptor)}" relevantMethods = [ m for m in descriptor.interface.members if m.isMethod() ] if not descriptor.interface.isCallback() else [] @@ -7055,8 +7052,7 @@ class CGDescriptor(CGThing): # These are inside the generated module cgThings = CGImports(cgThings, descriptors=[descriptor], callbacks=[], dictionaries=[], enums=[], typedefs=[], imports=[ - 'crate::dom', - 'crate::dom::bindings::import::module::*', + 'crate::import::module::*', ], config=config) cgThings = CGWrapper(CGNamespace(toBindingNamespace(descriptor.name), @@ -7066,7 +7062,7 @@ class CGDescriptor(CGThing): if reexports: reexports = ', '.join([reexportedName(name) for name in reexports]) namespace = toBindingNamespace(descriptor.name) - cgThings = CGList([CGGeneric(f'pub(crate) use self::{namespace}::{{{reexports}}};'), + cgThings = CGList([CGGeneric(f'pub use self::{namespace}::{{{reexports}}};'), cgThings], '\n') self.cgRoot = cgThings @@ -7171,10 +7167,10 @@ impl{self.generic} Clone for {self.makeClassName(self.dictionary)}{self.genericS typeName += parentSuffix if type_needs_tracing(d.parent): typeName = f"RootedTraceableBox<{typeName}>" - inheritance = f" pub(crate) parent: {typeName},\n" + inheritance = f" pub parent: {typeName},\n" else: inheritance = "" - memberDecls = [f" pub(crate) {self.makeMemberName(m[0].identifier.name)}: {self.getMemberType(m)}," + memberDecls = [f" pub {self.makeMemberName(m[0].identifier.name)}: {self.getMemberType(m)}," for m in self.memberInfo] derive = ["JSTraceable"] + self.derives @@ -7216,7 +7212,7 @@ impl{self.generic} Clone for {self.makeClassName(self.dictionary)}{self.genericS return ( f"#[derive({', '.join(derive)})]\n" f"{mustRoot}" - f"pub(crate) struct {self.makeClassName(d)}{self.generic} {{\n" + f"pub struct {self.makeClassName(d)}{self.generic} {{\n" f"{inheritance}" f"{joinedMemberDecls}\n" "}\n" @@ -7290,7 +7286,7 @@ impl{self.generic} Clone for {self.makeClassName(self.dictionary)}{self.genericS return ( f"impl{self.generic} {selfName}{self.genericSuffix} {{\n" f"{CGIndenter(CGGeneric(self.makeEmpty()), indentLevel=4).define()}\n" - " pub(crate) fn new(cx: SafeJSContext, val: HandleValue) \n" + " pub fn new(cx: SafeJSContext, val: HandleValue) \n" f" -> Result<ConversionResult<{actualType}>, ()> {{\n" f" {unsafe_if_necessary} {{\n" " let object = if val.get().is_null_or_undefined() {\n" @@ -7320,7 +7316,7 @@ impl{self.generic} Clone for {self.makeClassName(self.dictionary)}{self.genericS "\n" f"impl{self.generic} {selfName}{self.genericSuffix} {{\n" " #[allow(clippy::wrong_self_convention)]\n" - " pub(crate) unsafe fn to_jsobject(&self, cx: *mut JSContext, mut obj: MutableHandleObject) {\n" + " pub unsafe fn to_jsobject(&self, cx: *mut JSContext, mut obj: MutableHandleObject) {\n" f"{CGIndenter(CGList(memberInserts), indentLevel=8).define()} }}\n" "}\n" "\n" @@ -7396,7 +7392,7 @@ impl{self.generic} Clone for {self.makeClassName(self.dictionary)}{self.genericS parentTemplate = "parent: %s::%s::empty(),\n" fieldTemplate = "%s: %s,\n" functionTemplate = ( - "pub(crate) fn empty() -> Self {\n" + "pub fn empty() -> Self {\n" " Self {\n" "%s" " }\n" @@ -7406,7 +7402,7 @@ impl{self.generic} Clone for {self.makeClassName(self.dictionary)}{self.genericS parentTemplate = "dictionary.parent = %s::%s::empty();\n" fieldTemplate = "dictionary.%s = %s;\n" functionTemplate = ( - "pub(crate) fn empty() -> RootedTraceableBox<Self> {\n" + "pub fn empty() -> RootedTraceableBox<Self> {\n" " let mut dictionary = RootedTraceableBox::new(Self::default());\n" "%s" " dictionary\n" @@ -7463,7 +7459,7 @@ class CGInitAllStatics(CGAbstractMethod): def definition_body(self): return CGList([ - CGGeneric(f" Bindings::{toBindingModuleFileFromDescriptor(desc)}::{toBindingNamespace(desc.name)}" + CGGeneric(f" GenericBindings::{toBindingModuleFileFromDescriptor(desc)}::{toBindingNamespace(desc.name)}" "::init_statics::<D>();") for desc in self.descriptors ], "\n") @@ -7473,18 +7469,21 @@ class CGRegisterProxyHandlersMethod(CGAbstractMethod): def __init__(self, descriptors): docs = "Create the global vtables used by the generated DOM bindings to implement JS proxies." CGAbstractMethod.__init__(self, None, 'RegisterProxyHandlers', 'void', [], - unsafe=True, pub=True, docs=docs, templateArgs=["D: DomTypes"]) + pub=True, docs=docs, templateArgs=["D: DomTypes"]) self.descriptors = descriptors def definition_body(self): - return CGList([ + body = [CGGeneric("unsafe {")] + body += [ CGGeneric(f"proxy_handlers::{desc.name}.store(\n" - f" Bindings::{toBindingModuleFile(desc.name)}::{toBindingNamespace(desc.name)}" + f" GenericBindings::{toBindingModuleFile(desc.name)}::{toBindingNamespace(desc.name)}" "::DefineProxyHandler::<D>() as *mut _,\n" " std::sync::atomic::Ordering::Release,\n" ");") for desc in self.descriptors - ], "\n") + ] + body += [CGGeneric("}")] + return CGList(body, "\n") class CGRegisterProxyHandlers(CGThing): @@ -7687,11 +7686,11 @@ class CGBindingRoot(CGThing): if t.innerType.isUnion() and not t.innerType.nullable(): # Allow using the typedef's name for accessing variants. - typeDefinition = f"pub(crate) use self::{type.replace('<D>', '')} as {name};" + typeDefinition = f"pub use self::{type.replace('<D>', '')} as {name};" else: generic = "<D>" if containsDomInterface(t.innerType) else "" replacedType = type.replace("D::", "<D as DomTypes>::") - typeDefinition = f"pub(crate) type {name}{generic} = {replacedType};" + typeDefinition = f"pub type {name}{generic} = {replacedType};" cgthings.append(CGGeneric(typeDefinition)) @@ -7719,7 +7718,7 @@ class CGBindingRoot(CGThing): # These are the global imports (outside of the generated module) curr = CGImports(curr, descriptors=callbackDescriptors, callbacks=mainCallbacks, dictionaries=dictionaries, enums=enums, typedefs=typedefs, - imports=['crate::dom::bindings::import::base::*'], config=config) + imports=['crate::import::base::*'], config=config) # Add the auto-generated comment. curr = CGWrapper(curr, pre=f"{AUTOGENERATED_WARNING_COMMENT}{ALLOWED_WARNINGS}") @@ -8589,7 +8588,7 @@ class GlobalGenRoots(): ], "\n") return CGImports(code, descriptors=[], callbacks=[], dictionaries=[], enums=[], typedefs=[], imports=[ - 'crate::dom::bindings::codegen::Bindings', + 'crate::codegen::GenericBindings', 'crate::DomTypes', ], config=config) @@ -8616,7 +8615,7 @@ class GlobalGenRoots(): | set(leafModule(d) for d in config.getDictionaries())) curr = CGList([CGGeneric( "#[allow(clippy::derivable_impls)]\n" - f"pub(crate) mod {name};\n" + f"pub mod {name};\n" ) for name in sorted(descriptors)]) curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT) return curr @@ -8663,6 +8662,7 @@ class GlobalGenRoots(): if base in topTypes: typeIdCode.append(CGGeneric(f""" impl {base} {{ + #[allow(dead_code)] pub(crate) fn type_id(&self) -> &'static {base}TypeId {{ unsafe {{ &get_dom_class(self.reflector().get_jsobject().get()) @@ -8752,7 +8752,7 @@ impl Clone for TopTypeId { unions.add(name) generic = "<crate::DomTypeHolder>" if containsDomInterface(t) else "" cgthings += [CGGeneric(f"pub(crate) type {name} = " - f"crate::dom::bindings::codegen::GenericUnionTypes::{name}{generic};\n")] + f"script_bindings::codegen::GenericUnionTypes::{name}{generic};\n")] curr = CGList(cgthings) curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT) return curr diff --git a/components/script_bindings/codegen/Configuration.py b/components/script_bindings/codegen/Configuration.py index a4a6a538fdc..636a70197d2 100644 --- a/components/script_bindings/codegen/Configuration.py +++ b/components/script_bindings/codegen/Configuration.py @@ -228,7 +228,7 @@ class Descriptor(DescriptorProvider): self.nativeType = typeName pathDefault = 'crate::dom::types::%s' % typeName elif self.interface.isCallback(): - ty = 'crate::dom::bindings::codegen::GenericBindings::%sBinding::%s' % (ifaceName, ifaceName) + ty = 'crate::codegen::GenericBindings::%sBinding::%s' % (ifaceName, ifaceName) pathDefault = ty self.returnType = "Rc<%s<D>>" % ty self.argumentType = "???" @@ -238,7 +238,7 @@ class Descriptor(DescriptorProvider): self.argumentType = "&%s%s" % (prefix, typeName) self.nativeType = "*const %s%s" % (prefix, typeName) if self.interface.isIteratorInterface(): - pathDefault = 'crate::dom::bindings::iterable::IterableIterator' + pathDefault = 'crate::iterable::IterableIterator' else: pathDefault = 'crate::dom::types::%s' % MakeNativeName(typeName) @@ -491,7 +491,7 @@ def getIdlFileName(object): def getModuleFromObject(object): - return ('crate::dom::bindings::codegen::GenericBindings::' + getIdlFileName(object) + 'Binding') + return ('crate::codegen::GenericBindings::' + getIdlFileName(object) + 'Binding') def getTypesFromDescriptor(descriptor): @@ -552,5 +552,5 @@ def iteratorNativeType(descriptor, infer=False): res = "IterableIterator%s" % ("" if infer else '<D, D::%s>' % descriptor.interface.identifier.name) # todo: this hack is telling us that something is still wrong in codegen if iterableDecl.isSetlike() or iterableDecl.isMaplike(): - res = f"crate::dom::bindings::iterable::{res}" + res = f"crate::iterable::{res}" return res diff --git a/components/script_bindings/constructor.rs b/components/script_bindings/constructor.rs new file mode 100644 index 00000000000..0fbf1e5a45b --- /dev/null +++ b/components/script_bindings/constructor.rs @@ -0,0 +1,37 @@ +/* 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::ptr; + +use js::jsapi::{CallArgs, JSObject}; +use js::rust::HandleObject; + +use crate::codegen::PrototypeList; +use crate::error::throw_constructor_without_new; +use crate::interface::get_desired_proto; +use crate::script_runtime::JSContext; +use crate::utils::ProtoOrIfaceArray; + +pub(crate) unsafe fn call_default_constructor<D: crate::DomTypes>( + cx: JSContext, + args: &CallArgs, + global: &D::GlobalScope, + proto_id: PrototypeList::ID, + ctor_name: &str, + creator: unsafe fn(JSContext, HandleObject, *mut ProtoOrIfaceArray), + constructor: impl FnOnce(JSContext, &CallArgs, &D::GlobalScope, HandleObject) -> bool, +) -> bool { + if !args.is_constructing() { + throw_constructor_without_new(cx, ctor_name); + return false; + } + + rooted!(in(*cx) let mut desired_proto = ptr::null_mut::<JSObject>()); + let proto_result = get_desired_proto(cx, args, proto_id, creator, desired_proto.handle_mut()); + if proto_result.is_err() { + return false; + } + + constructor(cx, args, global, desired_proto.handle()) +} diff --git a/components/script_bindings/conversions.rs b/components/script_bindings/conversions.rs index 847f83fc606..a45da5b4cc4 100644 --- a/components/script_bindings/conversions.rs +++ b/components/script_bindings/conversions.rs @@ -13,21 +13,24 @@ use js::glue::{ JS_GetReservedSlot, UnwrapObjectDynamic, }; use js::jsapi::{ - JS_DeprecatedStringHasLatin1Chars, JS_GetLatin1StringCharsAndLength, + Heap, IsWindowProxy, JS_DeprecatedStringHasLatin1Chars, JS_GetLatin1StringCharsAndLength, JS_GetTwoByteStringCharsAndLength, JS_NewStringCopyN, JSContext, JSObject, JSString, }; use js::jsval::{ObjectValue, StringValue, UndefinedValue}; +use js::rust::wrappers::IsArrayObject; use js::rust::{ HandleId, HandleValue, MutableHandleValue, ToString, get_object_class, is_dom_class, is_dom_object, maybe_wrap_value, }; use num_traits::Float; +use crate::JSTraceable; use crate::inheritance::Castable; use crate::num::Finite; use crate::reflector::{DomObject, Reflector}; use crate::root::DomRoot; use crate::str::{ByteString, DOMString, USVString}; +use crate::trace::RootedTraceableBox; use crate::utils::{DOMClass, DOMJSClass}; /// A trait to check whether a given `JSObject` implements an IDL interface. @@ -494,3 +497,88 @@ where } native_from_object(v.get().to_object(), cx) } + +impl<T: ToJSValConvertible + JSTraceable> ToJSValConvertible for RootedTraceableBox<T> { + #[inline] + unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { + let value = &**self; + value.to_jsval(cx, rval); + } +} + +impl<T> FromJSValConvertible for RootedTraceableBox<Heap<T>> +where + T: FromJSValConvertible + js::rust::GCMethods + Copy, + Heap<T>: JSTraceable + Default, +{ + type Config = T::Config; + + unsafe fn from_jsval( + cx: *mut JSContext, + value: HandleValue, + config: Self::Config, + ) -> Result<ConversionResult<Self>, ()> { + T::from_jsval(cx, value, config).map(|result| match result { + ConversionResult::Success(inner) => { + ConversionResult::Success(RootedTraceableBox::from_box(Heap::boxed(inner))) + }, + ConversionResult::Failure(msg) => ConversionResult::Failure(msg), + }) + } +} + +/// Returns whether `value` is an array-like object (Array, FileList, +/// HTMLCollection, HTMLFormControlsCollection, HTMLOptionsCollection, +/// NodeList). +/// +/// # Safety +/// `cx` must point to a valid, non-null JSContext. +pub unsafe fn is_array_like<D: crate::DomTypes>(cx: *mut JSContext, value: HandleValue) -> bool { + let mut is_array = false; + assert!(IsArrayObject(cx, value, &mut is_array)); + if is_array { + return true; + } + + let object: *mut JSObject = match FromJSValConvertible::from_jsval(cx, value, ()).unwrap() { + ConversionResult::Success(object) => object, + _ => return false, + }; + + if root_from_object::<D::FileList>(object, cx).is_ok() { + return true; + } + if root_from_object::<D::HTMLCollection>(object, cx).is_ok() { + return true; + } + if root_from_object::<D::HTMLFormControlsCollection>(object, cx).is_ok() { + return true; + } + if root_from_object::<D::HTMLOptionsCollection>(object, cx).is_ok() { + return true; + } + if root_from_object::<D::NodeList>(object, cx).is_ok() { + return true; + } + + false +} + +/// Get a `DomRoot<T>` for a WindowProxy accessible from a `HandleValue`. +/// Caller is responsible for throwing a JS exception if needed in case of error. +pub(crate) unsafe fn windowproxy_from_handlevalue<D: crate::DomTypes>( + v: HandleValue, + _cx: *mut JSContext, +) -> Result<DomRoot<D::WindowProxy>, ()> { + if !v.get().is_object() { + return Err(()); + } + let object = v.get().to_object(); + if !IsWindowProxy(object) { + return Err(()); + } + let mut value = UndefinedValue(); + GetProxyReservedSlot(object, 0, &mut value); + let ptr = value.to_private() as *const D::WindowProxy; + Ok(DomRoot::from_ref(&*ptr)) +} diff --git a/components/script_bindings/finalize.rs b/components/script_bindings/finalize.rs index fa1079b5624..46fbc83c965 100644 --- a/components/script_bindings/finalize.rs +++ b/components/script_bindings/finalize.rs @@ -30,7 +30,7 @@ unsafe fn do_finalize_global(obj: *mut JSObject) { /// # Safety /// `this` must point to a valid, non-null instance of T. -pub unsafe fn finalize_common<T>(this: *const T) { +pub(crate) unsafe fn finalize_common<T>(this: *const T) { if !this.is_null() { // The pointer can be null if the object is the unforgeable holder of that interface. let _ = Box::from_raw(this as *mut T); @@ -41,7 +41,7 @@ pub unsafe fn finalize_common<T>(this: *const T) { /// # Safety /// `obj` must point to a valid, non-null JS object. /// `this` must point to a valid, non-null instance of T. -pub unsafe fn finalize_global<T>(obj: *mut JSObject, this: *const T) { +pub(crate) unsafe fn finalize_global<T>(obj: *mut JSObject, this: *const T) { do_finalize_global(obj); finalize_common::<T>(this); } @@ -49,7 +49,7 @@ pub unsafe fn finalize_global<T>(obj: *mut JSObject, this: *const T) { /// # Safety /// `obj` must point to a valid, non-null JS object. /// `this` must point to a valid, non-null instance of T. -pub unsafe fn finalize_weak_referenceable<T: WeakReferenceable>( +pub(crate) unsafe fn finalize_weak_referenceable<T: WeakReferenceable>( obj: *mut JSObject, this: *const T, ) { diff --git a/components/script/dom/bindings/guard.rs b/components/script_bindings/guard.rs index 16b5187abef..68286b48fec 100644 --- a/components/script/dom/bindings/guard.rs +++ b/components/script_bindings/guard.rs @@ -5,12 +5,12 @@ //! Machinery to conditionally expose things. use js::rust::HandleObject; -use script_bindings::codegen::Globals::Globals; use servo_config::prefs::get; use crate::DomTypes; -use crate::dom::bindings::interface::is_exposed_in; -use crate::dom::globalscope::GlobalScopeHelpers; +use crate::codegen::Globals::Globals; +use crate::interface::is_exposed_in; +use crate::interfaces::GlobalScopeHelpers; use crate::realms::{AlreadyInRealm, InRealm}; use crate::script_runtime::JSContext; diff --git a/components/script_bindings/import.rs b/components/script_bindings/import.rs new file mode 100644 index 00000000000..65e9ee30e1d --- /dev/null +++ b/components/script_bindings/import.rs @@ -0,0 +1,137 @@ +/* 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/. */ + +pub(crate) mod base { + pub(crate) use std::ptr; + pub(crate) use std::rc::Rc; + + pub(crate) use js::conversions::{ + ConversionBehavior, ConversionResult, FromJSValConvertible, ToJSValConvertible, + }; + pub(crate) use js::error::throw_type_error; + pub(crate) use js::jsapi::{ + CurrentGlobalOrNull, HandleValue as RawHandleValue, HandleValueArray, Heap, IsCallable, + JS_NewObject, JSContext, JSObject, + }; + pub(crate) use js::jsval::{JSVal, NullValue, ObjectOrNullValue, ObjectValue, UndefinedValue}; + pub(crate) use js::panic::maybe_resume_unwind; + pub(crate) use js::rust::wrappers::{Call, JS_WrapValue}; + pub(crate) use js::rust::{HandleObject, HandleValue, MutableHandleObject, MutableHandleValue}; + + pub(crate) use crate::callback::{ + CallSetup, CallbackContainer, CallbackFunction, CallbackInterface, CallbackObject, + ExceptionHandling, ThisReflector, wrap_call_this_value, + }; + pub(crate) use crate::codegen::DomTypes::DomTypes; + pub(crate) use crate::codegen::GenericUnionTypes; + pub(crate) use crate::conversions::{StringificationBehavior, root_from_handlevalue}; + pub(crate) use crate::error::Error::JSFailed; + pub(crate) use crate::error::Fallible; + pub(crate) use crate::interfaces::*; + pub(crate) use crate::lock::ThreadUnsafeOnceLock; + pub(crate) use crate::num::Finite; + pub(crate) use crate::proxyhandler::CrossOriginProperties; + pub(crate) use crate::reflector::{DomGlobalGeneric, DomObject}; + pub(crate) use crate::root::DomRoot; + pub(crate) use crate::script_runtime::{CanGc, JSContext as SafeJSContext}; + pub(crate) use crate::str::{ByteString, DOMString, USVString}; + pub(crate) use crate::trace::RootedTraceableBox; + pub(crate) use crate::utils::{get_dictionary_property, set_dictionary_property}; +} + +pub(crate) mod module { + pub(crate) use std::cmp; + pub(crate) use std::ffi::CString; + pub(crate) use std::ptr::NonNull; + + pub(crate) use js::conversions::ToJSValConvertible; + pub(crate) use js::glue::{ + CreateProxyHandler, GetProxyReservedSlot, JS_GetReservedSlot, ProxyTraps, + SetProxyReservedSlot, + }; + pub(crate) use js::jsapi::{ + __BindgenBitfieldUnit, CallArgs, GCContext, GetRealmErrorPrototype, + GetRealmFunctionPrototype, GetRealmIteratorPrototype, GetRealmObjectPrototype, + GetWellKnownSymbol, Handle as RawHandle, HandleId as RawHandleId, + HandleObject as RawHandleObject, JS_AtomizeAndPinString, JS_ForwardGetPropertyTo, + JS_GetPropertyDescriptorById, JS_HasPropertyById, JS_NewPlainObject, JS_SetReservedSlot, + JSAutoRealm, JSCLASS_FOREGROUND_FINALIZE, JSCLASS_RESERVED_SLOTS_SHIFT, JSClass, + JSClassOps, JSFunctionSpec, JSITER_HIDDEN, JSITER_OWNONLY, JSITER_SYMBOLS, + JSJitGetterCallArgs, JSJitInfo, JSJitInfo__bindgen_ty_1, JSJitInfo__bindgen_ty_2, + JSJitInfo__bindgen_ty_3, JSJitInfo_AliasSet, JSJitInfo_ArgType, JSJitInfo_OpType, + JSJitMethodCallArgs, JSJitSetterCallArgs, JSNativeWrapper, JSPROP_ENUMERATE, + JSPROP_PERMANENT, JSPROP_READONLY, JSPropertySpec, JSPropertySpec_Accessor, + JSPropertySpec_AccessorsOrValue, JSPropertySpec_AccessorsOrValue_Accessors, + JSPropertySpec_Kind, JSPropertySpec_Name, JSPropertySpec_ValueWrapper, + JSPropertySpec_ValueWrapper__bindgen_ty_1, JSPropertySpec_ValueWrapper_Type, JSTracer, + JSTypedMethodJitInfo, JSValueType, MutableHandle as RawMutableHandle, + MutableHandleIdVector as RawMutableHandleIdVector, + MutableHandleObject as RawMutableHandleObject, MutableHandleValue as RawMutableHandleValue, + ObjectOpResult, PropertyDescriptor, SymbolCode, UndefinedHandleValue, jsid, + }; + pub(crate) use js::jsval::PrivateValue; + pub(crate) use js::panic::wrap_panic; + pub(crate) use js::rust::wrappers::{ + AppendToIdVector, Call, GetPropertyKeys, JS_CopyOwnPropertiesAndPrivateFields, + JS_DefineProperty, JS_DefinePropertyById2, JS_GetProperty, + JS_InitializePropertiesFromCompatibleNativeObject, JS_NewObjectWithGivenProto, + JS_NewObjectWithoutMetadata, JS_SetImmutablePrototype, JS_SetProperty, JS_SetPrototype, + JS_WrapObject, NewProxyObject, RUST_INTERNED_STRING_TO_JSID, RUST_SYMBOL_TO_JSID, + int_to_jsid, + }; + pub(crate) use js::rust::{ + CustomAutoRooterGuard, GCMethods, Handle, MutableHandle, get_context_realm, + get_object_class, get_object_realm, + }; + pub(crate) use js::typedarray::{ + ArrayBuffer, ArrayBufferView, Float32Array, Float64Array, Uint8Array, Uint8ClampedArray, + }; + pub(crate) use js::{ + JS_CALLEE, JSCLASS_GLOBAL_SLOT_COUNT, JSCLASS_IS_DOMJSCLASS, JSCLASS_IS_GLOBAL, + JSCLASS_RESERVED_SLOTS_MASK, typedarray, + }; + pub(crate) use servo_config::pref; + + pub(crate) use super::base::*; + pub(crate) use crate::codegen::Globals::Globals; + pub(crate) use crate::codegen::{PrototypeList, RegisterBindings}; + pub(crate) use crate::constant::{ConstantSpec, ConstantVal}; + pub(crate) use crate::constructor::call_default_constructor; + pub(crate) use crate::conversions::{ + DOM_OBJECT_SLOT, StringificationBehavior, is_array_like, jsid_to_string, + native_from_handlevalue, native_from_object_static, + }; + pub(crate) use crate::error::{Error, ErrorResult}; + pub(crate) use crate::finalize::{ + finalize_common, finalize_global, finalize_weak_referenceable, + }; + pub(crate) use crate::guard::{Condition, Guard}; + pub(crate) use crate::inheritance::Castable; + pub(crate) use crate::interface::{ + ConstructorClassHook, InterfaceConstructorBehavior, NonCallbackInterfaceObjectClass, + ProtoOrIfaceIndex, create_callback_interface_object, create_global_object, + create_interface_prototype_object, create_named_constructors, + create_noncallback_interface_object, define_dom_interface, define_guarded_methods, + define_guarded_properties, get_per_interface_object_handle, is_exposed_in, + }; + pub(crate) use crate::iterable::{Iterable, IterableIterator, IteratorType}; + pub(crate) use crate::like::{Maplike, Setlike}; + pub(crate) use crate::mem::malloc_size_of_including_raw_self; + pub(crate) use crate::namespace::{NamespaceObjectClass, create_namespace_object}; + pub(crate) use crate::proxyhandler::{ + ensure_expando_object, get_expando_object, set_property_descriptor, + }; + pub(crate) use crate::realms::{AlreadyInRealm, InRealm}; + pub(crate) use crate::root::{Dom, DomSlice, MaybeUnreflectedDom, Root}; + pub(crate) use crate::script_runtime::CanGc; + pub(crate) use crate::utils::{ + AsVoidPtr, DOM_PROTO_UNFORGEABLE_HOLDER_SLOT, DOMClass, DOMJSClass, JSCLASS_DOM_GLOBAL, + ProtoOrIfaceArray, enumerate_global, exception_to_promise, generic_getter, + generic_lenient_getter, generic_lenient_setter, generic_method, generic_setter, + generic_static_promise_method, get_array_index_from_id, get_property_on_prototype, + has_property_on_prototype, resolve_global, trace_global, + }; + pub(crate) use crate::weakref::DOM_WEAK_SLOT; + pub(crate) use crate::{JSTraceable, proxyhandler}; +} diff --git a/components/script/dom/bindings/interface.rs b/components/script_bindings/interface.rs index d1c191be3a4..08ee0a4f420 100644 --- a/components/script/dom/bindings/interface.rs +++ b/components/script_bindings/interface.rs @@ -32,19 +32,19 @@ use js::rust::{ HandleObject, HandleValue, MutableHandleObject, RealmOptions, define_methods, define_properties, get_object_class, is_dom_class, maybe_wrap_object, }; -use script_bindings::constant::{ConstantSpec, define_constants}; use servo_url::MutableOrigin; use crate::DomTypes; -use crate::dom::bindings::codegen::InterfaceObjectMap::Globals; -use crate::dom::bindings::codegen::PrototypeList; -use crate::dom::bindings::conversions::{DOM_OBJECT_SLOT, get_dom_class}; -use crate::dom::bindings::guard::Guard; -use crate::dom::bindings::principals::ServoJSPrincipals; -use crate::dom::bindings::utils::{ +use crate::codegen::Globals::Globals; +use crate::codegen::PrototypeList; +use crate::constant::{ConstantSpec, define_constants}; +use crate::conversions::{DOM_OBJECT_SLOT, get_dom_class}; +use crate::guard::Guard; +use crate::principals::ServoJSPrincipals; +use crate::script_runtime::JSContext as SafeJSContext; +use crate::utils::{ DOM_PROTOTYPE_SLOT, DOMJSClass, JSCLASS_DOM_GLOBAL, ProtoOrIfaceArray, get_proto_or_iface_array, }; -use crate::script_runtime::JSContext as SafeJSContext; /// The class of a non-callback interface object. #[derive(Clone, Copy)] @@ -599,7 +599,8 @@ fn get_proto_id_for_new_target(new_target: HandleObject) -> Option<PrototypeList } } -pub(crate) fn get_desired_proto( +#[allow(clippy::result_unit_err)] +pub fn get_desired_proto( cx: SafeJSContext, args: &CallArgs, proto_id: PrototypeList::ID, diff --git a/components/script_bindings/interfaces.rs b/components/script_bindings/interfaces.rs index ce8fc71f23b..12b95b8eb4c 100644 --- a/components/script_bindings/interfaces.rs +++ b/components/script_bindings/interfaces.rs @@ -2,14 +2,103 @@ * 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::RefCell; +use std::rc::Rc; +use std::thread::LocalKey; + +use js::conversions::ToJSValConvertible; +use js::glue::JSPrincipalsCallbacks; +use js::jsapi::{CallArgs, HandleObject as RawHandleObject, JSContext as RawJSContext, JSObject}; use js::rust::{HandleObject, MutableHandleObject}; +use servo_url::{MutableOrigin, ServoUrl}; + +use crate::DomTypes; +use crate::codegen::PrototypeList; +use crate::conversions::DerivedFrom; +use crate::error::Error; +use crate::realms::InRealm; +use crate::reflector::{DomObject, DomObjectWrap}; +use crate::root::DomRoot; +use crate::script_runtime::{CanGc, JSContext}; +use crate::settings_stack::StackEntry; +use crate::utils::ProtoOrIfaceArray; + +/// Operations that must be invoked from the generated bindings. +pub trait DomHelpers<D: DomTypes> { + fn throw_dom_exception(cx: JSContext, global: &D::GlobalScope, result: Error, can_gc: CanGc); + + fn call_html_constructor<T: DerivedFrom<D::Element> + DomObject>( + cx: JSContext, + args: &CallArgs, + global: &D::GlobalScope, + proto_id: PrototypeList::ID, + creator: unsafe fn(JSContext, HandleObject, *mut ProtoOrIfaceArray), + can_gc: CanGc, + ) -> bool; + + fn settings_stack() -> &'static LocalKey<RefCell<Vec<StackEntry<D>>>>; -use crate::script_runtime::JSContext; + fn principals_callbacks() -> &'static JSPrincipalsCallbacks; + + fn is_platform_object_same_origin(cx: JSContext, obj: RawHandleObject) -> bool; + + fn interface_map() -> &'static phf::Map<&'static [u8], for<'a> fn(JSContext, HandleObject)>; + + fn push_new_element_queue(); + fn pop_current_element_queue(can_gc: CanGc); + + fn reflect_dom_object<T, U>(obj: Box<T>, global: &U, can_gc: CanGc) -> DomRoot<T> + where + T: DomObject + DomObjectWrap<D>, + U: DerivedFrom<D::GlobalScope>; + + fn report_pending_exception(cx: JSContext, dispatch_event: bool, realm: InRealm, can_gc: CanGc); +} + +/// Operations that must be invoked from the generated bindings. +#[allow(unsafe_code)] +pub trait GlobalScopeHelpers<D: DomTypes> { + /// # Safety + /// `cx` must point to a valid, non-null RawJSContext. + unsafe fn from_context(cx: *mut RawJSContext, realm: InRealm) -> DomRoot<D::GlobalScope>; + fn get_cx() -> JSContext; + /// # Safety + /// `obj` must point to a valid, non-null JSObject. + unsafe fn from_object(obj: *mut JSObject) -> DomRoot<D::GlobalScope>; + fn from_reflector(reflector: &impl DomObject, realm: InRealm) -> DomRoot<D::GlobalScope>; + + /// # Safety + /// `obj` must point to a valid, non-null JSObject. + /// `cx` must point to a valid, non-null RawJSContext. + unsafe fn from_object_maybe_wrapped( + obj: *mut JSObject, + cx: *mut RawJSContext, + ) -> DomRoot<D::GlobalScope>; + + fn origin(&self) -> &MutableOrigin; + + fn incumbent() -> Option<DomRoot<D::GlobalScope>>; + + fn perform_a_microtask_checkpoint(&self, can_gc: CanGc); + + fn get_url(&self) -> ServoUrl; + + fn is_secure_context(&self) -> bool; +} pub trait DocumentHelpers { fn ensure_safe_to_run_script_or_layout(&self); } +/// Operations that must be invoked from the generated bindings. +pub trait PromiseHelpers<D: crate::DomTypes> { + fn new_resolved( + global: &D::GlobalScope, + cx: JSContext, + value: impl ToJSValConvertible, + ) -> Rc<D::Promise>; +} + pub trait ServoInternalsHelpers { fn is_servo_internal(cx: JSContext, global: HandleObject) -> bool; } diff --git a/components/script_bindings/iterable.rs b/components/script_bindings/iterable.rs index 61cd4540bae..03efd8a4856 100644 --- a/components/script_bindings/iterable.rs +++ b/components/script_bindings/iterable.rs @@ -2,13 +2,36 @@ * 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/. */ +//! Implementation of `iterable<...>` and `iterable<..., ...>` WebIDL declarations. + +use std::cell::Cell; +use std::marker::PhantomData; +use std::ptr; +use std::ptr::NonNull; + +use dom_struct::dom_struct; use js::conversions::ToJSValConvertible; +use js::jsapi::{Heap, JSObject}; +use js::jsval::UndefinedValue; +use js::rust::{HandleObject, HandleValue, MutableHandleObject}; +use crate::codegen::GenericBindings::IterableIteratorBinding::{ + IterableKeyAndValueResult, IterableKeyOrValueResult, +}; +use crate::conversions::IDLInterface; +use crate::error::Fallible; +use crate::interfaces::DomHelpers; +use crate::realms::InRealm; +use crate::reflector::{DomGlobalGeneric, DomObjectIteratorWrap, DomObjectWrap, Reflector}; +use crate::root::{Dom, DomRoot, Root}; +use crate::script_runtime::{CanGc, JSContext}; +use crate::trace::{NoTrace, RootedTraceableBox}; use crate::utils::DOMClass; +use crate::{DomTypes, JSTraceable}; /// The values that an iterator will iterate over. #[derive(JSTraceable, MallocSizeOf)] -pub enum IteratorType { +pub(crate) enum IteratorType { /// The keys of the iterable object. Keys, /// The values of the iterable object. @@ -38,3 +61,148 @@ pub trait Iterable { pub trait IteratorDerives { fn derives(class: &'static DOMClass) -> bool; } + +/// An iterator over the iterable entries of a given DOM interface. +#[dom_struct] +pub struct IterableIterator< + D: DomTypes, + T: DomObjectIteratorWrap<D> + JSTraceable + Iterable + DomGlobalGeneric<D>, +> { + reflector: Reflector, + iterable: Dom<T>, + type_: IteratorType, + index: Cell<u32>, + _marker: NoTrace<PhantomData<D>>, +} + +impl<D: DomTypes, T: DomObjectIteratorWrap<D> + JSTraceable + Iterable> IterableIterator<D, T> { + pub fn global_(&self, realm: InRealm) -> DomRoot<D::GlobalScope> { + <Self as DomGlobalGeneric<D>>::global_(self, realm) + } +} + +impl< + D: DomTypes, + T: DomObjectIteratorWrap<D> + + JSTraceable + + Iterable + + DomGlobalGeneric<D> + + IDLInterface + + IteratorDerives, +> IDLInterface for IterableIterator<D, T> +{ + fn derives(class: &'static DOMClass) -> bool { + <T as IteratorDerives>::derives(class) + } +} + +impl<D: DomTypes, T: DomObjectIteratorWrap<D> + JSTraceable + Iterable + DomGlobalGeneric<D>> + IterableIterator<D, T> +{ + /// Create a new iterator instance for the provided iterable DOM interface. + pub(crate) fn new(iterable: &T, type_: IteratorType, realm: InRealm) -> DomRoot<Self> { + let iterator = Box::new(IterableIterator { + reflector: Reflector::new(), + type_, + iterable: Dom::from_ref(iterable), + index: Cell::new(0), + _marker: NoTrace(PhantomData), + }); + <D as DomHelpers<D>>::reflect_dom_object(iterator, &*iterable.global_(realm), CanGc::note()) + } + + /// Return the next value from the iterable object. + #[allow(non_snake_case)] + pub fn Next(&self, cx: JSContext) -> Fallible<NonNull<JSObject>> { + let index = self.index.get(); + rooted!(in(*cx) let mut value = UndefinedValue()); + rooted!(in(*cx) let mut rval = ptr::null_mut::<JSObject>()); + let result = if index >= self.iterable.get_iterable_length() { + dict_return(cx, rval.handle_mut(), true, value.handle()) + } else { + match self.type_ { + IteratorType::Keys => { + unsafe { + self.iterable + .get_key_at_index(index) + .to_jsval(*cx, value.handle_mut()); + } + dict_return(cx, rval.handle_mut(), false, value.handle()) + }, + IteratorType::Values => { + unsafe { + self.iterable + .get_value_at_index(index) + .to_jsval(*cx, value.handle_mut()); + } + dict_return(cx, rval.handle_mut(), false, value.handle()) + }, + IteratorType::Entries => { + rooted!(in(*cx) let mut key = UndefinedValue()); + unsafe { + self.iterable + .get_key_at_index(index) + .to_jsval(*cx, key.handle_mut()); + self.iterable + .get_value_at_index(index) + .to_jsval(*cx, value.handle_mut()); + } + key_and_value_return(cx, rval.handle_mut(), key.handle(), value.handle()) + }, + } + }; + self.index.set(index + 1); + result.map(|_| NonNull::new(rval.get()).expect("got a null pointer")) + } +} + +impl<D: DomTypes, T: DomObjectIteratorWrap<D> + JSTraceable + Iterable + DomGlobalGeneric<D>> + DomObjectWrap<D> for IterableIterator<D, T> +{ + const WRAP: unsafe fn( + JSContext, + &D::GlobalScope, + Option<HandleObject>, + Box<Self>, + CanGc, + ) -> Root<Dom<Self>> = T::ITER_WRAP; +} + +fn dict_return( + cx: JSContext, + mut result: MutableHandleObject, + done: bool, + value: HandleValue, +) -> Fallible<()> { + let mut dict = IterableKeyOrValueResult::empty(); + dict.done = done; + dict.value.set(value.get()); + rooted!(in(*cx) let mut dict_value = UndefinedValue()); + unsafe { + dict.to_jsval(*cx, dict_value.handle_mut()); + } + result.set(dict_value.to_object()); + Ok(()) +} + +fn key_and_value_return( + cx: JSContext, + mut result: MutableHandleObject, + key: HandleValue, + value: HandleValue, +) -> Fallible<()> { + let mut dict = IterableKeyAndValueResult::empty(); + dict.done = false; + dict.value = Some( + vec![key, value] + .into_iter() + .map(|handle| RootedTraceableBox::from_box(Heap::boxed(handle.get()))) + .collect(), + ); + rooted!(in(*cx) let mut dict_value = UndefinedValue()); + unsafe { + dict.to_jsval(*cx, dict_value.handle_mut()); + } + result.set(dict_value.to_object()); + Ok(()) +} diff --git a/components/script_bindings/lib.rs b/components/script_bindings/lib.rs index 0ecad1ca07c..83dc9fbd1c8 100644 --- a/components/script_bindings/lib.rs +++ b/components/script_bindings/lib.rs @@ -11,6 +11,8 @@ #![deny(crown_is_not_used)] #[macro_use] +extern crate js; +#[macro_use] extern crate jstraceable_derive; #[macro_use] extern crate log; @@ -18,21 +20,30 @@ extern crate log; extern crate malloc_size_of_derive; pub mod callback; -pub mod constant; +mod constant; +mod constructor; pub mod conversions; pub mod error; -pub mod finalize; +mod finalize; +mod guard; +mod import; pub mod inheritance; +pub mod interface; pub mod interfaces; pub mod iterable; pub mod like; -pub mod lock; +mod lock; +mod mem; +mod namespace; pub mod num; +pub mod principals; pub mod proxyhandler; +pub mod realms; pub mod record; pub mod reflector; pub mod root; pub mod script_runtime; +pub mod settings_stack; pub mod str; pub mod trace; pub mod utils; @@ -51,6 +62,32 @@ pub mod codegen { pub mod PrototypeList { include!(concat!(env!("OUT_DIR"), "/PrototypeList.rs")); } + pub(crate) mod DomTypes { + include!(concat!(env!("OUT_DIR"), "/DomTypes.rs")); + } + #[allow( + dead_code, + clippy::extra_unused_type_parameters, + clippy::missing_safety_doc, + clippy::result_unit_err + )] + pub mod GenericBindings { + include!(concat!(env!("OUT_DIR"), "/Bindings/mod.rs")); + } + #[allow( + non_camel_case_types, + unused_imports, + unused_variables, + clippy::large_enum_variant, + clippy::upper_case_acronyms, + clippy::enum_variant_names + )] + pub mod GenericUnionTypes { + include!(concat!(env!("OUT_DIR"), "/GenericUnionTypes.rs")); + } + pub mod RegisterBindings { + include!(concat!(env!("OUT_DIR"), "/RegisterBindings.rs")); + } } // These trait exports are public, because they are used in the DOM bindings. @@ -58,4 +95,6 @@ pub mod codegen { // it is useful that they are accessible at the root of the crate. pub(crate) use js::gc::Traceable as JSTraceable; +pub use crate::codegen::DomTypes::DomTypes; +pub(crate) use crate::reflector::{DomObject, MutDomObject, Reflector}; pub(crate) use crate::trace::CustomTraceable; diff --git a/components/script/mem.rs b/components/script_bindings/mem.rs index 52a6e724b22..52a6e724b22 100644 --- a/components/script/mem.rs +++ b/components/script_bindings/mem.rs diff --git a/components/script/dom/bindings/namespace.rs b/components/script_bindings/namespace.rs index ad0a5801519..1ed5e06915b 100644 --- a/components/script/dom/bindings/namespace.rs +++ b/components/script_bindings/namespace.rs @@ -9,11 +9,11 @@ use std::ptr; use js::jsapi::{JSClass, JSFunctionSpec}; use js::rust::{HandleObject, MutableHandleObject}; -use script_bindings::constant::ConstantSpec; use crate::DomTypes; -use crate::dom::bindings::guard::Guard; -use crate::dom::bindings::interface::{create_object, define_on_global_object}; +use crate::constant::ConstantSpec; +use crate::guard::Guard; +use crate::interface::{create_object, define_on_global_object}; use crate::script_runtime::JSContext; /// The class of a namespace object. diff --git a/components/script_bindings/principals.rs b/components/script_bindings/principals.rs new file mode 100644 index 00000000000..6b30f71fb7e --- /dev/null +++ b/components/script_bindings/principals.rs @@ -0,0 +1,128 @@ +/* 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::marker::PhantomData; +use std::mem::ManuallyDrop; +use std::ops::Deref; +use std::ptr::NonNull; + +use js::glue::{CreateRustJSPrincipals, GetRustJSPrincipalsPrivate}; +use js::jsapi::{JS_DropPrincipals, JS_HoldPrincipals, JSPrincipals}; +use js::rust::Runtime; +use servo_url::MutableOrigin; + +use crate::DomTypes; +use crate::interfaces::DomHelpers; + +/// An owned reference to Servo's `JSPrincipals` instance. +#[repr(transparent)] +pub struct ServoJSPrincipals(NonNull<JSPrincipals>); + +impl ServoJSPrincipals { + pub fn new<D: DomTypes>(origin: &MutableOrigin) -> Self { + unsafe { + let private: Box<MutableOrigin> = Box::new(origin.clone()); + let raw = CreateRustJSPrincipals( + <D as DomHelpers<D>>::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)) + } + } + + /// Construct `Self` from a raw `*mut JSPrincipals`, incrementing its + /// reference count. + /// + /// # Safety + /// `raw` must point to a valid JSPrincipals value. + #[inline] + pub unsafe fn from_raw_nonnull(raw: NonNull<JSPrincipals>) -> Self { + JS_HoldPrincipals(raw.as_ptr()); + Self(raw) + } + + #[inline] + pub fn origin(&self) -> MutableOrigin { + unsafe { + let origin = GetRustJSPrincipalsPrivate(self.0.as_ptr()) as *mut MutableOrigin; + (*origin).clone() + } + } + + #[inline] + pub fn as_raw_nonnull(&self) -> NonNull<JSPrincipals> { + self.0 + } + + #[inline] + pub fn as_raw(&self) -> *mut JSPrincipals { + self.0.as_ptr() + } +} + +impl Clone for ServoJSPrincipals { + #[inline] + fn clone(&self) -> Self { + unsafe { Self::from_raw_nonnull(self.as_raw_nonnull()) } + } +} + +impl Drop for ServoJSPrincipals { + #[inline] + fn drop(&mut self) { + if let Some(cx) = Runtime::get() { + unsafe { JS_DropPrincipals(cx.as_ptr(), self.as_raw()) }; + } + } +} + +/// A borrowed reference to Servo's `JSPrincipals` instance. Does not update the +/// reference count on creation and deletion. +pub struct ServoJSPrincipalsRef<'a>(ManuallyDrop<ServoJSPrincipals>, PhantomData<&'a ()>); + +impl ServoJSPrincipalsRef<'_> { + /// Construct `Self` from a raw `NonNull<JSPrincipals>`. + /// + /// # Safety + /// + /// `ServoJSPrincipalsRef` does not update the reference count of the + /// wrapped `JSPrincipals` object. It's up to the caller to ensure the + /// returned `ServoJSPrincipalsRef` object or any clones are not used past + /// the lifetime of the wrapped object. + #[inline] + pub unsafe fn from_raw_nonnull(raw: NonNull<JSPrincipals>) -> Self { + // Don't use `ServoJSPrincipals::from_raw_nonnull`; we don't want to + // update the reference count + Self(ManuallyDrop::new(ServoJSPrincipals(raw)), PhantomData) + } + + /// Construct `Self` from a raw `*mut JSPrincipals`. + /// + /// # Safety + /// + /// The behavior is undefined if `raw` is null. See also + /// [`Self::from_raw_nonnull`]. + #[inline] + pub unsafe fn from_raw_unchecked(raw: *mut JSPrincipals) -> Self { + Self::from_raw_nonnull(NonNull::new_unchecked(raw)) + } +} + +impl Clone for ServoJSPrincipalsRef<'_> { + #[inline] + fn clone(&self) -> Self { + Self(ManuallyDrop::new(ServoJSPrincipals(self.0.0)), PhantomData) + } +} + +impl Deref for ServoJSPrincipalsRef<'_> { + type Target = ServoJSPrincipals; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.0 + } +} diff --git a/components/script_bindings/proxyhandler.rs b/components/script_bindings/proxyhandler.rs index 7614479ac59..e075e669b0a 100644 --- a/components/script_bindings/proxyhandler.rs +++ b/components/script_bindings/proxyhandler.rs @@ -8,15 +8,20 @@ use std::ffi::CStr; use std::os::raw::c_char; use std::ptr; -use js::glue::{GetProxyHandlerFamily, GetProxyPrivate, SetProxyPrivate}; +use js::conversions::ToJSValConvertible; +use js::glue::{ + GetProxyHandler, GetProxyHandlerFamily, GetProxyPrivate, InvokeGetOwnPropertyDescriptor, + 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, + 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, - MutableHandleObject as RawMutableHandleObject, ObjectOpResult, PropertyDescriptor, - SetDOMProxyInformation, SymbolCode, jsid, + MutableHandleObject as RawMutableHandleObject, MutableHandleValue as RawMutableHandleValue, + ObjectOpResult, PropertyDescriptor, SetDOMProxyInformation, SymbolCode, jsid, }; use js::jsid::SymbolId; use js::jsval::{ObjectValue, UndefinedValue}; @@ -27,8 +32,13 @@ use js::rust::wrappers::{ use js::rust::{Handle, HandleObject, HandleValue, MutableHandle, MutableHandleObject}; use js::{jsapi, rooted}; +use crate::DomTypes; use crate::conversions::{is_dom_proxy, jsid_to_string, jsstring_to_str}; -use crate::script_runtime::JSContext as SafeJSContext; +use crate::error::Error; +use crate::interfaces::{DomHelpers, GlobalScopeHelpers}; +use crate::realms::{AlreadyInRealm, InRealm}; +use crate::reflector::DomObject; +use crate::script_runtime::{CanGc, JSContext as SafeJSContext}; use crate::str::DOMString; use crate::utils::delete_property_by_id; @@ -36,7 +46,7 @@ use crate::utils::delete_property_by_id; /// /// # Safety /// `cx` must point to a valid, non-null JSContext. -pub unsafe extern "C" fn shadow_check_callback( +pub(crate) unsafe extern "C" fn shadow_check_callback( cx: *mut JSContext, object: RawHandleObject, id: RawHandleId, @@ -78,7 +88,7 @@ pub fn init() { /// # 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( +pub(crate) unsafe extern "C" fn define_property( cx: *mut JSContext, proxy: RawHandleObject, id: RawHandleId, @@ -95,7 +105,7 @@ pub unsafe extern "C" fn define_property( /// # 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( +pub(crate) unsafe extern "C" fn delete( cx: *mut JSContext, proxy: RawHandleObject, id: RawHandleId, @@ -115,7 +125,7 @@ pub unsafe extern "C" fn delete( /// /// # Safety /// `result` must point to a valid, non-null ObjectOpResult. -pub unsafe extern "C" fn prevent_extensions( +pub(crate) unsafe extern "C" fn prevent_extensions( _cx: *mut JSContext, _proxy: RawHandleObject, result: *mut ObjectOpResult, @@ -128,7 +138,7 @@ pub unsafe extern "C" fn prevent_extensions( /// /// # Safety /// `succeeded` must point to a valid, non-null bool. -pub unsafe extern "C" fn is_extensible( +pub(crate) unsafe extern "C" fn is_extensible( _cx: *mut JSContext, _proxy: RawHandleObject, succeeded: *mut bool, @@ -149,7 +159,7 @@ pub unsafe extern "C" fn is_extensible( /// /// # Safety /// `is_ordinary` must point to a valid, non-null bool. -pub unsafe extern "C" fn get_prototype_if_ordinary( +pub(crate) unsafe extern "C" fn get_prototype_if_ordinary( _: *mut JSContext, proxy: RawHandleObject, is_ordinary: *mut bool, @@ -161,7 +171,7 @@ pub unsafe extern "C" fn get_prototype_if_ordinary( } /// Get the expando object, or null if there is none. -pub fn get_expando_object(obj: RawHandleObject, mut expando: MutableHandleObject) { +pub(crate) fn get_expando_object(obj: RawHandleObject, mut expando: MutableHandleObject) { unsafe { assert!(is_dom_proxy(obj.get())); let val = &mut UndefinedValue(); @@ -179,7 +189,7 @@ pub fn get_expando_object(obj: RawHandleObject, mut expando: MutableHandleObject /// /// # Safety /// `cx` must point to a valid, non-null JSContext. -pub unsafe fn ensure_expando_object( +pub(crate) unsafe fn ensure_expando_object( cx: *mut JSContext, obj: RawHandleObject, mut expando: MutableHandleObject, @@ -212,7 +222,7 @@ pub fn set_property_descriptor( *is_none = false; } -pub fn id_to_source(cx: SafeJSContext, id: RawHandleId) -> Option<DOMString> { +pub(crate) fn id_to_source(cx: SafeJSContext, id: RawHandleId) -> Option<DOMString> { unsafe { rooted!(in(*cx) let mut value = UndefinedValue()); rooted!(in(*cx) let mut jsstr = ptr::null_mut::<jsapi::JSString>()); @@ -230,9 +240,9 @@ pub fn id_to_source(cx: SafeJSContext, id: RawHandleId) -> Option<DOMString> { /// [`CrossOriginProperties(O)`]. /// /// [`CrossOriginProperties(O)`]: https://html.spec.whatwg.org/multipage/#crossoriginproperties-(-o-) -pub struct CrossOriginProperties { - pub attributes: &'static [JSPropertySpec], - pub methods: &'static [JSFunctionSpec], +pub(crate) struct CrossOriginProperties { + pub(crate) attributes: &'static [JSPropertySpec], + pub(crate) methods: &'static [JSFunctionSpec], } impl CrossOriginProperties { @@ -250,7 +260,7 @@ impl CrossOriginProperties { /// Implementation of [`CrossOriginOwnPropertyKeys`]. /// /// [`CrossOriginOwnPropertyKeys`]: https://html.spec.whatwg.org/multipage/#crossoriginownpropertykeys-(-o-) -pub fn cross_origin_own_property_keys( +pub(crate) fn cross_origin_own_property_keys( cx: SafeJSContext, _proxy: RawHandleObject, cross_origin_properties: &'static CrossOriginProperties, @@ -276,7 +286,7 @@ pub fn cross_origin_own_property_keys( /// # Safety /// `is_ordinary` must point to a valid, non-null bool. -pub unsafe extern "C" fn maybe_cross_origin_get_prototype_if_ordinary_rawcx( +pub(crate) unsafe extern "C" fn maybe_cross_origin_get_prototype_if_ordinary_rawcx( _: *mut JSContext, _proxy: RawHandleObject, is_ordinary: *mut bool, @@ -294,7 +304,7 @@ pub unsafe extern "C" fn maybe_cross_origin_get_prototype_if_ordinary_rawcx( /// /// # Safety /// `result` must point to a valid, non-null ObjectOpResult. -pub unsafe extern "C" fn maybe_cross_origin_set_prototype_rawcx( +pub(crate) unsafe extern "C" fn maybe_cross_origin_set_prototype_rawcx( cx: *mut JSContext, proxy: RawHandleObject, proto: RawHandleObject, @@ -323,25 +333,25 @@ pub unsafe extern "C" fn maybe_cross_origin_set_prototype_rawcx( true } -pub fn get_getter_object(d: &PropertyDescriptor, out: RawMutableHandleObject) { +pub(crate) fn get_getter_object(d: &PropertyDescriptor, out: RawMutableHandleObject) { if d.hasGetter_() { out.set(d.getter_); } } -pub fn get_setter_object(d: &PropertyDescriptor, out: RawMutableHandleObject) { +pub(crate) fn get_setter_object(d: &PropertyDescriptor, out: RawMutableHandleObject) { if d.hasSetter_() { out.set(d.setter_); } } /// <https://tc39.es/ecma262/#sec-isaccessordescriptor> -pub fn is_accessor_descriptor(d: &PropertyDescriptor) -> bool { +pub(crate) fn is_accessor_descriptor(d: &PropertyDescriptor) -> bool { d.hasSetter_() || d.hasGetter_() } /// <https://tc39.es/ecma262/#sec-isdatadescriptor> -pub fn is_data_descriptor(d: &PropertyDescriptor) -> bool { +pub(crate) fn is_data_descriptor(d: &PropertyDescriptor) -> bool { d.hasWritable_() || d.hasValue_() } @@ -353,7 +363,7 @@ pub fn is_data_descriptor(d: &PropertyDescriptor) -> bool { /// /// # Safety /// `bp` must point to a valid, non-null bool. -pub unsafe fn cross_origin_has_own( +pub(crate) unsafe fn cross_origin_has_own( cx: SafeJSContext, _proxy: RawHandleObject, cross_origin_properties: &'static CrossOriginProperties, @@ -379,7 +389,7 @@ pub unsafe fn cross_origin_has_own( /// for a maybe-cross-origin object. /// /// [`CrossOriginGetOwnPropertyHelper`]: https://html.spec.whatwg.org/multipage/#crossorigingetownpropertyhelper-(-o,-p-) -pub fn cross_origin_get_own_property_helper( +pub(crate) fn cross_origin_get_own_property_helper( cx: SafeJSContext, proxy: RawHandleObject, cross_origin_properties: &'static CrossOriginProperties, @@ -405,7 +415,7 @@ const ALLOWLISTED_SYMBOL_CODES: &[SymbolCode] = &[ SymbolCode::isConcatSpreadable, ]; -pub fn is_cross_origin_allowlisted_prop(cx: SafeJSContext, id: RawHandleId) -> bool { +pub(crate) 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; @@ -492,3 +502,269 @@ fn ensure_cross_origin_property_holder( true } + +/// Report a cross-origin denial for a property, Always returns `false`, so it +/// can be used as `return report_cross_origin_denial(...);`. +/// +/// What this function does corresponds to the operations in +/// <https://html.spec.whatwg.org/multipage/#the-location-interface> denoted as +/// "Throw a `SecurityError` DOMException". +pub(crate) unsafe fn report_cross_origin_denial<D: DomTypes>( + cx: SafeJSContext, + id: RawHandleId, + access: &str, +) -> bool { + debug!( + "permission denied to {} property {} on cross-origin object", + access, + id_to_source(cx, id).as_deref().unwrap_or("< error >"), + ); + let in_realm_proof = AlreadyInRealm::assert_for_cx(cx); + if !JS_IsExceptionPending(*cx) { + let global = D::GlobalScope::from_context(*cx, InRealm::Already(&in_realm_proof)); + // TODO: include `id` and `access` in the exception message + <D as DomHelpers<D>>::throw_dom_exception(cx, &global, Error::Security, CanGc::note()); + } + false +} + +/// 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<D: DomTypes>( + cx: *mut JSContext, + proxy: RawHandleObject, + id: RawHandleId, + v: RawHandleValue, + receiver: RawHandleValue, + result: *mut ObjectOpResult, +) -> bool { + let cx = SafeJSContext::from_ptr(cx); + + if !<D as DomHelpers<D>>::is_platform_object_same_origin(cx, proxy) { + return cross_origin_set::<D>(cx, proxy, id, v, receiver, result); + } + + // Safe to enter the Realm of proxy now. + let _ac = JSAutoRealm::new(*cx, proxy.get()); + + // OrdinarySet + // <https://tc39.es/ecma262/#sec-ordinaryset> + rooted!(in(*cx) let mut own_desc = PropertyDescriptor::default()); + let mut is_none = false; + if !InvokeGetOwnPropertyDescriptor( + GetProxyHandler(*proxy), + *cx, + proxy, + id, + own_desc.handle_mut().into(), + &mut is_none, + ) { + return false; + } + + js::jsapi::SetPropertyIgnoringNamedGetter( + *cx, + proxy, + id, + v, + receiver, + own_desc.handle().into(), + result, + ) +} + +/// Implementation of `[[GetPrototypeOf]]` for [`Location`]. +/// +/// [`Location`]: https://html.spec.whatwg.org/multipage/#location-getprototypeof +pub(crate) unsafe fn maybe_cross_origin_get_prototype<D: DomTypes>( + cx: SafeJSContext, + proxy: RawHandleObject, + get_proto_object: unsafe fn(cx: SafeJSContext, global: HandleObject, rval: MutableHandleObject), + proto: RawMutableHandleObject, +) -> bool { + // > 1. If ! IsPlatformObjectSameOrigin(this) is true, then return ! OrdinaryGetPrototypeOf(this). + if <D as DomHelpers<D>>::is_platform_object_same_origin(cx, proxy) { + let ac = JSAutoRealm::new(*cx, proxy.get()); + let global = D::GlobalScope::from_context(*cx, InRealm::Entered(&ac)); + get_proto_object( + cx, + global.reflector().get_jsobject(), + MutableHandleObject::from_raw(proto), + ); + return !proto.is_null(); + } + + // > 2. Return null. + proto.set(ptr::null_mut()); + 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<D: DomTypes>( + cx: SafeJSContext, + proxy: RawHandleObject, + receiver: RawHandleValue, + id: RawHandleId, + vp: RawMutableHandleValue, +) -> bool { + // > 1. Let `desc` be `? O.[[GetOwnProperty]](P)`. + rooted!(in(*cx) let mut descriptor = PropertyDescriptor::default()); + let mut is_none = false; + if !InvokeGetOwnPropertyDescriptor( + GetProxyHandler(*proxy), + *cx, + proxy, + id, + descriptor.handle_mut().into(), + &mut is_none, + ) { + return false; + } + + // > 2. Assert: `desc` is not undefined. + assert!( + !is_none, + "Callees should throw in all cases when they are not finding \ + a property decriptor" + ); + + // > 3. If `! IsDataDescriptor(desc)` is true, then return `desc.[[Value]]`. + if is_data_descriptor(&descriptor) { + vp.set(descriptor.value_); + return true; + } + + // > 4. Assert: `IsAccessorDescriptor(desc)` is `true`. + assert!(is_accessor_descriptor(&descriptor)); + + // > 5. Let `getter` be `desc.[[Get]]`. + // > + // > 6. If `getter` is `undefined`, then throw a `SecurityError` + // > `DOMException`. + rooted!(in(*cx) let mut getter = ptr::null_mut::<JSObject>()); + get_getter_object(&descriptor, getter.handle_mut().into()); + if getter.get().is_null() { + return report_cross_origin_denial::<D>(cx, id, "get"); + } + + rooted!(in(*cx) let mut getter_jsval = UndefinedValue()); + getter.get().to_jsval(*cx, getter_jsval.handle_mut()); + + // > 7. Return `? Call(getter, Receiver)`. + jsapi::Call( + *cx, + receiver, + getter_jsval.handle().into(), + &jsapi::HandleValueArray::empty(), + vp, + ) +} + +/// Implementation of [`CrossOriginSet`]. +/// +/// `cx` and `proxy` are expected to be different-Realm here. `proxy` is a proxy +/// 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<D: DomTypes>( + cx: SafeJSContext, + proxy: RawHandleObject, + id: RawHandleId, + v: RawHandleValue, + receiver: RawHandleValue, + result: *mut ObjectOpResult, +) -> bool { + // > 1. Let desc be ? O.[[GetOwnProperty]](P). + rooted!(in(*cx) let mut descriptor = PropertyDescriptor::default()); + let mut is_none = false; + if !InvokeGetOwnPropertyDescriptor( + GetProxyHandler(*proxy), + *cx, + proxy, + id, + descriptor.handle_mut().into(), + &mut is_none, + ) { + return false; + } + + // > 2. Assert: desc is not undefined. + assert!( + !is_none, + "Callees should throw in all cases when they are not finding \ + a property decriptor" + ); + + // > 3. If desc.[[Set]] is present and its value is not undefined, + // > then: [...] + rooted!(in(*cx) let mut setter = ptr::null_mut::<JSObject>()); + get_setter_object(&descriptor, setter.handle_mut().into()); + if setter.get().is_null() { + // > 4. Throw a "SecurityError" DOMException. + return report_cross_origin_denial::<D>(cx, id, "set"); + } + + rooted!(in(*cx) let mut setter_jsval = UndefinedValue()); + setter.get().to_jsval(*cx, setter_jsval.handle_mut()); + + // > 3.1. Perform ? Call(setter, Receiver, «V»). + // > + // > 3.2. Return true. + rooted!(in(*cx) let mut ignored = UndefinedValue()); + if !jsapi::Call( + *cx, + receiver, + setter_jsval.handle().into(), + // FIXME: Our binding lacks `HandleValueArray(Handle<Value>)` + // <https://searchfox.org/mozilla-central/rev/072710086ddfe25aa2962c8399fefb2304e8193b/js/public/ValueArray.h#54-55> + &jsapi::HandleValueArray { + length_: 1, + elements_: v.ptr, + }, + ignored.handle_mut().into(), + ) { + return false; + } + + (*result).code_ = 0 /* OkCode */; + true +} + +/// 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<D: DomTypes>( + cx: SafeJSContext, + _proxy: RawHandleObject, + id: RawHandleId, + desc: RawMutableHandle<PropertyDescriptor>, + is_none: &mut bool, +) -> bool { + assert!(*is_none, "why are we being called?"); + + // > 1. If P is `then`, `@@toStringTag`, `@@hasInstance`, or + // > `@@isConcatSpreadable`, then return `PropertyDescriptor{ [[Value]]: + // > undefined, [[Writable]]: false, [[Enumerable]]: false, + // > [[Configurable]]: true }`. + if is_cross_origin_allowlisted_prop(cx, id) { + set_property_descriptor( + MutableHandle::from_raw(desc), + HandleValue::undefined(), + jsapi::JSPROP_READONLY as u32, + is_none, + ); + return true; + } + + // > 2. Throw a `SecurityError` `DOMException`. + report_cross_origin_denial::<D>(cx, id, "access") +} diff --git a/components/script_bindings/realms.rs b/components/script_bindings/realms.rs new file mode 100644 index 00000000000..3fe60d5ae5c --- /dev/null +++ b/components/script_bindings/realms.rs @@ -0,0 +1,64 @@ +/* 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 js::jsapi::{GetCurrentRealmOrNull, JSAutoRealm}; + +use crate::DomTypes; +use crate::interfaces::GlobalScopeHelpers; +use crate::reflector::DomObject; +use crate::script_runtime::JSContext; + +pub struct AlreadyInRealm(()); + +impl AlreadyInRealm { + #![allow(unsafe_code)] + pub fn assert<D: DomTypes>() -> AlreadyInRealm { + unsafe { + assert!(!GetCurrentRealmOrNull(*D::GlobalScope::get_cx()).is_null()); + } + AlreadyInRealm(()) + } + + pub fn assert_for_cx(cx: JSContext) -> AlreadyInRealm { + unsafe { + assert!(!GetCurrentRealmOrNull(*cx).is_null()); + } + AlreadyInRealm(()) + } +} + +#[derive(Clone, Copy)] +pub enum InRealm<'a> { + Already(&'a AlreadyInRealm), + Entered(&'a JSAutoRealm), +} + +impl<'a> From<&'a AlreadyInRealm> for InRealm<'a> { + fn from(token: &'a AlreadyInRealm) -> InRealm<'a> { + InRealm::already(token) + } +} + +impl<'a> From<&'a JSAutoRealm> for InRealm<'a> { + fn from(token: &'a JSAutoRealm) -> InRealm<'a> { + InRealm::entered(token) + } +} + +impl InRealm<'_> { + pub fn already(token: &AlreadyInRealm) -> InRealm { + InRealm::Already(token) + } + + pub fn entered(token: &JSAutoRealm) -> InRealm { + InRealm::Entered(token) + } +} + +pub fn enter_realm<D: DomTypes>(object: &impl DomObject) -> JSAutoRealm { + JSAutoRealm::new( + *D::GlobalScope::get_cx(), + object.reflector().get_jsobject().get(), + ) +} diff --git a/components/script_bindings/reflector.rs b/components/script_bindings/reflector.rs index 2f9e77c6928..6b6ae03cb69 100644 --- a/components/script_bindings/reflector.rs +++ b/components/script_bindings/reflector.rs @@ -6,6 +6,13 @@ use js::jsapi::{Heap, JSObject}; use js::rust::HandleObject; use malloc_size_of_derive::MallocSizeOf; +use crate::interfaces::GlobalScopeHelpers; +use crate::iterable::{Iterable, IterableIterator}; +use crate::realms::{AlreadyInRealm, InRealm}; +use crate::root::{Dom, DomRoot, Root}; +use crate::script_runtime::{CanGc, JSContext}; +use crate::{DomTypes, JSTraceable}; + /// A struct to store a reference to the reflector of a DOM object. #[cfg_attr(crown, allow(crown::unrooted_must_root))] #[derive(MallocSizeOf)] @@ -90,3 +97,55 @@ impl MutDomObject for Reflector { self.set_jsobject(obj) } } + +pub trait DomGlobalGeneric<D: DomTypes>: DomObject { + /// Returns the [`GlobalScope`] of the realm that the [`DomObject`] was created in. If this + /// object is a `Node`, this will be different from it's owning `Document` if adopted by. For + /// `Node`s it's almost always better to use `NodeTraits::owning_global`. + fn global_(&self, realm: InRealm) -> DomRoot<D::GlobalScope> + where + Self: Sized, + { + D::GlobalScope::from_reflector(self, realm) + } + + /// Returns the [`GlobalScope`] of the realm that the [`DomObject`] was created in. If this + /// object is a `Node`, this will be different from it's owning `Document` if adopted by. For + /// `Node`s it's almost always better to use `NodeTraits::owning_global`. + fn global(&self) -> DomRoot<D::GlobalScope> + where + Self: Sized, + { + let realm = AlreadyInRealm::assert_for_cx(D::GlobalScope::get_cx()); + D::GlobalScope::from_reflector(self, InRealm::already(&realm)) + } +} + +impl<D: DomTypes, T: DomObject> DomGlobalGeneric<D> for T {} + +/// A trait to provide a function pointer to wrap function for DOM objects. +pub trait DomObjectWrap<D: DomTypes>: Sized + DomObject + DomGlobalGeneric<D> { + /// Function pointer to the general wrap function type + #[allow(clippy::type_complexity)] + const WRAP: unsafe fn( + JSContext, + &D::GlobalScope, + Option<HandleObject>, + Box<Self>, + CanGc, + ) -> Root<Dom<Self>>; +} + +/// A trait to provide a function pointer to wrap function for +/// DOM iterator interfaces. +pub trait DomObjectIteratorWrap<D: DomTypes>: DomObjectWrap<D> + JSTraceable + Iterable { + /// Function pointer to the wrap function for `IterableIterator<T>` + #[allow(clippy::type_complexity)] + const ITER_WRAP: unsafe fn( + JSContext, + &D::GlobalScope, + Option<HandleObject>, + Box<IterableIterator<D, Self>>, + CanGc, + ) -> Root<Dom<IterableIterator<D, Self>>>; +} diff --git a/components/script_bindings/settings_stack.rs b/components/script_bindings/settings_stack.rs new file mode 100644 index 00000000000..cdc8c2a029d --- /dev/null +++ b/components/script_bindings/settings_stack.rs @@ -0,0 +1,142 @@ +/* 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::marker::PhantomData; +use std::thread; + +use js::jsapi::{HideScriptedCaller, UnhideScriptedCaller}; +use js::rust::Runtime; + +use crate::DomTypes; +use crate::interfaces::{DomHelpers, GlobalScopeHelpers}; +use crate::root::{Dom, DomRoot}; +use crate::script_runtime::CanGc; + +#[derive(Debug, Eq, JSTraceable, PartialEq)] +pub enum StackEntryKind { + Incumbent, + Entry, +} + +#[cfg_attr(crown, allow(crown::unrooted_must_root))] +#[derive(JSTraceable)] +pub struct StackEntry<D: DomTypes> { + pub global: Dom<D::GlobalScope>, + pub kind: StackEntryKind, +} + +/// RAII struct that pushes and pops entries from the script settings stack. +pub struct GenericAutoEntryScript<D: DomTypes> { + global: DomRoot<D::GlobalScope>, + #[cfg(feature = "tracing")] + #[allow(dead_code)] + span: tracing::span::EnteredSpan, +} + +impl<D: DomTypes> GenericAutoEntryScript<D> { + /// <https://html.spec.whatwg.org/multipage/#prepare-to-run-script> + pub fn new(global: &D::GlobalScope) -> Self { + let settings_stack = <D as DomHelpers<D>>::settings_stack(); + settings_stack.with(|stack| { + trace!("Prepare to run script with {:p}", global); + let mut stack = stack.borrow_mut(); + stack.push(StackEntry { + global: Dom::from_ref(global), + kind: StackEntryKind::Entry, + }); + Self { + global: DomRoot::from_ref(global), + #[cfg(feature = "tracing")] + span: tracing::info_span!( + "ScriptEvaluate", + servo_profiling = true, + url = global.get_url().to_string(), + ) + .entered(), + } + }) + } +} + +impl<D: DomTypes> Drop for GenericAutoEntryScript<D> { + /// <https://html.spec.whatwg.org/multipage/#clean-up-after-running-script> + fn drop(&mut self) { + let settings_stack = <D as DomHelpers<D>>::settings_stack(); + settings_stack.with(|stack| { + let mut stack = stack.borrow_mut(); + let entry = stack.pop().unwrap(); + assert_eq!( + &*entry.global as *const D::GlobalScope, &*self.global as *const D::GlobalScope, + "Dropped AutoEntryScript out of order." + ); + assert_eq!(entry.kind, StackEntryKind::Entry); + trace!("Clean up after running script with {:p}", &*entry.global); + }); + + // Step 5 + if !thread::panicking() && D::GlobalScope::incumbent().is_none() { + self.global.perform_a_microtask_checkpoint(CanGc::note()); + } + } +} + +/// RAII struct that pushes and pops entries from the script settings stack. +pub struct GenericAutoIncumbentScript<D: DomTypes> { + global: usize, + _marker: PhantomData<D>, +} + +impl<D: DomTypes> GenericAutoIncumbentScript<D> { + /// <https://html.spec.whatwg.org/multipage/#prepare-to-run-a-callback> + pub fn new(global: &D::GlobalScope) -> Self { + // Step 2-3. + unsafe { + let cx = + Runtime::get().expect("Creating a new incumbent script after runtime shutdown"); + HideScriptedCaller(cx.as_ptr()); + } + let settings_stack = <D as DomHelpers<D>>::settings_stack(); + settings_stack.with(|stack| { + trace!("Prepare to run a callback with {:p}", global); + // Step 1. + let mut stack = stack.borrow_mut(); + stack.push(StackEntry { + global: Dom::from_ref(global), + kind: StackEntryKind::Incumbent, + }); + Self { + global: global as *const _ as usize, + _marker: PhantomData, + } + }) + } +} + +impl<D: DomTypes> Drop for GenericAutoIncumbentScript<D> { + /// <https://html.spec.whatwg.org/multipage/#clean-up-after-running-a-callback> + fn drop(&mut self) { + let settings_stack = <D as DomHelpers<D>>::settings_stack(); + settings_stack.with(|stack| { + // Step 4. + let mut stack = stack.borrow_mut(); + let entry = stack.pop().unwrap(); + // Step 3. + assert_eq!( + &*entry.global as *const D::GlobalScope as usize, self.global, + "Dropped AutoIncumbentScript out of order." + ); + assert_eq!(entry.kind, StackEntryKind::Incumbent); + trace!( + "Clean up after running a callback with {:p}", + &*entry.global + ); + }); + unsafe { + // Step 1-2. + if let Some(cx) = Runtime::get() { + UnhideScriptedCaller(cx.as_ptr()); + } + } + } +} diff --git a/components/script_bindings/trace.rs b/components/script_bindings/trace.rs index 3248fbfc67c..dbdf5be94df 100644 --- a/components/script_bindings/trace.rs +++ b/components/script_bindings/trace.rs @@ -3,16 +3,21 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use std::cell::OnceCell; +use std::fmt::Display; use std::hash::{BuildHasher, Hash}; use std::marker::PhantomData; +use std::mem; +use std::ops::{Deref, DerefMut}; use crossbeam_channel::Sender; use html5ever::interface::{Tracer as HtmlTracer, TreeSink}; use html5ever::tokenizer::{TokenSink, Tokenizer}; use html5ever::tree_builder::TreeBuilder; use indexmap::IndexMap; +use js::gc::{GCMethods, Handle}; use js::glue::CallObjectTracer; use js::jsapi::{GCTraceKindToAscii, Heap, JSObject, JSTracer, TraceKind}; +use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; use parking_lot::RwLock; use servo_arc::Arc as ServoArc; use smallvec::SmallVec; @@ -46,7 +51,11 @@ pub unsafe fn trace_reflector(tracer: *mut JSTracer, description: &str, reflecto /// /// # Safety /// tracer must point to a valid, non-null JS tracer. -pub unsafe fn trace_object(tracer: *mut JSTracer, description: &str, obj: &Heap<*mut JSObject>) { +pub(crate) unsafe fn trace_object( + tracer: *mut JSTracer, + description: &str, + obj: &Heap<*mut JSObject>, +) { unsafe { trace!("tracing {}", description); CallObjectTracer( @@ -308,3 +317,101 @@ unsafe impl<Handle: JSTraceable + Clone, Sink: JSTraceable + XmlTreeSink<Handle tree_builder.sink.trace(trc); } } + +/// Roots any JSTraceable thing +/// +/// If you have a valid DomObject, use DomRoot. +/// If you have GC things like *mut JSObject or JSVal, use rooted!. +/// If you have an arbitrary number of DomObjects to root, use rooted_vec!. +/// If you know what you're doing, use this. +#[cfg_attr(crown, crown::unrooted_must_root_lint::allow_unrooted_interior)] +pub struct RootedTraceableBox<T: JSTraceable + 'static>(js::gc::RootedTraceableBox<T>); + +unsafe impl<T: JSTraceable + 'static> JSTraceable for RootedTraceableBox<T> { + unsafe fn trace(&self, tracer: *mut JSTracer) { + self.0.trace(tracer); + } +} + +impl<T: JSTraceable + 'static> RootedTraceableBox<T> { + /// DomRoot a JSTraceable thing for the life of this RootedTraceableBox + pub fn new(traceable: T) -> RootedTraceableBox<T> { + Self(js::gc::RootedTraceableBox::new(traceable)) + } + + /// Consumes a boxed JSTraceable and roots it for the life of this RootedTraceableBox. + pub fn from_box(boxed_traceable: Box<T>) -> RootedTraceableBox<T> { + Self(js::gc::RootedTraceableBox::from_box(boxed_traceable)) + } +} + +impl<T> RootedTraceableBox<Heap<T>> +where + Heap<T>: JSTraceable + 'static, + T: GCMethods + Copy, +{ + pub fn handle(&self) -> Handle<T> { + self.0.handle() + } +} + +impl<T: JSTraceable + MallocSizeOf> MallocSizeOf for RootedTraceableBox<T> { + fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { + // Briefly resurrect the real Box value so we can rely on the existing calculations. + // Then immediately forget about it again to avoid dropping the box. + let inner = unsafe { Box::from_raw(self.0.ptr()) }; + let size = inner.size_of(ops); + mem::forget(inner); + size + } +} + +impl<T: JSTraceable + Default> Default for RootedTraceableBox<T> { + fn default() -> RootedTraceableBox<T> { + RootedTraceableBox::new(T::default()) + } +} + +impl<T: JSTraceable> Deref for RootedTraceableBox<T> { + type Target = T; + fn deref(&self) -> &T { + self.0.deref() + } +} + +impl<T: JSTraceable> DerefMut for RootedTraceableBox<T> { + fn deref_mut(&mut self) -> &mut T { + self.0.deref_mut() + } +} + +/// Wrapper type for nop traceble +/// +/// SAFETY: Inner type must not impl JSTraceable +#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)] +#[cfg_attr(crown, crown::trace_in_no_trace_lint::must_not_have_traceable)] +pub(crate) struct NoTrace<T>(pub(crate) T); + +impl<T: Display> Display for NoTrace<T> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } +} + +impl<T> From<T> for NoTrace<T> { + fn from(item: T) -> Self { + Self(item) + } +} + +#[allow(unsafe_code)] +unsafe impl<T> JSTraceable for NoTrace<T> { + #[inline] + unsafe fn trace(&self, _: *mut ::js::jsapi::JSTracer) {} +} + +impl<T: MallocSizeOf> MallocSizeOf for NoTrace<T> { + fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { + self.0.size_of(ops) + } +} diff --git a/components/script_bindings/utils.rs b/components/script_bindings/utils.rs index 001360e07ff..95f742c9a06 100644 --- a/components/script_bindings/utils.rs +++ b/components/script_bindings/utils.rs @@ -5,17 +5,23 @@ use std::ffi::CString; use std::os::raw::{c_char, c_void}; use std::ptr::{self, NonNull}; +use std::slice; use js::conversions::ToJSValConvertible; +use js::gc::Handle; use js::glue::{ CallJitGetterOp, CallJitMethodOp, CallJitSetterOp, JS_GetReservedSlot, RUST_FUNCTION_VALUE_TO_JITINFO, }; use js::jsapi::{ AtomToLinearString, CallArgs, ExceptionStackBehavior, GetLinearStringCharAt, - GetLinearStringLength, GetNonCCWObjectGlobal, HandleObject as RawHandleObject, Heap, - JS_ClearPendingException, JS_IsExceptionPending, JSAtom, JSContext, JSJitInfo, JSObject, - JSTracer, MutableHandleValue as RawMutableHandleValue, ObjectOpResult, StringIsArrayIndex, + GetLinearStringLength, GetNonCCWObjectGlobal, HandleId as RawHandleId, + HandleObject as RawHandleObject, Heap, JS_ClearPendingException, + JS_DeprecatedStringHasLatin1Chars, JS_EnumerateStandardClasses, + JS_GetLatin1StringCharsAndLength, JS_IsExceptionPending, JS_IsGlobalObject, + JS_ResolveStandardClass, JSAtom, JSContext, JSJitInfo, JSObject, JSTracer, + MutableHandleIdVector as RawMutableHandleIdVector, MutableHandleValue as RawMutableHandleValue, + ObjectOpResult, StringIsArrayIndex, }; use js::jsval::{JSVal, UndefinedValue}; use js::rust::wrappers::{ @@ -29,11 +35,13 @@ use js::rust::{ use js::{JS_CALLEE, rooted}; use malloc_size_of::MallocSizeOfOps; +use crate::DomTypes; use crate::codegen::Globals::Globals; use crate::codegen::InheritTypes::TopTypeId; use crate::codegen::PrototypeList::{self, MAX_PROTO_CHAIN_LENGTH, PROTO_OR_IFACE_LENGTH}; use crate::conversions::{PrototypeCheck, jsstring_to_str, private_from_proto_check}; use crate::error::throw_invalid_this; +use crate::interfaces::DomHelpers; use crate::script_runtime::{CanGc, JSContext as SafeJSContext}; use crate::str::DOMString; use crate::trace::trace_object; @@ -77,24 +85,24 @@ unsafe impl Sync for DOMJSClass {} /// The index of the slot where the object holder of that interface's /// unforgeable members are defined. -pub const DOM_PROTO_UNFORGEABLE_HOLDER_SLOT: u32 = 0; +pub(crate) const DOM_PROTO_UNFORGEABLE_HOLDER_SLOT: u32 = 0; /// The index of the slot that contains a reference to the ProtoOrIfaceArray. // All DOM globals must have a slot at DOM_PROTOTYPE_SLOT. -pub const DOM_PROTOTYPE_SLOT: u32 = js::JSCLASS_GLOBAL_SLOT_COUNT; +pub(crate) const DOM_PROTOTYPE_SLOT: u32 = js::JSCLASS_GLOBAL_SLOT_COUNT; /// The flag set on the `JSClass`es for DOM global objects. // NOTE: This is baked into the Ion JIT as 0 in codegen for LGetDOMProperty and // LSetDOMProperty. Those constants need to be changed accordingly if this value // changes. -pub const JSCLASS_DOM_GLOBAL: u32 = js::JSCLASS_USERBIT1; +pub(crate) const JSCLASS_DOM_GLOBAL: u32 = js::JSCLASS_USERBIT1; /// Returns the ProtoOrIfaceArray for the given global object. /// Fails if `global` is not a DOM global object. /// /// # Safety /// `global` must point to a valid, non-null JS object. -pub unsafe fn get_proto_or_iface_array(global: *mut JSObject) -> *mut ProtoOrIfaceArray { +pub(crate) unsafe fn get_proto_or_iface_array(global: *mut JSObject) -> *mut ProtoOrIfaceArray { assert_ne!(((*get_object_class(global)).flags & JSCLASS_DOM_GLOBAL), 0); let mut slot = UndefinedValue(); JS_GetReservedSlot(global, DOM_PROTOTYPE_SLOT, &mut slot); @@ -112,7 +120,7 @@ pub type ProtoOrIfaceArray = [*mut JSObject; PROTO_OR_IFACE_LENGTH]; /// # Safety /// `cx` must point to a valid, non-null JSContext. /// `found` must point to a valid, non-null bool. -pub unsafe fn get_property_on_prototype( +pub(crate) unsafe fn get_property_on_prototype( cx: *mut JSContext, proxy: HandleObject, receiver: HandleValue, @@ -206,7 +214,7 @@ pub fn get_array_index_from_id(id: HandleId) -> Option<u32> { /// # Safety /// `cx` must point to a valid, non-null JSContext. #[allow(clippy::result_unit_err)] -pub unsafe fn find_enum_value<'a, T>( +pub(crate) unsafe fn find_enum_value<'a, T>( cx: *mut JSContext, v: HandleValue, pairs: &'a [(&'static str, T)], @@ -285,7 +293,7 @@ pub unsafe fn get_dictionary_property( /// # Safety /// `cx` must point to a valid, non-null JSContext. #[allow(clippy::result_unit_err)] -pub unsafe fn set_dictionary_property( +pub(crate) unsafe fn set_dictionary_property( cx: *mut JSContext, object: HandleObject, property: &str, @@ -325,7 +333,7 @@ pub unsafe fn has_property_on_prototype( /// /// # Safety /// `cx` must point to a valid, non-null JSContext. -pub unsafe fn delete_property_by_id( +pub(crate) unsafe fn delete_property_by_id( cx: *mut JSContext, object: HandleObject, id: HandleId, @@ -404,7 +412,7 @@ unsafe fn generic_call<const EXCEPTION_TO_REJECTION: bool>( /// # Safety /// `cx` must point to a valid, non-null JSContext. /// `vp` must point to a VALID, non-null JSVal. -pub unsafe extern "C" fn generic_method<const EXCEPTION_TO_REJECTION: bool>( +pub(crate) unsafe extern "C" fn generic_method<const EXCEPTION_TO_REJECTION: bool>( cx: *mut JSContext, argc: libc::c_uint, vp: *mut JSVal, @@ -417,7 +425,7 @@ pub unsafe extern "C" fn generic_method<const EXCEPTION_TO_REJECTION: bool>( /// # Safety /// `cx` must point to a valid, non-null JSContext. /// `vp` must point to a VALID, non-null JSVal. -pub unsafe extern "C" fn generic_getter<const EXCEPTION_TO_REJECTION: bool>( +pub(crate) unsafe extern "C" fn generic_getter<const EXCEPTION_TO_REJECTION: bool>( cx: *mut JSContext, argc: libc::c_uint, vp: *mut JSVal, @@ -430,7 +438,7 @@ pub unsafe extern "C" fn generic_getter<const EXCEPTION_TO_REJECTION: bool>( /// # Safety /// `cx` must point to a valid, non-null JSContext. /// `vp` must point to a VALID, non-null JSVal. -pub unsafe extern "C" fn generic_lenient_getter<const EXCEPTION_TO_REJECTION: bool>( +pub(crate) unsafe extern "C" fn generic_lenient_getter<const EXCEPTION_TO_REJECTION: bool>( cx: *mut JSContext, argc: libc::c_uint, vp: *mut JSVal, @@ -458,7 +466,7 @@ unsafe extern "C" fn call_setter( /// # Safety /// `cx` must point to a valid, non-null JSContext. /// `vp` must point to a VALID, non-null JSVal. -pub unsafe extern "C" fn generic_setter( +pub(crate) unsafe extern "C" fn generic_setter( cx: *mut JSContext, argc: libc::c_uint, vp: *mut JSVal, @@ -471,7 +479,7 @@ pub unsafe extern "C" fn generic_setter( /// # Safety /// `cx` must point to a valid, non-null JSContext. /// `vp` must point to a VALID, non-null JSVal. -pub unsafe extern "C" fn generic_lenient_setter( +pub(crate) unsafe extern "C" fn generic_lenient_setter( cx: *mut JSContext, argc: libc::c_uint, vp: *mut JSVal, @@ -484,7 +492,7 @@ pub unsafe extern "C" fn generic_lenient_setter( /// /// `cx` must point to a valid, non-null JSContext. /// `vp` must point to a VALID, non-null JSVal. -pub unsafe extern "C" fn generic_static_promise_method( +pub(crate) unsafe extern "C" fn generic_static_promise_method( cx: *mut JSContext, argc: libc::c_uint, vp: *mut JSVal, @@ -508,7 +516,7 @@ pub unsafe extern "C" fn generic_static_promise_method( /// /// # Safety /// `cx` must point to a valid, non-null JSContext. -pub unsafe fn exception_to_promise( +pub(crate) unsafe fn exception_to_promise( cx: *mut JSContext, rval: RawMutableHandleValue, _can_gc: CanGc, @@ -533,7 +541,7 @@ pub unsafe fn exception_to_promise( /// # 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) { +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() { @@ -557,7 +565,7 @@ impl<T> AsVoidPtr for T { } // Generic method for returning c_char from caller -pub trait AsCCharPtrPtr { +pub(crate) trait AsCCharPtrPtr { fn as_c_char_ptr(&self) -> *const c_char; } @@ -566,3 +574,58 @@ impl AsCCharPtrPtr for [u8] { self as *const [u8] as *const c_char } } + +/// Enumerate lazy properties of a global object. +pub(crate) unsafe extern "C" fn enumerate_global<D: DomTypes>( + cx: *mut JSContext, + obj: RawHandleObject, + _props: RawMutableHandleIdVector, + _enumerable_only: bool, +) -> bool { + assert!(JS_IsGlobalObject(obj.get())); + if !JS_EnumerateStandardClasses(cx, obj) { + return false; + } + for init_fun in <D as DomHelpers<D>>::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<D: DomTypes>( + cx: *mut JSContext, + obj: RawHandleObject, + id: RawHandleId, + rval: *mut bool, +) -> bool { + assert!(JS_IsGlobalObject(obj.get())); + if !JS_ResolveStandardClass(cx, obj, id, rval) { + return false; + } + if *rval { + return true; + } + if !id.is_string() { + *rval = false; + return true; + } + + let string = id.to_string(); + if !JS_DeprecatedStringHasLatin1Chars(string) { + *rval = false; + return true; + } + let mut length = 0; + let ptr = JS_GetLatin1StringCharsAndLength(cx, ptr::null(), string, &mut length); + assert!(!ptr.is_null()); + let bytes = slice::from_raw_parts(ptr, length); + + if let Some(init_fun) = <D as DomHelpers<D>>::interface_map().get(bytes) { + init_fun(SafeJSContext::from_ptr(cx), Handle::from_raw(obj)); + *rval = true; + } else { + *rval = false; + } + true +} diff --git a/components/script_bindings/weakref.rs b/components/script_bindings/weakref.rs index 38f4f709997..2ec07e1f4af 100644 --- a/components/script_bindings/weakref.rs +++ b/components/script_bindings/weakref.rs @@ -29,7 +29,7 @@ use crate::root::DomRoot; /// stored for weak-referenceable bindings. We use slot 1 for holding it, /// this is unsafe for globals, we disallow weak-referenceable globals /// directly in codegen. -pub const DOM_WEAK_SLOT: u32 = 1; +pub(crate) const DOM_WEAK_SLOT: u32 = 1; /// A weak reference to a JS-managed DOM object. #[cfg_attr(crown, crown::unrooted_must_root_lint::allow_unrooted_interior)] |