Implement NamedItem and NamedGetter on HTMLFormControlsCollection

This commit is contained in:
Keith Yeung 2016-01-22 22:44:48 -05:00
parent 9e3af70941
commit 4229b68062
10 changed files with 94 additions and 140 deletions

View file

@ -5,13 +5,17 @@
use dom::bindings::codegen::Bindings::HTMLCollectionBinding::HTMLCollectionMethods; use dom::bindings::codegen::Bindings::HTMLCollectionBinding::HTMLCollectionMethods;
use dom::bindings::codegen::Bindings::HTMLFormControlsCollectionBinding; use dom::bindings::codegen::Bindings::HTMLFormControlsCollectionBinding;
use dom::bindings::codegen::Bindings::HTMLFormControlsCollectionBinding::HTMLFormControlsCollectionMethods; use dom::bindings::codegen::Bindings::HTMLFormControlsCollectionBinding::HTMLFormControlsCollectionMethods;
use dom::bindings::codegen::UnionTypes::RadioNodeListOrElement::{self, eElement, eRadioNodeList};
use dom::bindings::global::GlobalRef; use dom::bindings::global::GlobalRef;
use dom::bindings::inheritance::Castable;
use dom::bindings::js::Root; use dom::bindings::js::Root;
use dom::bindings::reflector::reflect_dom_object; use dom::bindings::reflector::{Reflectable, reflect_dom_object};
use dom::element::Element; use dom::element::Element;
use dom::htmlcollection::{CollectionFilter, HTMLCollection}; use dom::htmlcollection::{CollectionFilter, HTMLCollection};
use dom::node::Node; use dom::node::Node;
use dom::radionodelist::RadioNodeList;
use dom::window::Window; use dom::window::Window;
use std::iter;
use util::str::DOMString; use util::str::DOMString;
#[dom_struct] #[dom_struct]
@ -43,8 +47,41 @@ impl HTMLFormControlsCollection {
impl HTMLFormControlsCollectionMethods for HTMLFormControlsCollection { impl HTMLFormControlsCollectionMethods for HTMLFormControlsCollection {
// https://html.spec.whatwg.org/multipage/#dom-htmlformcontrolscollection-nameditem // https://html.spec.whatwg.org/multipage/#dom-htmlformcontrolscollection-nameditem
fn NamedGetter(&self, name: DOMString, found: &mut bool) -> Option<Root<Element>> { fn NamedItem(&self, name: DOMString) -> Option<RadioNodeListOrElement> {
self.collection.NamedGetter(name, found) // Step 1
if name.is_empty() { return None; }
let mut filter_map = self.collection.elements_iter().filter_map(|elem| {
if elem.get_string_attribute(&atom!("name")) == name
|| elem.get_string_attribute(&atom!("id")) == name {
Some(elem)
} else { None }
});
if let Some(elem) = filter_map.next() {
let mut peekable = filter_map.peekable();
// Step 2
if peekable.peek().is_none() {
Some(eElement(elem))
} else {
// Step 4-5
let once = iter::once(Root::upcast::<Node>(elem));
let list = once.chain(peekable.map(Root::upcast));
let global = self.global();
let global = global.r();
let window = global.as_window();
Some(eRadioNodeList(RadioNodeList::new_simple_list(window, list)))
}
// Step 3
} else { None }
}
// https://html.spec.whatwg.org/multipage/#dom-htmlformcontrolscollection-nameditem
fn NamedGetter(&self, name: DOMString, found: &mut bool) -> Option<RadioNodeListOrElement> {
let maybe_elem = self.NamedItem(name);
*found = maybe_elem.is_some();
maybe_elem
} }
// https://html.spec.whatwg.org/multipage/#the-htmlformcontrolscollection-interface:supported-property-names // https://html.spec.whatwg.org/multipage/#the-htmlformcontrolscollection-interface:supported-property-names

View file

@ -41,8 +41,7 @@ impl NodeList {
GlobalRef::Window(window), NodeListBinding::Wrap) GlobalRef::Window(window), NodeListBinding::Wrap)
} }
pub fn new_simple_list<T>(window: &Window, iter: T) pub fn new_simple_list<T>(window: &Window, iter: T) -> Root<NodeList>
-> Root<NodeList>
where T: Iterator<Item=Root<Node>> { where T: Iterator<Item=Root<Node>> {
NodeList::new(window, NodeListType::Simple(iter.map(|r| JS::from_rooted(&r)).collect())) NodeList::new(window, NodeListType::Simple(iter.map(|r| JS::from_rooted(&r)).collect()))
} }
@ -93,8 +92,12 @@ impl NodeList {
} }
} }
pub fn get_list_type(&self) -> &NodeListType { pub fn as_simple_list(&self) -> &Vec<JS<Node>> {
&self.list_type if let NodeListType::Simple(ref list) = self.list_type {
list
} else {
panic!("called as_simple_list() on a children node list")
}
} }
} }
@ -108,7 +111,7 @@ pub struct ChildrenList {
} }
impl ChildrenList { impl ChildrenList {
fn new(node: &Node) -> ChildrenList { pub fn new(node: &Node) -> ChildrenList {
let last_visited = node.GetFirstChild(); let last_visited = node.GetFirstChild();
ChildrenList { ChildrenList {
node: JS::from_ref(node), node: JS::from_ref(node),
@ -117,10 +120,6 @@ impl ChildrenList {
} }
} }
pub fn get_parent_node(&self) -> &Node {
&*self.node
}
pub fn len(&self) -> u32 { pub fn len(&self) -> u32 {
self.node.children_count() self.node.children_count()
} }

View file

@ -8,7 +8,7 @@ use dom::bindings::codegen::Bindings::RadioNodeListBinding;
use dom::bindings::codegen::Bindings::RadioNodeListBinding::RadioNodeListMethods; use dom::bindings::codegen::Bindings::RadioNodeListBinding::RadioNodeListMethods;
use dom::bindings::global::GlobalRef; use dom::bindings::global::GlobalRef;
use dom::bindings::inheritance::Castable; use dom::bindings::inheritance::Castable;
use dom::bindings::js::Root; use dom::bindings::js::{JS, Root};
use dom::bindings::reflector::reflect_dom_object; use dom::bindings::reflector::reflect_dom_object;
use dom::htmlinputelement::HTMLInputElement; use dom::htmlinputelement::HTMLInputElement;
use dom::node::Node; use dom::node::Node;
@ -36,6 +36,15 @@ impl RadioNodeList {
RadioNodeListBinding::Wrap) RadioNodeListBinding::Wrap)
} }
pub fn new_simple_list<T>(window: &Window, iter: T) -> Root<RadioNodeList>
where T: Iterator<Item=Root<Node>> {
RadioNodeList::new(window, NodeListType::Simple(iter.map(|r| JS::from_rooted(&r)).collect()))
}
pub fn empty(window: &Window) -> Root<RadioNodeList> {
RadioNodeList::new(window, NodeListType::Simple(vec![]))
}
// FIXME: This shouldn't need to be implemented here since NodeList (the parent of // FIXME: This shouldn't need to be implemented here since NodeList (the parent of
// RadioNodeList) implements Length // RadioNodeList) implements Length
// https://github.com/servo/servo/issues/5875 // https://github.com/servo/servo/issues/5875
@ -47,98 +56,48 @@ impl RadioNodeList {
impl RadioNodeListMethods for RadioNodeList { impl RadioNodeListMethods for RadioNodeList {
// https://html.spec.whatwg.org/multipage/#dom-radionodelist-value // https://html.spec.whatwg.org/multipage/#dom-radionodelist-value
fn Value(&self) -> DOMString { fn Value(&self) -> DOMString {
match *self.upcast::<NodeList>().get_list_type() { self.upcast::<NodeList>().as_simple_list().iter().filter_map(|node| {
NodeListType::Simple(ref v) => { // Step 1
v.iter().filter_map(|node| { node.downcast::<HTMLInputElement>().and_then(|input| {
// Step 1 match input.type_() {
node.downcast::<HTMLInputElement>().and_then(|input| { atom!("radio") if input.Checked() => {
match input.type_() { // Step 3-4
atom!("radio") if input.Checked() => { let value = input.Value();
// Step 3-4 Some(if value.is_empty() { DOMString::from("on") } else { value })
let value = input.Value(); }
Some(if value.is_empty() { DOMString::from("on") } else { value }) _ => None
} }
_ => None })
} }).next()
}) // Step 2
}).next() .unwrap_or(DOMString::from(""))
// Step 2
.unwrap_or(DOMString::from(""))
}
NodeListType::Children(ref cl) => {
cl.get_parent_node().traverse_preorder().filter_map(|node| {
// Step 1
node.downcast::<HTMLInputElement>().and_then(|input| {
match input.type_() {
atom!("radio") if input.Checked() => {
// Step 3-4
let value = input.Value();
Some(if value.is_empty() { DOMString::from("on") } else { value })
}
_ => None
}
})
}).next()
// Step 2
.unwrap_or(DOMString::from(""))
}
}
} }
// https://html.spec.whatwg.org/multipage/infrastructure.html#dom-radionodelist-value // https://html.spec.whatwg.org/multipage/#dom-radionodelist-value
fn SetValue(&self, value: DOMString) { fn SetValue(&self, value: DOMString) {
match *self.upcast::<NodeList>().get_list_type() { for node in self.upcast::<NodeList>().as_simple_list().iter() {
NodeListType::Simple(ref v) => { // Step 1
for node in v.iter() { if let Some(input) = node.downcast::<HTMLInputElement>() {
// Step 1 match input.type_() {
if let Some(input) = node.downcast::<HTMLInputElement>() { atom!("radio") if value == DOMString::from("on") => {
match input.type_() { // Step 2
atom!("radio") if value == DOMString::from("on") => { let val = input.Value();
// Step 2 if val.is_empty() || val == value {
let val = input.Value(); input.SetChecked(true);
if val.is_empty() || val == DOMString::from("on") { return;
input.SetChecked(true);
return;
}
}
atom!("radio") => {
// Step 2
if input.Value() == value {
input.SetChecked(true);
return;
}
}
_ => {}
} }
} }
} atom!("radio") => {
} // Step 2
NodeListType::Children(ref cl) => { if input.Value() == value {
for node in cl.get_parent_node().traverse_preorder() { input.SetChecked(true);
// Step 1 return;
if let Some(input) = node.downcast::<HTMLInputElement>() {
match input.type_() {
atom!("radio") if value == DOMString::from("on") => {
// Step 2
let val = input.Value();
if val.is_empty() || val == DOMString::from("on") {
input.SetChecked(true);
return;
}
}
atom!("radio") => {
// Step 2
if input.Value() == value {
input.SetChecked(true);
return;
}
}
_ => {}
} }
} }
_ => {}
} }
} }
}; }
} }
// FIXME: This shouldn't need to be implemented here since NodeList (the parent of // FIXME: This shouldn't need to be implemented here since NodeList (the parent of

