diff --git a/components/script/dom/customelementregistry.rs b/components/script/dom/customelementregistry.rs index 94f580f7be9..5437371f75c 100644 --- a/components/script/dom/customelementregistry.rs +++ b/components/script/dom/customelementregistry.rs @@ -26,7 +26,7 @@ use dom::window::Window; use dom_struct::dom_struct; use html5ever::{LocalName, Prefix}; use js::conversions::ToJSValConvertible; -use js::jsapi::{Construct1, IsConstructor, HandleValueArray, HandleObject}; +use js::jsapi::{Construct1, IsCallable, IsConstructor, HandleValueArray, HandleObject, MutableHandleValue}; use js::jsapi::{JS_GetProperty, JSAutoCompartment, JSContext}; use js::jsval::{JSVal, ObjectValue, UndefinedValue}; use std::cell::Cell; @@ -94,15 +94,14 @@ impl CustomElementRegistry { /// https://html.spec.whatwg.org/multipage/#dom-customelementregistry-define /// Steps 10.1, 10.2 #[allow(unsafe_code)] - fn check_prototype(&self, constructor: HandleObject) -> ErrorResult { + fn check_prototype(&self, constructor: HandleObject, prototype: MutableHandleValue) -> ErrorResult { let global_scope = self.window.upcast::(); - rooted!(in(global_scope.get_cx()) let mut prototype = UndefinedValue()); unsafe { // Step 10.1 if !JS_GetProperty(global_scope.get_cx(), constructor, b"prototype\0".as_ptr() as *const _, - prototype.handle_mut()) { + prototype) { return Err(Error::JSFailed); } @@ -113,6 +112,45 @@ impl CustomElementRegistry { } Ok(()) } + + /// https://html.spec.whatwg.org/multipage/#dom-customelementregistry-define + /// Steps 10.3, 10.4 + fn get_callbacks(&self, prototype: HandleObject) -> Fallible { + let cx = self.window.get_cx(); + + // Step 4 + Ok(LifecycleCallbacks { + connected_callback: get_callback(cx, prototype, b"connectedCallback\0")?, + disconnected_callback: get_callback(cx, prototype, b"disconnectedCallback\0")?, + adopted_callback: get_callback(cx, prototype, b"adoptedCallback\0")?, + attribute_changed_callback: get_callback(cx, prototype, b"attributeChangedCallback\0")?, + }) + } +} + +/// https://html.spec.whatwg.org/multipage/#dom-customelementregistry-define +/// Step 10.4 +#[allow(unsafe_code)] +fn get_callback(cx: *mut JSContext, prototype: HandleObject, name: &[u8]) -> Fallible>> { + rooted!(in(cx) let mut callback = UndefinedValue()); + + // Step 10.4.1 + if unsafe { !JS_GetProperty(cx, + prototype, + name.as_ptr() as *const _, + callback.handle_mut()) } { + return Err(Error::JSFailed); + } + + // Step 10.4.2 + if !callback.is_undefined() { + if !callback.is_object() || unsafe { !IsCallable(callback.to_object()) } { + return Err(Error::Type("Lifecycle callback is not callable".to_owned())); + } + Ok(Some(Function::new(cx, callback.to_object()))) + } else { + Ok(None) + } } impl CustomElementRegistryMethods for CustomElementRegistry { @@ -173,22 +211,38 @@ impl CustomElementRegistryMethods for CustomElementRegistry { self.element_definition_is_running.set(true); // Steps 10.1 - 10.2 - let result = { + rooted!(in(global_scope.get_cx()) let mut prototype = UndefinedValue()); + { let _ac = JSAutoCompartment::new(global_scope.get_cx(), constructor.get()); - self.check_prototype(constructor.handle()) + if let Err(error) = self.check_prototype(constructor.handle(), prototype.handle_mut()) { + self.element_definition_is_running.set(false); + return Err(error); + } }; - // TODO: Steps 10.3 - 10.6 - // 10.3 - 10.4 Handle lifecycle callbacks - // 10.5 - 10.6 Get observed attributes from the constructor + // Steps 10.3 - 10.4 + rooted!(in(global_scope.get_cx()) let proto_object = prototype.to_object()); + let callbacks = { + let _ac = JSAutoCompartment::new(global_scope.get_cx(), proto_object.get()); + match self.get_callbacks(proto_object.handle()) { + Ok(callbacks) => callbacks, + Err(error) => { + self.element_definition_is_running.set(false); + return Err(error); + }, + } + }; + + // TODO: Steps 10.5 - 10.6 + // Get observed attributes from the constructor self.element_definition_is_running.set(false); - result?; // Step 11 let definition = CustomElementDefinition::new(name.clone(), local_name, - constructor_); + constructor_, + callbacks); // Step 12 self.definitions.borrow_mut().insert(name.clone(), Rc::new(definition)); @@ -254,6 +308,21 @@ impl CustomElementRegistryMethods for CustomElementRegistry { } } +#[derive(HeapSizeOf, JSTraceable, Clone)] +pub struct LifecycleCallbacks { + #[ignore_heap_size_of = "Rc"] + connected_callback: Option>, + + #[ignore_heap_size_of = "Rc"] + disconnected_callback: Option>, + + #[ignore_heap_size_of = "Rc"] + adopted_callback: Option>, + + #[ignore_heap_size_of = "Rc"] + attribute_changed_callback: Option>, +} + /// https://html.spec.whatwg.org/multipage/#custom-element-definition #[derive(HeapSizeOf, JSTraceable, Clone)] pub struct CustomElementDefinition { @@ -263,14 +332,17 @@ pub struct CustomElementDefinition { #[ignore_heap_size_of = "Rc"] pub constructor: Rc, + + pub callbacks: LifecycleCallbacks, } impl CustomElementDefinition { - fn new(name: LocalName, local_name: LocalName, constructor: Rc) -> CustomElementDefinition { + fn new(name: LocalName, local_name: LocalName, constructor: Rc, callbacks: LifecycleCallbacks) -> CustomElementDefinition { CustomElementDefinition { name: name, local_name: local_name, constructor: constructor, + callbacks: callbacks, } } diff --git a/tests/wpt/metadata/custom-elements/CustomElementRegistry.html.ini b/tests/wpt/metadata/custom-elements/CustomElementRegistry.html.ini index 72117493595..cef53cf203f 100644 --- a/tests/wpt/metadata/custom-elements/CustomElementRegistry.html.ini +++ b/tests/wpt/metadata/custom-elements/CustomElementRegistry.html.ini @@ -1,14 +1,5 @@ [CustomElementRegistry.html] type: testharness - [customElements.define must get callbacks of the constructor prototype] - expected: FAIL - - [customElements.define must rethrow an exception thrown while getting callbacks on the constructor prototype] - expected: FAIL - - [customElements.define must rethrow an exception thrown while converting a callback value to Function callback type] - expected: FAIL - [customElements.define must get "observedAttributes" property on the constructor prototype when "attributeChangedCallback" is present] expected: FAIL diff --git a/tests/wpt/metadata/custom-elements/custom-element-registry/define.html.ini b/tests/wpt/metadata/custom-elements/custom-element-registry/define.html.ini index a04a32d8f5e..14558fa24b2 100644 --- a/tests/wpt/metadata/custom-elements/custom-element-registry/define.html.ini +++ b/tests/wpt/metadata/custom-elements/custom-element-registry/define.html.ini @@ -3,51 +3,3 @@ [If constructor is arrow function, should throw a TypeError] expected: FAIL - [If constructor.prototype.connectedCallback throws, should rethrow] - expected: FAIL - - [If constructor.prototype.connectedCallback is null, should throw a TypeError] - expected: FAIL - - [If constructor.prototype.connectedCallback is object, should throw a TypeError] - expected: FAIL - - [If constructor.prototype.connectedCallback is integer, should throw a TypeError] - expected: FAIL - - [If constructor.prototype.disconnectedCallback throws, should rethrow] - expected: FAIL - - [If constructor.prototype.disconnectedCallback is null, should throw a TypeError] - expected: FAIL - - [If constructor.prototype.disconnectedCallback is object, should throw a TypeError] - expected: FAIL - - [If constructor.prototype.disconnectedCallback is integer, should throw a TypeError] - expected: FAIL - - [If constructor.prototype.adoptedCallback throws, should rethrow] - expected: FAIL - - [If constructor.prototype.adoptedCallback is null, should throw a TypeError] - expected: FAIL - - [If constructor.prototype.adoptedCallback is object, should throw a TypeError] - expected: FAIL - - [If constructor.prototype.adoptedCallback is integer, should throw a TypeError] - expected: FAIL - - [If constructor.prototype.attributeChangedCallback throws, should rethrow] - expected: FAIL - - [If constructor.prototype.attributeChangedCallback is null, should throw a TypeError] - expected: FAIL - - [If constructor.prototype.attributeChangedCallback is object, should throw a TypeError] - expected: FAIL - - [If constructor.prototype.attributeChangedCallback is integer, should throw a TypeError] - expected: FAIL -