mirror of
https://github.com/servo/servo.git
synced 2025-07-23 15:23:42 +01:00
Simplify and fix iteration over radio button group
This commit is contained in:
parent
63cb13f5fa
commit
f6bcc1f954
1 changed files with 49 additions and 53 deletions
|
@ -1306,55 +1306,59 @@ impl HTMLInputElementMethods for HTMLInputElement {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unsafe_code)]
|
fn radio_group_iter<'a>(
|
||||||
fn broadcast_radio_checked(broadcaster: &HTMLInputElement, group: Option<&Atom>) {
|
elem: &'a HTMLInputElement,
|
||||||
match group {
|
group: Option<&'a Atom>,
|
||||||
None | Some(&atom!("")) => {
|
) -> impl Iterator<Item = DomRoot<HTMLInputElement>> + 'a {
|
||||||
// Radio input elements with a missing or empty name are alone in their
|
let owner = elem.form_owner();
|
||||||
// own group.
|
let root = elem
|
||||||
return;
|
.upcast::<Node>()
|
||||||
},
|
.GetRootNode(&GetRootNodeOptions::empty());
|
||||||
_ => {},
|
|
||||||
|
// If group is None, in_same_group always fails, but we need to always return elem.
|
||||||
|
root.traverse_preorder(ShadowIncluding::No)
|
||||||
|
.filter_map(|r| DomRoot::downcast::<HTMLInputElement>(r))
|
||||||
|
.filter(move |r| &**r == elem || in_same_group(&r, owner.as_deref(), group, None))
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: if not in document, use root ancestor instead of document
|
fn broadcast_radio_checked(broadcaster: &HTMLInputElement, group: Option<&Atom>) {
|
||||||
let owner = broadcaster.form_owner();
|
for r in radio_group_iter(broadcaster, group) {
|
||||||
let doc = document_from_node(broadcaster);
|
if broadcaster != &*r && r.Checked() {
|
||||||
|
|
||||||
// This function is a workaround for lifetime constraint difficulties.
|
|
||||||
fn do_broadcast(
|
|
||||||
doc_node: &Node,
|
|
||||||
broadcaster: &HTMLInputElement,
|
|
||||||
owner: Option<&HTMLFormElement>,
|
|
||||||
group: Option<&Atom>,
|
|
||||||
) {
|
|
||||||
let iter = doc_node
|
|
||||||
.query_selector_iter(DOMString::from("input[type=radio]"))
|
|
||||||
.unwrap()
|
|
||||||
.filter_map(DomRoot::downcast::<HTMLInputElement>)
|
|
||||||
.filter(|r| in_same_group(&r, owner, group) && broadcaster != &**r);
|
|
||||||
for ref r in iter {
|
|
||||||
if r.Checked() {
|
|
||||||
r.SetChecked(false);
|
r.SetChecked(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
do_broadcast(doc.upcast(), broadcaster, owner.as_deref(), group)
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#radio-button-group
|
// https://html.spec.whatwg.org/multipage/#radio-button-group
|
||||||
fn in_same_group(
|
fn in_same_group(
|
||||||
other: &HTMLInputElement,
|
other: &HTMLInputElement,
|
||||||
owner: Option<&HTMLFormElement>,
|
owner: Option<&HTMLFormElement>,
|
||||||
group: Option<&Atom>,
|
group: Option<&Atom>,
|
||||||
|
tree_root: Option<&Node>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
other.input_type() == InputType::Radio &&
|
if group.is_none() {
|
||||||
// TODO Both a and b are in the same home subtree.
|
// Radio input elements with a missing or empty name are alone in their own group.
|
||||||
other.form_owner().as_deref() == owner &&
|
return false;
|
||||||
match (other.radio_group_name(), group) {
|
}
|
||||||
(Some(ref s1), Some(s2)) => s1 == s2 && s2 != &atom!(""),
|
|
||||||
_ => false
|
if other.input_type() != InputType::Radio ||
|
||||||
|
other.form_owner().as_deref() != owner ||
|
||||||
|
other.radio_group_name().as_ref() != group
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
match tree_root {
|
||||||
|
Some(tree_root) => {
|
||||||
|
let other_root = other
|
||||||
|
.upcast::<Node>()
|
||||||
|
.GetRootNode(&GetRootNodeOptions::empty());
|
||||||
|
tree_root == &*other_root
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
// Skip check if the tree root isn't provided.
|
||||||
|
true
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1461,10 +1465,10 @@ impl HTMLInputElement {
|
||||||
|
|
||||||
// https://html.spec.whatwg.org/multipage/#radio-button-group
|
// https://html.spec.whatwg.org/multipage/#radio-button-group
|
||||||
fn radio_group_name(&self) -> Option<Atom> {
|
fn radio_group_name(&self) -> Option<Atom> {
|
||||||
//TODO: determine form owner
|
|
||||||
self.upcast::<Element>()
|
self.upcast::<Element>()
|
||||||
.get_attribute(&ns!(), &local_name!("name"))
|
.get_attribute(&ns!(), &local_name!("name"))
|
||||||
.map(|name| name.value().as_atom().clone())
|
.map(|name| name.value().as_atom().clone())
|
||||||
|
.filter(|name| name != &atom!(""))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_checked_state(&self, checked: bool, dirty: bool) {
|
fn update_checked_state(&self, checked: bool, dirty: bool) {
|
||||||
|
@ -2385,20 +2389,8 @@ impl Activatable for HTMLInputElement {
|
||||||
},
|
},
|
||||||
// https://html.spec.whatwg.org/multipage/#radio-button-state-(type=radio):pre-click-activation-steps
|
// https://html.spec.whatwg.org/multipage/#radio-button-state-(type=radio):pre-click-activation-steps
|
||||||
InputType::Radio => {
|
InputType::Radio => {
|
||||||
//TODO: if not in document, use root ancestor instead of document
|
let checked_member = radio_group_iter(self, self.radio_group_name().as_ref())
|
||||||
let owner = self.form_owner();
|
.find(|r| r.Checked());
|
||||||
let doc = document_from_node(self);
|
|
||||||
let doc_node = doc.upcast::<Node>();
|
|
||||||
let group = self.radio_group_name();
|
|
||||||
|
|
||||||
// Safe since we only manipulate the DOM tree after finding an element
|
|
||||||
let checked_member = doc_node
|
|
||||||
.query_selector_iter(DOMString::from("input[type=radio]"))
|
|
||||||
.unwrap()
|
|
||||||
.filter_map(DomRoot::downcast::<HTMLInputElement>)
|
|
||||||
.find(|r| {
|
|
||||||
in_same_group(&*r, owner.as_deref(), group.as_ref()) && r.Checked()
|
|
||||||
});
|
|
||||||
cache.checked_radio = checked_member.as_deref().map(Dom::from_ref);
|
cache.checked_radio = checked_member.as_deref().map(Dom::from_ref);
|
||||||
cache.checked_changed = self.checked_changed.get();
|
cache.checked_changed = self.checked_changed.get();
|
||||||
self.SetChecked(true);
|
self.SetChecked(true);
|
||||||
|
@ -2436,12 +2428,16 @@ impl Activatable for HTMLInputElement {
|
||||||
// We want to restore state only if the element had been changed in the first place
|
// We want to restore state only if the element had been changed in the first place
|
||||||
if cache.was_mutable {
|
if cache.was_mutable {
|
||||||
if let Some(ref o) = cache.checked_radio {
|
if let Some(ref o) = cache.checked_radio {
|
||||||
|
let tree_root = self
|
||||||
|
.upcast::<Node>()
|
||||||
|
.GetRootNode(&GetRootNodeOptions::empty());
|
||||||
// Avoiding iterating through the whole tree here, instead
|
// Avoiding iterating through the whole tree here, instead
|
||||||
// we can check if the conditions for radio group siblings apply
|
// we can check if the conditions for radio group siblings apply
|
||||||
if in_same_group(
|
if in_same_group(
|
||||||
&o,
|
&o,
|
||||||
self.form_owner().as_deref(),
|
self.form_owner().as_deref(),
|
||||||
self.radio_group_name().as_ref(),
|
self.radio_group_name().as_ref(),
|
||||||
|
Some(&*tree_root),
|
||||||
) {
|
) {
|
||||||
o.SetChecked(true);
|
o.SetChecked(true);
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue