Skip to content

Commit

Permalink
Implement "list of options" concept on HTMLSelectElement.
Browse files Browse the repository at this point in the history
Fixes #13763.
  • Loading branch information
frewsxcv committed Dec 1, 2016
1 parent f159b5c commit 51f9671
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 20 deletions.
65 changes: 45 additions & 20 deletions components/script/dom/htmlselectelement.rs
Expand Up @@ -31,6 +31,7 @@ use dom::validation::Validatable;
use dom::validitystate::{ValidityState, ValidationFlags};
use dom::virtualmethods::VirtualMethods;
use html5ever_atoms::LocalName;
use std::iter;
use style::attr::AttrValue;
use style::element_state::*;

Expand Down Expand Up @@ -84,10 +85,25 @@ impl HTMLSelectElement {
HTMLSelectElementBinding::Wrap)
}

// https://html.spec.whatwg.org/multipage/#concept-select-option-list
fn list_of_options(&self) -> impl Iterator<Item=Root<HTMLOptionElement>> {
self.upcast::<Node>()
.children()
.flat_map(|node| {
if node.is::<HTMLOptionElement>() {
let node = Root::downcast::<HTMLOptionElement>(node).unwrap();
Choice3::First(iter::once(node))
} else if node.is::<HTMLOptGroupElement>() {
Choice3::Second(node.children().filter_map(Root::downcast))
} else {
Choice3::Third(iter::empty())
}
})
}

