From b1d6b0f7970b18820b85385c0df85ced0ebc1b5e Mon Sep 17 00:00:00 2001 From: Dongie Agnir Date: Tue, 6 Oct 2015 17:38:39 -1000 Subject: [PATCH] Implement ask_for_reset for HTMLSelectElement. Fixes #7774 --- components/script/dom/htmloptionelement.rs | 11 ++- components/script/dom/htmlselectelement.rs | 49 +++++++++++++ tests/wpt/metadata/MANIFEST.json | 4 + .../select-ask-for-reset.html | 73 +++++++++++++++++++ 4 files changed, 135 insertions(+), 2 deletions(-) create mode 100644 tests/wpt/web-platform-tests/html/semantics/forms/the-select-element/select-ask-for-reset.html diff --git a/components/script/dom/htmloptionelement.rs b/components/script/dom/htmloptionelement.rs index 60af71b6c2e..c44b84c4c26 100644 --- a/components/script/dom/htmloptionelement.rs +++ b/components/script/dom/htmloptionelement.rs @@ -14,6 +14,7 @@ use dom::document::Document; use dom::element::{AttributeMutation, Element, IN_ENABLED_STATE}; use dom::htmlelement::HTMLElement; use dom::htmlscriptelement::HTMLScriptElement; +use dom::htmlselectelement::HTMLSelectElement; use dom::node::Node; use dom::text::Text; use dom::virtualmethods::VirtualMethods; @@ -51,6 +52,10 @@ impl HTMLOptionElement { let element = HTMLOptionElement::new_inherited(localName, prefix, document); Node::reflect_node(box element, document, HTMLOptionElementBinding::Wrap) } + + pub fn set_selectedness(&self, selected: bool) { + self.selectedness.set(selected); + } } fn collect_text(element: &Element, value: &mut DOMString) { @@ -134,8 +139,10 @@ impl HTMLOptionElementMethods for HTMLOptionElement { fn SetSelected(&self, selected: bool) { self.dirtiness.set(true); self.selectedness.set(selected); - // FIXME: as per the spec, implement 'ask for a reset' - // https://github.com/servo/servo/issues/7774 + if let Some(select) = self.upcast::().ancestors() + .filter_map(Root::downcast::).next() { + select.ask_for_reset(); + } } } diff --git a/components/script/dom/htmlselectelement.rs b/components/script/dom/htmlselectelement.rs index f82eddf39fe..d50ba64cc43 100644 --- a/components/script/dom/htmlselectelement.rs +++ b/components/script/dom/htmlselectelement.rs @@ -3,6 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use dom::attr::{Attr, AttrValue}; +use dom::bindings::codegen::Bindings::HTMLOptionElementBinding::HTMLOptionElementMethods; use dom::bindings::codegen::Bindings::HTMLSelectElementBinding; use dom::bindings::codegen::Bindings::HTMLSelectElementBinding::HTMLSelectElementMethods; use dom::bindings::codegen::UnionTypes::HTMLElementOrLong; @@ -14,6 +15,7 @@ use dom::element::{AttributeMutation, Element, IN_ENABLED_STATE}; use dom::htmlelement::HTMLElement; use dom::htmlfieldsetelement::HTMLFieldSetElement; use dom::htmlformelement::{FormControl, HTMLFormElement}; +use dom::htmloptionelement::HTMLOptionElement; use dom::node::{Node, window_from_node}; use dom::validitystate::ValidityState; use dom::virtualmethods::VirtualMethods; @@ -46,6 +48,53 @@ impl HTMLSelectElement { let element = HTMLSelectElement::new_inherited(localName, prefix, document); Node::reflect_node(box element, document, HTMLSelectElementBinding::Wrap) } + + // https://html.spec.whatwg.org/multipage/#ask-for-a-reset + pub fn ask_for_reset(&self) { + if self.Multiple() { + return; + } + + let mut first_enabled: Option> = None; + let mut last_selected: Option> = None; + + let node = self.upcast::(); + for opt in node.traverse_preorder().filter_map(Root::downcast::) { + if opt.Selected() { + opt.set_selectedness(false); + last_selected = Some(Root::from_ref(opt.r())); + } + let element = opt.upcast::(); + if first_enabled.is_none() && !element.get_disabled_state() { + first_enabled = Some(Root::from_ref(opt.r())); + } + } + + if last_selected.is_none() { + if self.display_size() == 1 { + // select the first enabled element + if let Some(first_opt) = first_enabled { + first_opt.set_selectedness(true); + } + } + } else { + // >= 1 selected, reselect last one + last_selected.unwrap().set_selectedness(true); + } + } + + // https://html.spec.whatwg.org/multipage/#concept-select-size + fn display_size(&self) -> u32 { + if self.Size() == 0 { + if self.Multiple() { + 4 + } else { + 1 + } + } else { + self.Size() + } + } } impl HTMLSelectElementMethods for HTMLSelectElement { diff --git a/tests/wpt/metadata/MANIFEST.json b/tests/wpt/metadata/MANIFEST.json index d2d65fe522c..cf966b53ed4 100644 --- a/tests/wpt/metadata/MANIFEST.json +++ b/tests/wpt/metadata/MANIFEST.json @@ -17917,6 +17917,10 @@ "path": "html/semantics/forms/the-select-element/select-remove.html", "url": "/html/semantics/forms/the-select-element/select-remove.html" }, + { + "path": "html/semantics/forms/the-select-element/select-ask-for-reset.html", + "url": "/html/semantics/forms/the-select-element/select-ask-for-reset.html" + }, { "path": "html/semantics/forms/the-textarea-element/textarea-type.html", "url": "/html/semantics/forms/the-textarea-element/textarea-type.html" diff --git a/tests/wpt/web-platform-tests/html/semantics/forms/the-select-element/select-ask-for-reset.html b/tests/wpt/web-platform-tests/html/semantics/forms/the-select-element/select-ask-for-reset.html new file mode 100644 index 00000000000..fd7683f8cbe --- /dev/null +++ b/tests/wpt/web-platform-tests/html/semantics/forms/the-select-element/select-ask-for-reset.html @@ -0,0 +1,73 @@ + + +HTMLSelectElement ask for reset + + + +
+