View file

@ -6,5 +6,5 @@
// https://html.spec.whatwg.org/multipage/#htmlformcontrolscollection // https://html.spec.whatwg.org/multipage/#htmlformcontrolscollection
interface HTMLFormControlsCollection : HTMLCollection { interface HTMLFormControlsCollection : HTMLCollection {
// inherits length and item() // inherits length and item()
// getter (RadioNodeList or Element)? namedItem(DOMString name); // shadows inherited namedItem() getter (RadioNodeList or Element)? namedItem(DOMString name); // shadows inherited namedItem()
}; };

View file

@ -5,5 +5,5 @@
// https://html.spec.whatwg.org/multipage/#radionodelist // https://html.spec.whatwg.org/multipage/#radionodelist
interface RadioNodeList : NodeList { interface RadioNodeList : NodeList {
attribute DOMString value; attribute DOMString value;
}; };

View file

@ -1359,9 +1359,6 @@
[HTMLCollection interface: calling namedItem(DOMString) on document.all with too few arguments must throw TypeError] [HTMLCollection interface: calling namedItem(DOMString) on document.all with too few arguments must throw TypeError]
expected: FAIL expected: FAIL
[HTMLFormControlsCollection interface: operation namedItem(DOMString)]
expected: FAIL
[HTMLFormControlsCollection must be primary interface of document.createElement("form").elements] [HTMLFormControlsCollection must be primary interface of document.createElement("form").elements]
expected: FAIL expected: FAIL
@ -1389,21 +1386,6 @@
[HTMLCollection interface: calling namedItem(DOMString) on document.createElement("form").elements with too few arguments must throw TypeError] [HTMLCollection interface: calling namedItem(DOMString) on document.createElement("form").elements with too few arguments must throw TypeError]
expected: FAIL expected: FAIL
[RadioNodeList interface: existence and properties of interface object]
expected: FAIL
[RadioNodeList interface object length]
expected: FAIL
[RadioNodeList interface: existence and properties of interface prototype object]
expected: FAIL
[RadioNodeList interface: existence and properties of interface prototype object's "constructor" property]
expected: FAIL
[RadioNodeList interface: attribute value]
expected: FAIL
[HTMLOptionsCollection interface: existence and properties of interface object] [HTMLOptionsCollection interface: existence and properties of interface object]
expected: FAIL expected: FAIL
@ -8946,9 +8928,6 @@
[HTMLAllCollection interface object name] [HTMLAllCollection interface object name]
expected: FAIL expected: FAIL
[RadioNodeList interface object name]
expected: FAIL
[HTMLOptionsCollection interface object name] [HTMLOptionsCollection interface object name]
expected: FAIL expected: FAIL

View file

@ -6,15 +6,9 @@
[HTMLFormControlsCollection(name) must return the named item] [HTMLFormControlsCollection(name) must return the named item]
expected: FAIL expected: FAIL
[The namedItem(name) must return RadioNodeList]
expected: FAIL
[Controls can be indexed by id or name attribute] [Controls can be indexed by id or name attribute]
expected: FAIL expected: FAIL
[The namedItem(name) must return the items with id or name attribute]
expected: FAIL
[The HTMLFormControlsCollection interface is used for collections of listed elements in form element] [The HTMLFormControlsCollection interface is used for collections of listed elements in form element]
expected: FAIL expected: FAIL

View file

@ -1,17 +1,5 @@
[radionodelist.html] [radionodelist.html]
type: testharness type: testharness
[The value attribute should be empty if no element is checked]
expected: FAIL
[The RadioNodeList.value must be the first checked radio button's value]
expected: FAIL
[Check the RadioNodeList.value on getting]
expected: FAIL
[Check the RadioNodeList.value on setting]
expected: FAIL
[Check the RadioNodeList.value on setting to 'on'] [Check the RadioNodeList.value on setting to 'on']
expected: FAIL expected: FAIL

View file

@ -1,8 +1,5 @@
[form-elements-nameditem-01.html] [form-elements-nameditem-01.html]
type: testharness type: testharness
[RadioNodeList should exist]
expected: FAIL
[elements collection should return elements or RadioNodeLists] [elements collection should return elements or RadioNodeLists]
expected: FAIL expected: FAIL

View file

@ -193,6 +193,7 @@ var interfaceNamesInGlobalScope = [
"PerformanceTiming", "PerformanceTiming",
"ProcessingInstruction", "ProcessingInstruction",
"ProgressEvent", "ProgressEvent",
"RadioNodeList",
"Range", "Range",
"Screen", "Screen",
"Storage", "Storage",