mirror of
https://github.com/servo/servo.git
synced 2025-06-25 01:24:37 +01:00
Implement "list of options" concept on HTMLSelectElement
.
Fixes https://github.com/servo/servo/issues/13763.
This commit is contained in:
parent
f159b5cb10
commit
51f9671db2
3 changed files with 107 additions and 20 deletions
|
@ -31,6 +31,7 @@ use dom::validation::Validatable;
|
||||||
use dom::validitystate::{ValidityState, ValidationFlags};
|
use dom::validitystate::{ValidityState, ValidationFlags};
|
||||||
use dom::virtualmethods::VirtualMethods;
|
use dom::virtualmethods::VirtualMethods;
|
||||||
use html5ever_atoms::LocalName;
|
use html5ever_atoms::LocalName;
|
||||||
|
use std::iter;
|
||||||
use style::attr::AttrValue;
|
use style::attr::AttrValue;
|
||||||
use style::element_state::*;
|
use style::element_state::*;
|
||||||
|
|
||||||
|
@ -84,10 +85,25 @@ impl HTMLSelectElement {
|
||||||
HTMLSelectElementBinding::Wrap)
|
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
|
// https://html.spec.whatwg.org/multipage/#the-select-element:concept-form-reset-control
|
||||||
pub fn reset(&self) {
|
pub fn reset(&self) {
|
||||||
let node = self.upcast::<Node>();
|
for opt in self.list_of_options() {
|
||||||
for opt in node.traverse_preorder().filter_map(Root::downcast::<HTMLOptionElement>) {
|
|
||||||
opt.set_selectedness(opt.DefaultSelected());
|
opt.set_selectedness(opt.DefaultSelected());
|
||||||
opt.set_dirtiness(false);
|
opt.set_dirtiness(false);
|
||||||
}
|
}
|
||||||
|
@ -103,8 +119,7 @@ impl HTMLSelectElement {
|
||||||
let mut first_enabled: Option<Root<HTMLOptionElement>> = None;
|
let mut first_enabled: Option<Root<HTMLOptionElement>> = None;
|
||||||
let mut last_selected: Option<Root<HTMLOptionElement>> = None;
|
let mut last_selected: Option<Root<HTMLOptionElement>> = None;
|
||||||
|
|
||||||
let node = self.upcast::<Node>();
|
for opt in self.list_of_options() {
|
||||||
for opt in node.traverse_preorder().filter_map(Root::downcast::<HTMLOptionElement>) {
|
|
||||||
if opt.Selected() {
|
if opt.Selected() {
|
||||||
opt.set_selectedness(false);
|
opt.set_selectedness(false);
|
||||||
last_selected = Some(Root::from_ref(&opt));
|
last_selected = Some(Root::from_ref(&opt));
|
||||||
|
@ -127,11 +142,10 @@ impl HTMLSelectElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push_form_data(&self, data_set: &mut Vec<FormDatum>) {
|
pub fn push_form_data(&self, data_set: &mut Vec<FormDatum>) {
|
||||||
let node = self.upcast::<Node>();
|
|
||||||
if self.Name().is_empty() {
|
if self.Name().is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for opt in node.traverse_preorder().filter_map(Root::downcast::<HTMLOptionElement>) {
|
for opt in self.list_of_options() {
|
||||||
let element = opt.upcast::<Element>();
|
let element = opt.upcast::<Element>();
|
||||||
if opt.Selected() && element.enabled_state() {
|
if opt.Selected() && element.enabled_state() {
|
||||||
data_set.push(FormDatum {
|
data_set.push(FormDatum {
|
||||||
|
@ -146,9 +160,8 @@ impl HTMLSelectElement {
|
||||||
// https://html.spec.whatwg.org/multipage/#concept-select-pick
|
// https://html.spec.whatwg.org/multipage/#concept-select-pick
|
||||||
pub fn pick_option(&self, picked: &HTMLOptionElement) {
|
pub fn pick_option(&self, picked: &HTMLOptionElement) {
|
||||||
if !self.Multiple() {
|
if !self.Multiple() {
|
||||||
let node = self.upcast::<Node>();
|
|
||||||
let picked = picked.upcast();
|
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 {
|
if opt.upcast::<HTMLElement>() != picked {
|
||||||
opt.set_selectedness(false);
|
opt.set_selectedness(false);
|
||||||
}
|
}
|
||||||
|
@ -271,9 +284,7 @@ impl HTMLSelectElementMethods for HTMLSelectElement {
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#dom-select-value
|
// https://html.spec.whatwg.org/multipage/#dom-select-value
|
||||||
fn Value(&self) -> DOMString {
|
fn Value(&self) -> DOMString {
|
||||||
self.upcast::<Node>()
|
self.list_of_options()
|
||||||
.traverse_preorder()
|
|
||||||
.filter_map(Root::downcast::<HTMLOptionElement>)
|
|
||||||
.filter(|opt_elem| opt_elem.Selected())
|
.filter(|opt_elem| opt_elem.Selected())
|
||||||
.map(|opt_elem| opt_elem.Value())
|
.map(|opt_elem| opt_elem.Value())
|
||||||
.next()
|
.next()
|
||||||
|
@ -282,9 +293,7 @@ impl HTMLSelectElementMethods for HTMLSelectElement {
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#dom-select-value
|
// https://html.spec.whatwg.org/multipage/#dom-select-value
|
||||||
fn SetValue(&self, value: DOMString) {
|
fn SetValue(&self, value: DOMString) {
|
||||||
let mut opt_iter = self.upcast::<Node>()
|
let mut opt_iter = self.list_of_options();
|
||||||
.traverse_preorder()
|
|
||||||
.filter_map(Root::downcast::<HTMLOptionElement>);
|
|
||||||
// Reset until we find an <option> with a matching value
|
// Reset until we find an <option> with a matching value
|
||||||
for opt in opt_iter.by_ref() {
|
for opt in opt_iter.by_ref() {
|
||||||
if opt.Value() == value {
|
if opt.Value() == value {
|
||||||
|
@ -302,9 +311,7 @@ impl HTMLSelectElementMethods for HTMLSelectElement {
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#dom-select-selectedindex
|
// https://html.spec.whatwg.org/multipage/#dom-select-selectedindex
|
||||||
fn SelectedIndex(&self) -> i32 {
|
fn SelectedIndex(&self) -> i32 {
|
||||||
self.upcast::<Node>()
|
self.list_of_options()
|
||||||
.traverse_preorder()
|
|
||||||
.filter_map(Root::downcast::<HTMLOptionElement>)
|
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.filter(|&(_, ref opt_elem)| opt_elem.Selected())
|
.filter(|&(_, ref opt_elem)| opt_elem.Selected())
|
||||||
.map(|(i, _)| i as i32)
|
.map(|(i, _)| i as i32)
|
||||||
|
@ -314,9 +321,7 @@ impl HTMLSelectElementMethods for HTMLSelectElement {
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#dom-select-selectedindex
|
// https://html.spec.whatwg.org/multipage/#dom-select-selectedindex
|
||||||
fn SetSelectedIndex(&self, index: i32) {
|
fn SetSelectedIndex(&self, index: i32) {
|
||||||
let mut opt_iter = self.upcast::<Node>()
|
let mut opt_iter = self.list_of_options();
|
||||||
.traverse_preorder()
|
|
||||||
.filter_map(Root::downcast::<HTMLOptionElement>);
|
|
||||||
for opt in opt_iter.by_ref().take(index as usize) {
|
for opt in opt_iter.by_ref().take(index as usize) {
|
||||||
opt.set_selectedness(false);
|
opt.set_selectedness(false);
|
||||||
}
|
}
|
||||||
|
@ -394,3 +399,23 @@ impl Validatable for HTMLSelectElement {
|
||||||
true
|
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(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -39689,6 +39689,12 @@
|
||||||
"timeout": "long",
|
"timeout": "long",
|
||||||
"url": "/html/semantics/forms/form-submission-0/submit-entity-body.html"
|
"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"
|
||||||
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -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>
|
Loading…
Add table
Add a link
Reference in a new issue