From 9e3af70941c74eed41f47ff13d0fc7cfd2300403 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Fri, 22 Jan 2016 03:24:28 -0500 Subject: [PATCH 1/2] Implement RadioNodeList --- components/script/dom/mod.rs | 1 + components/script/dom/nodelist.rs | 13 +- components/script/dom/radionodelist.rs | 152 ++++++++++++++++++ .../webidls/HTMLFormControlsCollection.webidl | 6 - components/script/dom/webidls/NodeList.webidl | 5 +- .../script/dom/webidls/RadioNodeList.webidl | 9 ++ 6 files changed, 175 insertions(+), 11 deletions(-) create mode 100644 components/script/dom/radionodelist.rs create mode 100644 components/script/dom/webidls/RadioNodeList.webidl 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..5987b1b26f0 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,8 +36,7 @@ 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) } @@ -93,6 +92,10 @@ impl NodeList { panic!("called as_children_list() on a simple node list") } } + + pub fn get_list_type(&self) -> &NodeListType { + &self.list_type + } } #[derive(JSTraceable, HeapSizeOf)] @@ -114,6 +117,10 @@ impl ChildrenList { } } + pub fn get_parent_node(&self) -> &Node { + &*self.node + } + pub fn len(&self) -> u32 { self.node.children_count() } diff --git a/components/script/dom/radionodelist.rs b/components/script/dom/radionodelist.rs new file mode 100644 index 00000000000..ebb9909f1d6 --- /dev/null +++ b/components/script/dom/radionodelist.rs @@ -0,0 +1,152 @@ +/* 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::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) + } + + // 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 { + match *self.upcast::().get_list_type() { + NodeListType::Simple(ref v) => { + v.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("")) + } + NodeListType::Children(ref cl) => { + cl.get_parent_node().traverse_preorder().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/infrastructure.html#dom-radionodelist-value + fn SetValue(&self, value: DOMString) { + match *self.upcast::().get_list_type() { + NodeListType::Simple(ref v) => { + for node in v.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 == DOMString::from("on") { + input.SetChecked(true); + return; + } + } + atom!("radio") => { + // Step 2 + if input.Value() == value { + input.SetChecked(true); + return; + } + } + _ => {} + } + } + } + } + NodeListType::Children(ref cl) => { + for node in cl.get_parent_node().traverse_preorder() { + // 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 == 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 + // 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..c4a26c7b20c 100644 --- a/components/script/dom/webidls/HTMLFormControlsCollection.webidl +++ b/components/script/dom/webidls/HTMLFormControlsCollection.webidl @@ -8,9 +8,3 @@ interface HTMLFormControlsCollection : HTMLCollection { // inherits length and item() // 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..d3c1ed892dc --- /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; +}; From 4229b68062a603a14a63952c0e010de3b0145c15 Mon Sep 17 00:00:00 2001 From: Keith Yeung Date: Fri, 22 Jan 2016 22:44:48 -0500 Subject: [PATCH 2/2] Implement NamedItem and NamedGetter on HTMLFormControlsCollection --- .../script/dom/htmlformcontrolscollection.rs | 43 +++++- components/script/dom/nodelist.rs | 17 ++- components/script/dom/radionodelist.rs | 127 ++++++------------ .../webidls/HTMLFormControlsCollection.webidl | 2 +- .../script/dom/webidls/RadioNodeList.webidl | 2 +- .../wpt/metadata/html/dom/interfaces.html.ini | 21 --- .../htmlformcontrolscollection.html.ini | 6 - .../collections/radionodelist.html.ini | 12 -- .../form-elements-nameditem-01.html.ini | 3 - .../wpt/mozilla/tests/mozilla/interfaces.html | 1 + 10 files changed, 94 insertions(+), 140 deletions(-) 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/nodelist.rs b/components/script/dom/nodelist.rs index 5987b1b26f0..2d1bbe363a7 100644 --- a/components/script/dom/nodelist.rs +++ b/components/script/dom/nodelist.rs @@ -41,8 +41,7 @@ impl NodeList { 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,8 +92,12 @@ impl NodeList { } } - pub fn get_list_type(&self) -> &NodeListType { - &self.list_type + 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") + } } } @@ -108,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), @@ -117,10 +120,6 @@ impl ChildrenList { } } - pub fn get_parent_node(&self) -> &Node { - &*self.node - } - pub fn len(&self) -> u32 { self.node.children_count() } diff --git a/components/script/dom/radionodelist.rs b/components/script/dom/radionodelist.rs index ebb9909f1d6..ae6ac13919d 100644 --- a/components/script/dom/radionodelist.rs +++ b/components/script/dom/radionodelist.rs @@ -8,7 +8,7 @@ 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::Root; +use dom::bindings::js::{JS, Root}; use dom::bindings::reflector::reflect_dom_object; use dom::htmlinputelement::HTMLInputElement; use dom::node::Node; @@ -36,6 +36,15 @@ impl RadioNodeList { 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 @@ -47,98 +56,48 @@ impl RadioNodeList { impl RadioNodeListMethods for RadioNodeList { // https://html.spec.whatwg.org/multipage/#dom-radionodelist-value fn Value(&self) -> DOMString { - match *self.upcast::().get_list_type() { - NodeListType::Simple(ref v) => { - v.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("")) - } - NodeListType::Children(ref cl) => { - cl.get_parent_node().traverse_preorder().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("")) - } - } + 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/infrastructure.html#dom-radionodelist-value + // https://html.spec.whatwg.org/multipage/#dom-radionodelist-value fn SetValue(&self, value: DOMString) { - match *self.upcast::().get_list_type() { - NodeListType::Simple(ref v) => { - for node in v.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 == DOMString::from("on") { - input.SetChecked(true); - return; - } - } - atom!("radio") => { - // Step 2 - if input.Value() == value { - input.SetChecked(true); - return; - } - } - _ => {} + 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; } } - } - } - NodeListType::Children(ref cl) => { - for node in cl.get_parent_node().traverse_preorder() { - // 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 == DOMString::from("on") { - input.SetChecked(true); - return; - } - } - atom!("radio") => { - // Step 2 - if input.Value() == 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 diff --git a/components/script/dom/webidls/HTMLFormControlsCollection.webidl b/components/script/dom/webidls/HTMLFormControlsCollection.webidl index c4a26c7b20c..0e81285575b 100644 --- a/components/script/dom/webidls/HTMLFormControlsCollection.webidl +++ b/components/script/dom/webidls/HTMLFormControlsCollection.webidl @@ -6,5 +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() }; diff --git a/components/script/dom/webidls/RadioNodeList.webidl b/components/script/dom/webidls/RadioNodeList.webidl index d3c1ed892dc..9ab1d7ea5f0 100644 --- a/components/script/dom/webidls/RadioNodeList.webidl +++ b/components/script/dom/webidls/RadioNodeList.webidl @@ -5,5 +5,5 @@ // https://html.spec.whatwg.org/multipage/#radionodelist interface RadioNodeList : NodeList { - attribute DOMString value; + attribute DOMString value; }; diff --git a/tests/wpt/metadata/html/dom/interfaces.html.ini b/tests/wpt/metadata/html/dom/interfaces.html.ini index 02806e334df..d4e5846bc89 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 @@ -8946,9 +8928,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",