mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
Store lifecycle callbacks when CEs are defined
This implements steps 10.3 - 10.4 of the CustomElementRegistry.define steps.
This commit is contained in:
parent
347cbb0635
commit
901a2028f1
3 changed files with 84 additions and 69 deletions
|
@ -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::<GlobalScope>();
|
||||
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<LifecycleCallbacks> {
|
||||
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<Option<Rc<Function>>> {
|
||||
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<Rc<Function>>,
|
||||
|
||||
#[ignore_heap_size_of = "Rc"]
|
||||
disconnected_callback: Option<Rc<Function>>,
|
||||
|
||||
#[ignore_heap_size_of = "Rc"]
|
||||
adopted_callback: Option<Rc<Function>>,
|
||||
|
||||
#[ignore_heap_size_of = "Rc"]
|
||||
attribute_changed_callback: Option<Rc<Function>>,
|
||||
}
|
||||
|
||||
/// 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<Function>,
|
||||
|
||||
pub callbacks: LifecycleCallbacks,
|
||||
}
|
||||
|
||||
impl CustomElementDefinition {
|
||||
fn new(name: LocalName, local_name: LocalName, constructor: Rc<Function>) -> CustomElementDefinition {
|
||||
fn new(name: LocalName, local_name: LocalName, constructor: Rc<Function>, callbacks: LifecycleCallbacks) -> CustomElementDefinition {
|
||||
CustomElementDefinition {
|
||||
name: name,
|
||||
local_name: local_name,
|
||||
constructor: constructor,
|
||||
callbacks: callbacks,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue