diff --git a/components/script/dom/htmlformcontrolscollection.rs b/components/script/dom/htmlformcontrolscollection.rs
index be6c72b2d2e..734878d75c4 100644
--- a/components/script/dom/htmlformcontrolscollection.rs
+++ b/components/script/dom/htmlformcontrolscollection.rs
@@ -5,13 +5,17 @@
use dom::bindings::codegen::Bindings::HTMLCollectionBinding::HTMLCollectionMethods;
use dom::bindings::codegen::Bindings::HTMLFormControlsCollectionBinding;
use dom::bindings::codegen::Bindings::HTMLFormControlsCollectionBinding::HTMLFormControlsCollectionMethods;
+use dom::bindings::codegen::UnionTypes::RadioNodeListOrElement::{self, eElement, eRadioNodeList};
use dom::bindings::global::GlobalRef;
+use dom::bindings::inheritance::Castable;
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::htmlcollection::{CollectionFilter, HTMLCollection};
use dom::node::Node;
+use dom::radionodelist::RadioNodeList;
use dom::window::Window;
+use std::iter;
use util::str::DOMString;
#[dom_struct]
@@ -43,8 +47,41 @@ impl HTMLFormControlsCollection {
impl HTMLFormControlsCollectionMethods for HTMLFormControlsCollection {
// https://html.spec.whatwg.org/multipage/#dom-htmlformcontrolscollection-nameditem
- fn NamedGetter(&self, name: DOMString, found: &mut bool) -> Option> {
- self.collection.NamedGetter(name, found)
+ fn NamedItem(&self, name: DOMString) -> Option {
+ // 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::(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 {
+ 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
diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs
index 0a9a7217c53..56693280f2f 100644
--- a/components/script/dom/mod.rs
+++ b/components/script/dom/mod.rs
@@ -341,6 +341,7 @@ pub mod performance;
pub mod performancetiming;
pub mod processinginstruction;
pub mod progressevent;
+pub mod radionodelist;
pub mod range;
pub mod screen;
pub mod servohtmlparser;
diff --git a/components/script/dom/nodelist.rs b/components/script/dom/nodelist.rs
index 6747ee5a948..2d1bbe363a7 100644
--- a/components/script/dom/nodelist.rs
+++ b/components/script/dom/nodelist.rs
@@ -28,7 +28,7 @@ pub struct NodeList {
impl NodeList {
#[allow(unrooted_must_root)]
- fn new_inherited(list_type: NodeListType) -> NodeList {
+ pub fn new_inherited(list_type: NodeListType) -> NodeList {
NodeList {
reflector_: Reflector::new(),
list_type: list_type,
@@ -36,14 +36,12 @@ impl NodeList {
}
#[allow(unrooted_must_root)]
- pub fn new(window: &Window,
- list_type: NodeListType) -> Root {
+ pub fn new(window: &Window, list_type: NodeListType) -> Root {
reflect_dom_object(box NodeList::new_inherited(list_type),
GlobalRef::Window(window), NodeListBinding::Wrap)
}
- pub fn new_simple_list(window: &Window, iter: T)
- -> Root
+ pub fn new_simple_list(window: &Window, iter: T) -> Root
where T: Iterator- > {
NodeList::new(window, NodeListType::Simple(iter.map(|r| JS::from_rooted(&r)).collect()))
}
@@ -93,6 +91,14 @@ impl NodeList {
panic!("called as_children_list() on a simple node list")
}
}
+
+ pub fn as_simple_list(&self) -> &Vec> {
+ if let NodeListType::Simple(ref list) = self.list_type {
+ list
+ } else {
+ panic!("called as_simple_list() on a children node list")
+ }
+ }
}
#[derive(JSTraceable, HeapSizeOf)]
@@ -105,7 +111,7 @@ pub struct ChildrenList {
}
impl ChildrenList {
- fn new(node: &Node) -> ChildrenList {
+ pub fn new(node: &Node) -> ChildrenList {
let last_visited = node.GetFirstChild();
ChildrenList {
node: JS::from_ref(node),
diff --git a/components/script/dom/radionodelist.rs b/components/script/dom/radionodelist.rs
new file mode 100644
index 00000000000..ae6ac13919d
--- /dev/null
+++ b/components/script/dom/radionodelist.rs
@@ -0,0 +1,111 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use dom::bindings::codegen::Bindings::HTMLInputElementBinding::HTMLInputElementMethods;
+use dom::bindings::codegen::Bindings::NodeListBinding::NodeListMethods;
+use dom::bindings::codegen::Bindings::RadioNodeListBinding;
+use dom::bindings::codegen::Bindings::RadioNodeListBinding::RadioNodeListMethods;
+use dom::bindings::global::GlobalRef;
+use dom::bindings::inheritance::Castable;
+use dom::bindings::js::{JS, Root};
+use dom::bindings::reflector::reflect_dom_object;
+use dom::htmlinputelement::HTMLInputElement;
+use dom::node::Node;
+use dom::nodelist::{NodeList, NodeListType};
+use dom::window::Window;
+use util::str::DOMString;
+
+#[dom_struct]
+pub struct RadioNodeList {
+ node_list: NodeList,
+}
+
+impl RadioNodeList {
+ #[allow(unrooted_must_root)]
+ fn new_inherited(list_type: NodeListType) -> RadioNodeList {
+ RadioNodeList {
+ node_list: NodeList::new_inherited(list_type)
+ }
+ }
+
+ #[allow(unrooted_must_root)]
+ pub fn new(window: &Window, list_type: NodeListType) -> Root {
+ reflect_dom_object(box RadioNodeList::new_inherited(list_type),
+ GlobalRef::Window(window),
+ RadioNodeListBinding::Wrap)
+ }
+
+ pub fn new_simple_list(window: &Window, iter: T) -> Root
+ where T: Iterator
- > {
+ RadioNodeList::new(window, NodeListType::Simple(iter.map(|r| JS::from_rooted(&r)).collect()))
+ }
+
+ pub fn empty(window: &Window) -> Root {
+ RadioNodeList::new(window, NodeListType::Simple(vec![]))
+ }
+
+ // FIXME: This shouldn't need to be implemented here since NodeList (the parent of
+ // RadioNodeList) implements Length
+ // https://github.com/servo/servo/issues/5875
+ pub fn Length(&self) -> u32 {
+ self.node_list.Length()
+ }
+}
+
+impl RadioNodeListMethods for RadioNodeList {
+ // https://html.spec.whatwg.org/multipage/#dom-radionodelist-value
+ fn Value(&self) -> DOMString {
+ self.upcast::().as_simple_list().iter().filter_map(|node| {
+ // Step 1
+ node.downcast::().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/#dom-radionodelist-value
+ fn SetValue(&self, value: DOMString) {
+ for node in self.upcast::().as_simple_list().iter() {
+ // Step 1
+ if let Some(input) = node.downcast::() {
+ match input.type_() {
+ atom!("radio") if value == DOMString::from("on") => {
+ // Step 2
+ let val = input.Value();
+ if val.is_empty() || val == value {
+ 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
+ // RadioNodeList) implements IndexedGetter.
+ // https://github.com/servo/servo/issues/5875
+ //
+ // https://dom.spec.whatwg.org/#dom-nodelist-item
+ fn IndexedGetter(&self, index: u32, found: &mut bool) -> Option> {
+ self.node_list.IndexedGetter(index, found)
+ }
+}
diff --git a/components/script/dom/webidls/HTMLFormControlsCollection.webidl b/components/script/dom/webidls/HTMLFormControlsCollection.webidl
index e7f2e84a566..0e81285575b 100644
--- a/components/script/dom/webidls/HTMLFormControlsCollection.webidl
+++ b/components/script/dom/webidls/HTMLFormControlsCollection.webidl
@@ -6,11 +6,5 @@
// https://html.spec.whatwg.org/multipage/#htmlformcontrolscollection
interface HTMLFormControlsCollection : HTMLCollection {
// inherits length and item()
- // getter (RadioNodeList or Element)? namedItem(DOMString name); // shadows inherited namedItem()
+ getter (RadioNodeList or Element)? namedItem(DOMString name); // shadows inherited namedItem()
};
-
-/*
-interface RadioNodeList : NodeList {
- attribute DOMString value;
-};
-*/
diff --git a/components/script/dom/webidls/NodeList.webidl b/components/script/dom/webidls/NodeList.webidl
index 9d40e9e19cf..413a7154606 100644
--- a/components/script/dom/webidls/NodeList.webidl
+++ b/components/script/dom/webidls/NodeList.webidl
@@ -8,8 +8,9 @@
*/
interface NodeList {
- [Pure]
- readonly attribute unsigned long length;
[Pure]
getter Node? item(unsigned long index);
+ [Pure]
+ readonly attribute unsigned long length;
+ // iterable;
};
diff --git a/components/script/dom/webidls/RadioNodeList.webidl b/components/script/dom/webidls/RadioNodeList.webidl
new file mode 100644
index 00000000000..9ab1d7ea5f0
--- /dev/null
+++ b/components/script/dom/webidls/RadioNodeList.webidl
@@ -0,0 +1,9 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// https://html.spec.whatwg.org/multipage/#radionodelist
+interface RadioNodeList : NodeList {
+ attribute DOMString value;
+};
diff --git a/tests/wpt/metadata/html/dom/interfaces.html.ini b/tests/wpt/metadata/html/dom/interfaces.html.ini
index ee818164602..31778f3116c 100644
--- a/tests/wpt/metadata/html/dom/interfaces.html.ini
+++ b/tests/wpt/metadata/html/dom/interfaces.html.ini
@@ -1359,9 +1359,6 @@
[HTMLCollection interface: calling namedItem(DOMString) on document.all with too few arguments must throw TypeError]
expected: FAIL
- [HTMLFormControlsCollection interface: operation namedItem(DOMString)]
- expected: FAIL
-
[HTMLFormControlsCollection must be primary interface of document.createElement("form").elements]
expected: FAIL
@@ -1389,21 +1386,6 @@
[HTMLCollection interface: calling namedItem(DOMString) on document.createElement("form").elements with too few arguments must throw TypeError]
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]
expected: FAIL
@@ -8934,9 +8916,6 @@
[HTMLAllCollection interface object name]
expected: FAIL
- [RadioNodeList interface object name]
- expected: FAIL
-
[HTMLOptionsCollection interface object name]
expected: FAIL
diff --git a/tests/wpt/metadata/html/infrastructure/common-dom-interfaces/collections/htmlformcontrolscollection.html.ini b/tests/wpt/metadata/html/infrastructure/common-dom-interfaces/collections/htmlformcontrolscollection.html.ini
index 5fb543d2eed..2daa58b237e 100644
--- a/tests/wpt/metadata/html/infrastructure/common-dom-interfaces/collections/htmlformcontrolscollection.html.ini
+++ b/tests/wpt/metadata/html/infrastructure/common-dom-interfaces/collections/htmlformcontrolscollection.html.ini
@@ -6,15 +6,9 @@
[HTMLFormControlsCollection(name) must return the named item]
expected: FAIL
- [The namedItem(name) must return RadioNodeList]
- expected: FAIL
-
[Controls can be indexed by id or name attribute]
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]
expected: FAIL
diff --git a/tests/wpt/metadata/html/infrastructure/common-dom-interfaces/collections/radionodelist.html.ini b/tests/wpt/metadata/html/infrastructure/common-dom-interfaces/collections/radionodelist.html.ini
index a4c1cf16d69..d1621994aa7 100644
--- a/tests/wpt/metadata/html/infrastructure/common-dom-interfaces/collections/radionodelist.html.ini
+++ b/tests/wpt/metadata/html/infrastructure/common-dom-interfaces/collections/radionodelist.html.ini
@@ -1,17 +1,5 @@
[radionodelist.html]
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']
expected: FAIL
diff --git a/tests/wpt/metadata/html/semantics/forms/the-form-element/form-elements-nameditem-01.html.ini b/tests/wpt/metadata/html/semantics/forms/the-form-element/form-elements-nameditem-01.html.ini
index b9425920944..84bdf90a6cb 100644
--- a/tests/wpt/metadata/html/semantics/forms/the-form-element/form-elements-nameditem-01.html.ini
+++ b/tests/wpt/metadata/html/semantics/forms/the-form-element/form-elements-nameditem-01.html.ini
@@ -1,8 +1,5 @@
[form-elements-nameditem-01.html]
type: testharness
- [RadioNodeList should exist]
- expected: FAIL
-
[elements collection should return elements or RadioNodeLists]
expected: FAIL
diff --git a/tests/wpt/mozilla/tests/mozilla/interfaces.html b/tests/wpt/mozilla/tests/mozilla/interfaces.html
index 2d63e0a60b2..b8a664c166f 100644
--- a/tests/wpt/mozilla/tests/mozilla/interfaces.html
+++ b/tests/wpt/mozilla/tests/mozilla/interfaces.html
@@ -193,6 +193,7 @@ var interfaceNamesInGlobalScope = [
"PerformanceTiming",
"ProcessingInstruction",
"ProgressEvent",
+ "RadioNodeList",
"Range",
"Screen",
"Storage",