mirror of
https://github.com/servo/servo
synced 2026-04-26 01:25:32 +02:00
script: Implement HTMLSelectElement.selectedOptions (#43017)
This introduces a `CollectionSource` trait as an alternative to `CollectionFilter` in `HTMLCollection`, allowing collections to provide elements directly via a custom iterator rather than filtering a full subtree traversal, which would otherwise be rather inefficient for smaller iterable sequences of options that can be determined without traversing the whole subtree again. The newly implemented `selectedOptions` attribute on the `<select>` element uses this to iterate only the element's list of options that are marked as selected. Testing: 14 assertions in the existing WPT went from failing to passing. Fixes #15522. --------- Signed-off-by: Jacob Adam <software@jacobadam.net>
This commit is contained in:
@@ -39,7 +39,7 @@ use crate::dom::document::Document;
|
||||
use crate::dom::element::{AttributeMutation, CustomElementCreationMode, Element, ElementCreator};
|
||||
use crate::dom::event::Event;
|
||||
use crate::dom::eventtarget::EventTarget;
|
||||
use crate::dom::html::htmlcollection::CollectionFilter;
|
||||
use crate::dom::html::htmlcollection::{CollectionFilter, CollectionSource, HTMLCollection};
|
||||
use crate::dom::html::htmlelement::HTMLElement;
|
||||
use crate::dom::html::htmlfieldsetelement::HTMLFieldSetElement;
|
||||
use crate::dom::html::htmlformelement::{FormControl, FormDatum, FormDatumValue, HTMLFormElement};
|
||||
@@ -90,10 +90,29 @@ impl CollectionFilter for OptionsFilter {
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides selected options directly via [`HTMLSelectElement::list_of_options`],
|
||||
/// avoiding a full subtree traversal.
|
||||
#[derive(JSTraceable, MallocSizeOf)]
|
||||
struct SelectedOptionsSource;
|
||||
impl CollectionSource for SelectedOptionsSource {
|
||||
fn iter<'a>(&'a self, root: &'a Node) -> Box<dyn Iterator<Item = DomRoot<Element>> + 'a> {
|
||||
let select = root
|
||||
.downcast::<HTMLSelectElement>()
|
||||
.expect("SelectedOptionsSource must be rooted on an HTMLSelectElement");
|
||||
Box::new(
|
||||
select
|
||||
.list_of_options()
|
||||
.filter(|option| option.Selected())
|
||||
.map(DomRoot::upcast::<Element>),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[dom_struct]
|
||||
pub(crate) struct HTMLSelectElement {
|
||||
htmlelement: HTMLElement,
|
||||
options: MutNullableDom<HTMLOptionsCollection>,
|
||||
selected_options: MutNullableDom<HTMLCollection>,
|
||||
form_owner: MutNullableDom<HTMLFormElement>,
|
||||
labels_node_list: MutNullableDom<NodeList>,
|
||||
validity_state: MutNullableDom<ValidityState>,
|
||||
@@ -121,6 +140,7 @@ impl HTMLSelectElement {
|
||||
document,
|
||||
),
|
||||
options: Default::default(),
|
||||
selected_options: Default::default(),
|
||||
form_owner: Default::default(),
|
||||
labels_node_list: Default::default(),
|
||||
validity_state: Default::default(),
|
||||
@@ -534,6 +554,19 @@ impl HTMLSelectElementMethods<crate::DomTypeHolder> for HTMLSelectElement {
|
||||
})
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-select-selectedoptions>
|
||||
fn SelectedOptions(&self) -> DomRoot<HTMLCollection> {
|
||||
self.selected_options.or_init(|| {
|
||||
let window = self.owner_window();
|
||||
HTMLCollection::new_with_source(
|
||||
&window,
|
||||
self.upcast(),
|
||||
Box::new(SelectedOptionsSource),
|
||||
CanGc::note(),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// <https://html.spec.whatwg.org/multipage/#dom-select-length>
|
||||
fn Length(&self) -> u32 {
|
||||
self.Options().Length()
|
||||
|
||||
Reference in New Issue
Block a user