aboutsummaryrefslogtreecommitdiffstats
path: root/components
diff options
context:
space:
mode:
authorJosh Matthews <josh@joshmatthews.net>2023-05-25 23:59:02 -0400
committerJosh Matthews <josh@joshmatthews.net>2023-05-28 23:23:12 -0400
commitd9600ff50f3c1bdd8c44e2dfc15a18416d80cb82 (patch)
tree6a56ce1cf4458292f41791399e0ac269e3d6d46e /components
parent4ee789a85c50bb31521a00176b311ecdb8ccbcc5 (diff)
downloadservo-d9600ff50f3c1bdd8c44e2dfc15a18416d80cb82.tar.gz
servo-d9600ff50f3c1bdd8c44e2dfc15a18416d80cb82.zip
Support arbitrary protos when wrapping EventTarget objects.
Diffstat (limited to 'components')
-rw-r--r--components/script/dom/bindings/codegen/CodegenRust.py55
-rw-r--r--components/script/dom/bindings/interface.rs122
-rw-r--r--components/script/dom/bindings/iterable.rs4
-rw-r--r--components/script/dom/bindings/reflector.rs14
-rw-r--r--components/script/dom/bindings/utils.rs4
-rw-r--r--components/script/dom/eventtarget.rs8
6 files changed, 185 insertions, 22 deletions
diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py
index 24cf71d8820..5ed330083b9 100644
--- a/components/script/dom/bindings/codegen/CodegenRust.py
+++ b/components/script/dom/bindings/codegen/CodegenRust.py
@@ -2313,10 +2313,11 @@ def DOMClass(descriptor):
return """\
DOMClass {
interface_chain: [ %s ],
+ depth: %d,
type_id: %s,
malloc_size_of: %s as unsafe fn(&mut _, _) -> _,
global: InterfaceObjectMap::Globals::%s,
-}""" % (prototypeChainString, DOMClassTypeId(descriptor), mallocSizeOf, globals_)
+}""" % (prototypeChainString, descriptor.prototypeDepth, DOMClassTypeId(descriptor), mallocSizeOf, globals_)
class CGDOMJSClass(CGThing):
@@ -2865,7 +2866,7 @@ ensure_expando_object(*cx, obj.handle().into(), expando.handle_mut());
copyFunc = "JS_InitializePropertiesFromCompatibleNativeObject"
copyCode += """\
let mut slot = UndefinedValue();
-JS_GetReservedSlot(proto.get(), DOM_PROTO_UNFORGEABLE_HOLDER_SLOT, &mut slot);
+JS_GetReservedSlot(canonical_proto.get(), DOM_PROTO_UNFORGEABLE_HOLDER_SLOT, &mut slot);
rooted!(in(*cx) let mut unforgeable_holder = ptr::null_mut::<JSObject>());
unforgeable_holder.handle_mut().set(slot.to_object());
assert!(%(copyFunc)s(*cx, %(obj)s.handle(), unforgeable_holder.handle()));
@@ -2884,6 +2885,7 @@ class CGWrapMethod(CGAbstractMethod):
assert not descriptor.isGlobal()
args = [Argument('SafeJSContext', 'cx'),
Argument('&GlobalScope', 'scope'),
+ Argument('Option<HandleObject>', 'given_proto'),
Argument("Box<%s>" % descriptor.concreteType, 'object')]
retval = 'DomRoot<%s>' % descriptor.concreteType
CGAbstractMethod.__init__(self, descriptor, 'Wrap', retval, args,
@@ -2896,7 +2898,7 @@ class CGWrapMethod(CGAbstractMethod):
proto = "ptr::null_mut()"
lazyProto = "true" # Our proxy handler will manage the prototype
else:
- proto = "proto.get()"
+ proto = "canonical_proto.get()"
lazyProto = "false"
create = """
@@ -2924,6 +2926,15 @@ SetProxyReservedSlot(
else:
lazyProto = None
create = """
+rooted!(in(*cx) let mut proto = ptr::null_mut::<JSObject>());
+if let Some(given) = given_proto {
+ *proto = *given;
+ if get_context_realm(*cx) != get_object_realm(*given) {
+ assert!(JS_WrapObject(*cx, proto.handle_mut()));
+ }
+} else {
+ *proto = *canonical_proto;
+}
rooted!(in(*cx) let obj = JS_NewObjectWithGivenProto(
*cx,
&Class.base,
@@ -2951,9 +2962,9 @@ assert!(!scope.get().is_null());
assert!(((*get_object_class(scope.get())).flags & JSCLASS_IS_GLOBAL) != 0);
let _ac = JSAutoRealm::new(*cx, scope.get());
-rooted!(in(*cx) let mut proto = ptr::null_mut::<JSObject>());
-GetProtoObject(cx, scope, proto.handle_mut());
-assert!(!proto.is_null());
+rooted!(in(*cx) let mut canonical_proto = ptr::null_mut::<JSObject>());
+GetProtoObject(cx, scope, canonical_proto.handle_mut());
+assert!(!canonical_proto.is_null());
%(createObject)s
let root = raw.reflect_with(obj.get());
@@ -3010,9 +3021,9 @@ assert!(!obj.is_null());
let root = raw.reflect_with(obj.get());
let _ac = JSAutoRealm::new(*cx, obj.get());
-rooted!(in(*cx) let mut proto = ptr::null_mut::<JSObject>());
-GetProtoObject(cx, obj.handle(), proto.handle_mut());
-assert!(JS_SetPrototype(*cx, obj.handle(), proto.handle()));
+rooted!(in(*cx) let mut canonical_proto = ptr::null_mut::<JSObject>());
+GetProtoObject(cx, obj.handle(), canonical_proto.handle_mut());
+assert!(JS_SetPrototype(*cx, obj.handle(), canonical_proto.handle()));
let mut immutable = false;
assert!(JS_SetImmutablePrototype(*cx, obj.handle(), &mut immutable));
assert!(immutable);
@@ -3076,6 +3087,7 @@ impl DomObjectWrap for %s {
const WRAP: unsafe fn(
SafeJSContext,
&GlobalScope,
+ Option<HandleObject>,
Box<Self>,
) -> Root<Dom<Self>> = Wrap;
}
@@ -3098,6 +3110,7 @@ impl DomObjectIteratorWrap for %s {
const ITER_WRAP: unsafe fn(
SafeJSContext,
&GlobalScope,
+ Option<HandleObject>,
Box<IterableIterator<Self>>,
) -> Root<Dom<IterableIterator<Self>>> = Wrap;
}
@@ -6102,13 +6115,25 @@ class CGClassConstructHook(CGAbstractExternMethod):
def definition_body(self):
preamble = """let cx = SafeJSContext::from_ptr(cx);
+let args = CallArgs::from_vp(vp, argc);
let global = GlobalScope::from_object(JS_CALLEE(*cx, vp).to_object());
-"""
+rooted!(in(*cx) let mut desired_proto = ptr::null_mut::<JSObject>());
+let proto_result = get_desired_proto(
+ cx,
+ &args,
+ PrototypeList::ID::%s,
+ CreateInterfaceObjects,
+ desired_proto.handle_mut(),
+);
+ assert!(proto_result.is_ok());
+if proto_result.is_err() {
+ return false;
+}
+""" % (MakeNativeName(self.descriptor.name))
if len(self.exposureSet) == 1:
preamble += """\
let global = DomRoot::downcast::<dom::types::%s>(global).unwrap();
""" % list(self.exposureSet)[0]
- preamble += """let args = CallArgs::from_vp(vp, argc);\n"""
preamble = CGGeneric(preamble)
if self.constructor.isHTMLConstructor():
signatures = self.constructor.signatures()
@@ -6122,7 +6147,10 @@ let global = DomRoot::downcast::<dom::types::%s>(global).unwrap();
else:
name = self.constructor.identifier.name
nativeName = MakeNativeName(self.descriptor.binaryNameFor(name))
- constructorCall = CGMethodCall(["&global"], nativeName, True,
+ args = ["&global"]
+ if self.descriptor.interface.identifier.name == "EventTarget":
+ args += ["desired_proto.handle()"]
+ constructorCall = CGMethodCall(args, nativeName, True,
self.descriptor, self.constructor)
return CGList([preamble, constructorCall])
@@ -6295,6 +6323,8 @@ def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries
'js::jsapi::CallArgs',
'js::jsapi::CurrentGlobalOrNull',
'js::rust::wrappers::GetPropertyKeys',
+ 'js::rust::get_object_realm',
+ 'js::rust::get_context_realm',
'js::jsapi::GCContext',
'js::jsapi::GetWellKnownSymbol',
'js::rust::Handle',
@@ -6439,6 +6469,7 @@ def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries
'crate::dom::bindings::interface::define_guarded_properties',
'crate::dom::bindings::interface::is_exposed_in',
'crate::dom::bindings::interface::get_per_interface_object_handle',
+ 'crate::dom::bindings::interface::get_desired_proto',
'crate::dom::bindings::htmlconstructor::pop_current_element_queue',
'crate::dom::bindings::htmlconstructor::push_new_element_queue',
'crate::dom::bindings::iterable::Iterable',
diff --git a/components/script/dom/bindings/interface.rs b/components/script/dom/bindings/interface.rs
index 86b8f464629..14546d214ea 100644
--- a/components/script/dom/bindings/interface.rs
+++ b/components/script/dom/bindings/interface.rs
@@ -12,6 +12,7 @@ use crate::dom::bindings::guard::Guard;
use crate::dom::bindings::principals::ServoJSPrincipals;
use crate::dom::bindings::utils::{
get_proto_or_iface_array, ProtoOrIfaceArray, DOM_PROTOTYPE_SLOT, JSCLASS_DOM_GLOBAL,
+ DOMJSClass,
};
use crate::script_runtime::JSContext as SafeJSContext;
use js::error::throw_type_error;
@@ -21,7 +22,7 @@ use js::jsapi::HandleObject as RawHandleObject;
use js::jsapi::{jsid, JSClass, JSClassOps};
use js::jsapi::{
Compartment, CompartmentSpecifier, IsSharableCompartment, IsSystemCompartment,
- JS_IterateCompartments, JS::CompartmentIterResult,
+ JS_IterateCompartments, JS::CompartmentIterResult, CallArgs,
};
use js::jsapi::{JSAutoRealm, JSContext, JSFunctionSpec, JSObject, JSFUN_CONSTRUCTOR};
use js::jsapi::{JSPropertySpec, JSString, JSTracer, JS_AtomizeAndPinString};
@@ -31,13 +32,22 @@ use js::jsapi::{JS_NewStringCopyN, JS_SetReservedSlot};
use js::jsapi::{ObjectOps, OnNewGlobalHookOption, SymbolCode};
use js::jsapi::{TrueHandleValue, Value};
use js::jsapi::{JSPROP_PERMANENT, JSPROP_READONLY, JSPROP_RESOLVING};
+use js::jsapi::CheckedUnwrapStatic;
+use js::jsapi::JS_GetProperty;
+use js::jsapi::GetFunctionRealm;
+use js::jsapi::GetRealmGlobalOrNull;
+use js::jsapi::GetNonCCWObjectGlobal;
+use js::jsapi::JS_WrapObject;
+use js::jsapi::CurrentGlobalOrNull;
use js::jsval::{JSVal, PrivateValue};
+use js::jsval::NullValue;
+use js::rust::is_dom_class;
use js::rust::wrappers::JS_FireOnNewGlobalObject;
use js::rust::wrappers::RUST_SYMBOL_TO_JSID;
use js::rust::wrappers::{JS_DefineProperty, JS_DefineProperty5};
use js::rust::wrappers::{JS_DefineProperty3, JS_DefineProperty4, JS_DefinePropertyById5};
use js::rust::wrappers::{JS_LinkConstructorAndPrototype, JS_NewObjectWithGivenProto};
-use js::rust::{define_methods, define_properties, get_object_class};
+use js::rust::{define_methods, define_properties, get_object_class, maybe_wrap_object};
use js::rust::{HandleObject, HandleValue, MutableHandleObject, RealmOptions};
use servo_url::MutableOrigin;
use std::convert::TryFrom;
@@ -592,3 +602,111 @@ pub fn define_dom_interface(
get_per_interface_object_handle(cx, global, id, creator, proto.handle_mut());
assert!(!proto.is_null());
}
+
+fn get_proto_id_for_new_target(
+ new_target: HandleObject,
+) -> Option<PrototypeList::ID> {
+ unsafe {
+ let new_target_class = get_object_class(*new_target);
+ if is_dom_class(&*new_target_class) {
+ let domjsclass: *const DOMJSClass = new_target_class as *const DOMJSClass;
+ let dom_class = &(*domjsclass).dom_class;
+ return Some(dom_class.interface_chain[dom_class.depth as usize]);
+ }
+ return None;
+ }
+}
+
+pub fn get_desired_proto(
+ cx: SafeJSContext,
+ args: &CallArgs,
+ proto_id: PrototypeList::ID,
+ creator: unsafe fn(SafeJSContext, HandleObject, *mut ProtoOrIfaceArray),
+ mut desired_proto: MutableHandleObject,
+) -> Result<(), ()> {
+ unsafe {
+ // This basically implements
+ // https://heycam.github.io/webidl/#internally-create-a-new-object-implementing-the-interface
+ // step 3.
+
+ //assert!(args.is_constructing())
+
+ // The desired prototype depends on the actual constructor that was invoked,
+ // which is passed to us as the newTarget in the callargs. We want to do
+ // something akin to the ES6 specification's GetProtototypeFromConstructor (so
+ // get .prototype on the newTarget, with a fallback to some sort of default).
+
+ // First, a fast path for the case when the the constructor is in fact one of
+ // our DOM constructors. This is safe because on those the "constructor"
+ // property is non-configurable and non-writable, so we don't have to do the
+ // slow JS_GetProperty call.
+ rooted!(in(*cx) let mut new_target = args.new_target().to_object());
+ rooted!(in(*cx) let original_new_target = *new_target);
+ // See whether we have a known DOM constructor here, such that we can take a
+ // fast path.
+ let target_proto_id = get_proto_id_for_new_target(new_target.handle())
+ .or_else(|| {
+ // We might still have a cross-compartment wrapper for a known DOM
+ // constructor. CheckedUnwrapStatic is fine here, because we're looking for
+ // DOM constructors and those can't be cross-origin objects.
+ *new_target = CheckedUnwrapStatic(*new_target);
+ if !new_target.is_null() && &*new_target != &*original_new_target {
+ get_proto_id_for_new_target(new_target.handle())
+ } else {
+ None
+ }
+ });
+
+ if let Some(proto_id) = target_proto_id {
+ let global = GetNonCCWObjectGlobal(*new_target);
+ let proto_or_iface_cache = get_proto_or_iface_array(global);
+ desired_proto.set((*proto_or_iface_cache)[proto_id as usize]);
+ if &*new_target != &*original_new_target {
+ if !JS_WrapObject(*cx, desired_proto.into()) {
+ return Err(());
+ }
+ }
+ return Ok(());
+ }
+
+ // Slow path. This basically duplicates the ES6 spec's
+ // GetPrototypeFromConstructor except that instead of taking a string naming
+ // the fallback prototype we determine the fallback based on the proto id we
+ // were handed.
+ rooted!(in(*cx) let mut proto_val = NullValue());
+ if !JS_GetProperty(*cx, original_new_target.handle().into(), b"prototype\0".as_ptr() as *const libc::c_char, proto_val.handle_mut().into()) {
+ return Err(());
+ }
+
+ if proto_val.is_object() {
+ desired_proto.set(proto_val.to_object());
+ return Ok(());
+ }
+
+ // Fall back to getting the proto for our given proto id in the realm that
+ // GetFunctionRealm(newTarget) returns.
+ rooted!(in(*cx) let realm = GetFunctionRealm(*cx, new_target.handle().into()));
+
+ if (*realm).is_null() {
+ return Err(());
+ }
+
+ {
+ let _realm = JSAutoRealm::new(*cx, GetRealmGlobalOrNull(*realm));
+ rooted!(in(*cx) let global = CurrentGlobalOrNull(*cx));
+ get_per_interface_object_handle(
+ cx,
+ global.handle(),
+ ProtoOrIfaceIndex::ID(proto_id),
+ creator,
+ desired_proto
+ );
+ if desired_proto.is_null() {
+ return Err(());
+ }
+ }
+
+ maybe_wrap_object(*cx, desired_proto);
+ return Ok(())
+ }
+}
diff --git a/components/script/dom/bindings/iterable.rs b/components/script/dom/bindings/iterable.rs
index 2eac830a4bb..9c7da60b037 100644
--- a/components/script/dom/bindings/iterable.rs
+++ b/components/script/dom/bindings/iterable.rs
@@ -20,7 +20,7 @@ use dom_struct::dom_struct;
use js::conversions::ToJSValConvertible;
use js::jsapi::{Heap, JSObject};
use js::jsval::UndefinedValue;
-use js::rust::{HandleValue, MutableHandleObject};
+use js::rust::{HandleValue, MutableHandleObject, HandleObject};
use std::cell::Cell;
use std::ptr;
use std::ptr::NonNull;
@@ -118,7 +118,7 @@ impl<T: DomObjectIteratorWrap + JSTraceable + Iterable> IterableIterator<T> {
}
impl<T: DomObjectIteratorWrap + JSTraceable + Iterable> DomObjectWrap for IterableIterator<T> {
- const WRAP: unsafe fn(JSContext, &GlobalScope, Box<Self>) -> Root<Dom<Self>> = T::ITER_WRAP;
+ const WRAP: unsafe fn(JSContext, &GlobalScope, Option<HandleObject>, Box<Self>) -> Root<Dom<Self>> = T::ITER_WRAP;
}
fn dict_return(
diff --git a/components/script/dom/bindings/reflector.rs b/components/script/dom/bindings/reflector.rs
index 8176989ed36..32c3d7645e1 100644
--- a/components/script/dom/bindings/reflector.rs
+++ b/components/script/dom/bindings/reflector.rs
@@ -23,7 +23,16 @@ where
U: DerivedFrom<GlobalScope>,
{
let global_scope = global.upcast();
- unsafe { T::WRAP(GlobalScope::get_cx(), global_scope, obj) }
+ unsafe { T::WRAP(GlobalScope::get_cx(), global_scope, None, obj) }
+}
+
+pub fn reflect_dom_object2<T, U>(obj: Box<T>, global: &U, proto: HandleObject) -> DomRoot<T>
+where
+ T: DomObject + DomObjectWrap,
+ U: DerivedFrom<GlobalScope>,
+{
+ let global_scope = global.upcast();
+ unsafe { T::WRAP(GlobalScope::get_cx(), global_scope, Some(proto), obj) }
}
/// A struct to store a reference to the reflector of a DOM object.
@@ -109,7 +118,7 @@ impl MutDomObject for Reflector {
/// A trait to provide a function pointer to wrap function for DOM objects.
pub trait DomObjectWrap: Sized + DomObject {
/// Function pointer to the general wrap function type
- const WRAP: unsafe fn(JSContext, &GlobalScope, Box<Self>) -> Root<Dom<Self>>;
+ const WRAP: unsafe fn(JSContext, &GlobalScope, Option<HandleObject>, Box<Self>) -> Root<Dom<Self>>;
}
/// A trait to provide a function pointer to wrap function for
@@ -119,6 +128,7 @@ pub trait DomObjectIteratorWrap: DomObjectWrap + JSTraceable + Iterable {
const ITER_WRAP: unsafe fn(
JSContext,
&GlobalScope,
+ Option<HandleObject>,
Box<IterableIterator<Self>>,
) -> Root<Dom<IterableIterator<Self>>>;
}
diff --git a/components/script/dom/bindings/utils.rs b/components/script/dom/bindings/utils.rs
index a0d3f75315d..87748da1ec4 100644
--- a/components/script/dom/bindings/utils.rs
+++ b/components/script/dom/bindings/utils.rs
@@ -99,6 +99,9 @@ pub struct DOMClass {
/// derivedness.
pub interface_chain: [PrototypeList::ID; MAX_PROTO_CHAIN_LENGTH],
+ /// The last valid index of `interface_chain`.
+ pub depth: u8,
+
/// The type ID of that interface.
pub type_id: TopTypeId,
@@ -112,6 +115,7 @@ unsafe impl Sync for DOMClass {}
/// The JSClass used for DOM object reflectors.
#[derive(Copy)]
+#[repr(C)]
pub struct DOMJSClass {
/// The actual JSClass.
pub base: js::jsapi::JSClass,
diff --git a/components/script/dom/eventtarget.rs b/components/script/dom/eventtarget.rs
index 9e9bf816c72..dd0079c47dd 100644
--- a/components/script/dom/eventtarget.rs
+++ b/components/script/dom/eventtarget.rs
@@ -21,7 +21,7 @@ use crate::dom::bindings::codegen::UnionTypes::EventListenerOptionsOrBoolean;
use crate::dom::bindings::codegen::UnionTypes::EventOrString;
use crate::dom::bindings::error::{report_pending_exception, Error, Fallible};
use crate::dom::bindings::inheritance::Castable;
-use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
+use crate::dom::bindings::reflector::{reflect_dom_object, reflect_dom_object2, DomObject, Reflector};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString;
use crate::dom::element::Element;
@@ -39,7 +39,7 @@ use fnv::FnvHasher;
use js::jsapi::JS_GetFunctionObject;
use js::rust::transform_u16_to_source_text;
use js::rust::wrappers::CompileFunction;
-use js::rust::{CompileOptionsWrapper, RootedObjectVectorWrapper};
+use js::rust::{CompileOptionsWrapper, RootedObjectVectorWrapper, HandleObject};
use libc::c_char;
use servo_atoms::Atom;
use servo_url::ServoUrl;
@@ -360,8 +360,8 @@ impl EventTarget {
}
#[allow(non_snake_case)]
- pub fn Constructor(global: &GlobalScope) -> Fallible<DomRoot<EventTarget>> {
- Ok(EventTarget::new(global))
+ pub fn Constructor(global: &GlobalScope, proto: HandleObject) -> Fallible<DomRoot<EventTarget>> {
+ Ok(reflect_dom_object2(Box::new(EventTarget::new_inherited()), global, proto))
}
pub fn has_listeners_for(&self, type_: &Atom) -> bool {