mirror of
https://github.com/servo/servo.git
synced 2025-08-03 20:50:07 +01: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 dom_struct::dom_struct;
|
||||||
use html5ever::{LocalName, Prefix};
|
use html5ever::{LocalName, Prefix};
|
||||||
use js::conversions::ToJSValConvertible;
|
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::jsapi::{JS_GetProperty, JSAutoCompartment, JSContext};
|
||||||
use js::jsval::{JSVal, ObjectValue, UndefinedValue};
|
use js::jsval::{JSVal, ObjectValue, UndefinedValue};
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
|
@ -94,15 +94,14 @@ impl CustomElementRegistry {
|
||||||
/// https://html.spec.whatwg.org/multipage/#dom-customelementregistry-define
|
/// https://html.spec.whatwg.org/multipage/#dom-customelementregistry-define
|
||||||
/// Steps 10.1, 10.2
|
/// Steps 10.1, 10.2
|
||||||
#[allow(unsafe_code)]
|
#[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>();
|
let global_scope = self.window.upcast::<GlobalScope>();
|
||||||
rooted!(in(global_scope.get_cx()) let mut prototype = UndefinedValue());
|
|
||||||
unsafe {
|
unsafe {
|
||||||
// Step 10.1
|
// Step 10.1
|
||||||
if !JS_GetProperty(global_scope.get_cx(),
|
if !JS_GetProperty(global_scope.get_cx(),
|
||||||
constructor,
|
constructor,
|
||||||
b"prototype\0".as_ptr() as *const _,
|
b"prototype\0".as_ptr() as *const _,
|
||||||
prototype.handle_mut()) {
|
prototype) {
|
||||||
return Err(Error::JSFailed);
|
return Err(Error::JSFailed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,6 +112,45 @@ impl CustomElementRegistry {
|
||||||
}
|
}
|
||||||
Ok(())
|
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 {
|
impl CustomElementRegistryMethods for CustomElementRegistry {
|
||||||
|
@ -173,22 +211,38 @@ impl CustomElementRegistryMethods for CustomElementRegistry {
|
||||||
self.element_definition_is_running.set(true);
|
self.element_definition_is_running.set(true);
|
||||||
|
|
||||||
// Steps 10.1 - 10.2
|
// 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());
|
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
|
// Steps 10.3 - 10.4
|
||||||
// 10.3 - 10.4 Handle lifecycle callbacks
|
rooted!(in(global_scope.get_cx()) let proto_object = prototype.to_object());
|
||||||
// 10.5 - 10.6 Get observed attributes from the constructor
|
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);
|
self.element_definition_is_running.set(false);
|
||||||
result?;
|
|
||||||
|
|
||||||
// Step 11
|
// Step 11
|
||||||
let definition = CustomElementDefinition::new(name.clone(),
|
let definition = CustomElementDefinition::new(name.clone(),
|
||||||
local_name,
|
local_name,
|
||||||
constructor_);
|
constructor_,
|
||||||
|
callbacks);
|
||||||
|
|
||||||
// Step 12
|
// Step 12
|
||||||
self.definitions.borrow_mut().insert(name.clone(), Rc::new(definition));
|
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
|
/// https://html.spec.whatwg.org/multipage/#custom-element-definition
|
||||||
#[derive(HeapSizeOf, JSTraceable, Clone)]
|
#[derive(HeapSizeOf, JSTraceable, Clone)]
|
||||||
pub struct CustomElementDefinition {
|
pub struct CustomElementDefinition {
|
||||||
|
@ -263,14 +332,17 @@ pub struct CustomElementDefinition {
|
||||||
|
|
||||||
#[ignore_heap_size_of = "Rc"]
|
#[ignore_heap_size_of = "Rc"]
|
||||||
pub constructor: Rc<Function>,
|
pub constructor: Rc<Function>,
|
||||||
|
|
||||||
|
pub callbacks: LifecycleCallbacks,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CustomElementDefinition {
|
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 {
|
CustomElementDefinition {
|
||||||
name: name,
|
name: name,
|
||||||
local_name: local_name,
|
local_name: local_name,
|
||||||
constructor: constructor,
|
constructor: constructor,
|
||||||
|
callbacks: callbacks,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,5 @@
|
||||||
[CustomElementRegistry.html]
|
[CustomElementRegistry.html]
|
||||||
type: testharness
|
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]
|
[customElements.define must get "observedAttributes" property on the constructor prototype when "attributeChangedCallback" is present]
|
||||||
expected: FAIL
|
expected: FAIL
|
||||||
|
|
||||||
|
|
|
@ -3,51 +3,3 @@
|
||||||
[If constructor is arrow function, should throw a TypeError]
|
[If constructor is arrow function, should throw a TypeError]
|
||||||
expected: FAIL
|
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