diff --git a/components/script/dom/htmloptionelement.rs b/components/script/dom/htmloptionelement.rs index 60af71b6c2e..8c7dea8ac1e 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,21 @@ 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 pick_if_selected_and_reset(&self) { + if let Some(select) = self.upcast::().ancestors() + .filter_map(Root::downcast::) + .next() { + if self.Selected() { + select.pick_option(self); + } + select.ask_for_reset(); + } + } } fn collect_text(element: &Element, value: &mut DOMString) { @@ -134,8 +150,7 @@ 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 + self.pick_if_selected_and_reset(); } } @@ -187,6 +202,8 @@ impl VirtualMethods for HTMLOptionElement { } self.upcast::().check_parent_disabled_state_for_option(); + + self.pick_if_selected_and_reset(); } fn unbind_from_tree(&self, tree_in_doc: bool) { diff --git a/components/script/dom/htmlselectelement.rs b/components/script/dom/htmlselectelement.rs index f82eddf39fe..04a3d174108 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,64 @@ 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 let Some(last_selected) = last_selected { + last_selected.set_selectedness(true); + } else { + if self.display_size() == 1 { + if let Some(first_enabled) = first_enabled { + first_enabled.set_selectedness(true); + } + } + } + } + + // https://html.spec.whatwg.org/multipage/#concept-select-pick + pub fn pick_option(&self, picked: &HTMLOptionElement) { + if !self.Multiple() { + let node = self.upcast::(); + let picked = picked.upcast(); + for opt in node.traverse_preorder().filter_map(Root::downcast::) { + if opt.upcast::() != picked { + opt.set_selectedness(false); + } + } + } + } + + // 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/metadata/html/semantics/forms/the-select-element/select-ask-for-reset.html.ini b/tests/wpt/metadata/html/semantics/forms/the-select-element/select-ask-for-reset.html.ini new file mode 100644 index 00000000000..f858a12d101 --- /dev/null +++ b/tests/wpt/metadata/html/semantics/forms/the-select-element/select-ask-for-reset.html.ini @@ -0,0 +1,4 @@ +[select-ask-for-reset.html] + type: testharness + [ask for reset on node remove, non multiple.] + expected: FAIL 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..822114fb265 --- /dev/null +++ b/tests/wpt/web-platform-tests/html/semantics/forms/the-select-element/select-ask-for-reset.html @@ -0,0 +1,97 @@ + + +HTMLSelectElement ask for reset + + + +
+