Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement custom element reactions #17614

Merged
merged 4 commits into from Jul 18, 2017
Merged
Changes from 1 commit
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

Next

Store lifecycle callbacks when CEs are defined

This implements steps 10.3 - 10.4 of the CustomElementRegistry.define
steps.
  • Loading branch information
cbrewster committed Jul 13, 2017
commit 901a2028f13b3e21e93114ea8ee6e58436547f00
@@ -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

ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.