// https://html.spec.whatwg.org/multipage/#the-select-element:concept-form-reset-control
pub fn reset(&self) {
let node = self.upcast::<Node>();
for opt in node.traverse_preorder().filter_map(Root::downcast::<HTMLOptionElement>) {
for opt in self.list_of_options() {
opt.set_selectedness(opt.DefaultSelected());
opt.set_dirtiness(false);
}
Expand All @@ -103,8 +119,7 @@ impl HTMLSelectElement {
let mut first_enabled: Option<Root<HTMLOptionElement>> = None;
let mut last_selected: Option<Root<HTMLOptionElement>> = None;

let node = self.upcast::<Node>();
for opt in node.traverse_preorder().filter_map(Root::downcast::<HTMLOptionElement>) {
for opt in self.list_of_options() {
if opt.Selected() {
opt.set_selectedness(false);
last_selected = Some(Root::from_ref(&opt));
Expand All @@ -127,11 +142,10 @@ impl HTMLSelectElement {
}

pub fn push_form_data(&self, data_set: &mut Vec<FormDatum>) {
let node = self.upcast::<Node>();
if self.Name().is_empty() {
return;
}
for opt in node.traverse_preorder().filter_map(Root::downcast::<HTMLOptionElement>) {
for opt in self.list_of_options() {
let element = opt.upcast::<Element>();
if opt.Selected() && element.enabled_state() {
data_set.push(FormDatum {
Expand All @@ -146,9 +160,8 @@ impl HTMLSelectElement {
// https://html.spec.whatwg.org/multipage/#concept-select-pick
pub fn pick_option(&self, picked: &HTMLOptionElement) {
if !self.Multiple() {
let node = self.upcast::<Node>();
let picked = picked.upcast();
for opt in node.traverse_preorder().filter_map(Root::downcast::<HTMLOptionElement>) {
for opt in self.list_of_options() {
if opt.upcast::<HTMLElement>() != picked {
opt.set_selectedness(false);
}
Expand Down Expand Up @@ -271,9 +284,7 @@ impl HTMLSelectElementMethods for HTMLSelectElement {

// https://html.spec.whatwg.org/multipage/#dom-select-value
fn Value(&self) -> DOMString {
self.upcast::<Node>()
.traverse_preorder()
.filter_map(Root::downcast::<HTMLOptionElement>)
self.list_of_options()
.filter(|opt_elem| opt_elem.Selected())
.map(|opt_elem| opt_elem.Value())
.next()
Expand All @@ -282,9 +293,7 @@ impl HTMLSelectElementMethods for HTMLSelectElement {

// https://html.spec.whatwg.org/multipage/#dom-select-value
fn SetValue(&self, value: DOMString) {
let mut opt_iter = self.upcast::<Node>()
.traverse_preorder()
.filter_map(Root::downcast::<HTMLOptionElement>);
let mut opt_iter = self.list_of_options();
// Reset until we find an <option> with a matching value
for opt in opt_iter.by_ref() {
if opt.Value() == value {
Expand All @@ -302,9 +311,7 @@ impl HTMLSelectElementMethods for HTMLSelectElement {

// https://html.spec.whatwg.org/multipage/#dom-select-selectedindex
fn SelectedIndex(&self) -> i32 {
self.upcast::<Node>()
.traverse_preorder()
.filter_map(Root::downcast::<HTMLOptionElement>)
self.list_of_options()
.enumerate()
.filter(|&(_, ref opt_elem)| opt_elem.Selected())
.map(|(i, _)| i as i32)
Expand All @@ -314,9 +321,7 @@ impl HTMLSelectElementMethods for HTMLSelectElement {

// https://html.spec.whatwg.org/multipage/#dom-select-selectedindex
fn SetSelectedIndex(&self, index: i32) {
let mut opt_iter = self.upcast::<Node>()
.traverse_preorder()
.filter_map(Root::downcast::<HTMLOptionElement>);
let mut opt_iter = self.list_of_options();
for opt in opt_iter.by_ref().take(index as usize) {
opt.set_selectedness(false);
}
Expand Down Expand Up @@ -394,3 +399,23 @@ impl Validatable for HTMLSelectElement {
true
}
}

enum Choice3<I, J, K> {
First(I),
Second(J),
Third(K),
}

impl<I, J, K, T> Iterator for Choice3<I, J, K>
where I: Iterator<Item=T>, J: Iterator<Item=T>, K: Iterator<Item=T>
{
type Item = T;

fn next(&mut self) -> Option<T> {
match *self {
Choice3::First(ref mut i) => i.next(),
Choice3::Second(ref mut j) => j.next(),
Choice3::Third(ref mut k) => k.next(),
}
}
}
6 changes: 6 additions & 0 deletions tests/wpt/metadata/MANIFEST.json
Expand Up @@ -39689,6 +39689,12 @@
"timeout": "long",
"url": "/html/semantics/forms/form-submission-0/submit-entity-body.html"
}
],
"html/semantics/forms/the-select-element/select-value.html": [
{
"path": "html/semantics/forms/the-select-element/select-value.html",
"url": "/html/semantics/forms/the-select-element/select-value.html"
}
]
}
},
Expand Down
@@ -0,0 +1,56 @@
<!doctype html>
<meta charset="utf-8">
<title>HTMLSelectElement.value</title>
<link rel="help" href="https://html.spec.whatwg.org/multipage/forms.html#dom-select-value">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<div id=log></div>

<select id=sel1>
<option value=0></option>
<option selected value=1></option>
</select>

<select id=sel2>
<optgroup>
<option value=0></option>
</optgroup>
<optgroup></optgroup>
<optgroup>
<option></option>
<option value=1></option>
<option selected value=2></option>
</optgroup>
</select>

<select id=sel3>
<option selected value=1></option>
</select>

<select id=sel4></select>

<script>
test(function() {
var select = document.getElementById('sel1');
assert_equals(select.value, '1');
}, 'options');

test(function() {
var select = document.getElementById('sel2');
assert_equals(select.value, '2');
}, 'optgroups');

test(function() {
var select = document.getElementById('sel3');
var option = select.options[0];
var div = document.createElement('div');
select.appendChild(div);
div.appendChild(option);
assert_equals(select.value, '');
}, 'option is child of div');

test(function() {
var select = document.getElementById('sel4');
assert_equals(select.value, '');
}, 'no options');
</script>

0 comments on commit 51f9671

Please sign in to comment.