script/dom/html/
htmlselectelement.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::default::Default;
6use std::iter;
7
8use dom_struct::dom_struct;
9use embedder_traits::EmbedderControlRequest;
10use embedder_traits::{SelectElementOption, SelectElementOptionOrOptgroup};
11use html5ever::{LocalName, Prefix, QualName, local_name, ns};
12use js::rust::HandleObject;
13use style::attr::AttrValue;
14use stylo_dom::ElementState;
15use crate::dom::bindings::refcounted::Trusted;
16use crate::dom::document_embedder_controls::ControlElement;
17use crate::dom::event::{EventBubbles, EventCancelable, EventComposed};
18use crate::dom::bindings::codegen::GenericBindings::HTMLOptGroupElementBinding::HTMLOptGroupElement_Binding::HTMLOptGroupElementMethods;
19use crate::dom::activation::Activatable;
20use crate::dom::attr::Attr;
21use crate::dom::bindings::cell::{DomRefCell, Ref};
22use crate::dom::bindings::codegen::Bindings::ElementBinding::ElementMethods;
23use crate::dom::bindings::codegen::Bindings::HTMLCollectionBinding::HTMLCollectionMethods;
24use crate::dom::bindings::codegen::Bindings::HTMLOptionElementBinding::HTMLOptionElementMethods;
25use crate::dom::bindings::codegen::Bindings::HTMLOptionsCollectionBinding::HTMLOptionsCollectionMethods;
26use crate::dom::bindings::codegen::Bindings::HTMLSelectElementBinding::HTMLSelectElementMethods;
27use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
28use crate::dom::bindings::codegen::GenericBindings::CharacterDataBinding::CharacterData_Binding::CharacterDataMethods;
29use crate::dom::bindings::codegen::UnionTypes::{
30    HTMLElementOrLong, HTMLOptionElementOrHTMLOptGroupElement,
31};
32use crate::dom::bindings::error::ErrorResult;
33use crate::dom::bindings::inheritance::Castable;
34use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
35use crate::dom::bindings::str::DOMString;
36use crate::dom::characterdata::CharacterData;
37use crate::dom::document::Document;
38use crate::dom::element::{AttributeMutation, CustomElementCreationMode, Element, ElementCreator};
39use crate::dom::event::Event;
40use crate::dom::eventtarget::EventTarget;
41use crate::dom::html::htmlcollection::CollectionFilter;
42use crate::dom::html::htmlelement::HTMLElement;
43use crate::dom::html::htmlfieldsetelement::HTMLFieldSetElement;
44use crate::dom::html::htmlformelement::{FormControl, FormDatum, FormDatumValue, HTMLFormElement};
45use crate::dom::html::htmloptgroupelement::HTMLOptGroupElement;
46use crate::dom::html::htmloptionelement::HTMLOptionElement;
47use crate::dom::html::htmloptionscollection::HTMLOptionsCollection;
48use crate::dom::node::{BindContext, ChildrenMutation, Node, NodeTraits, UnbindContext};
49use crate::dom::nodelist::NodeList;
50use crate::dom::text::Text;
51use crate::dom::types::FocusEvent;
52use crate::dom::validation::{Validatable, is_barred_by_datalist_ancestor};
53use crate::dom::validitystate::{ValidationFlags, ValidityState};
54use crate::dom::virtualmethods::VirtualMethods;
55use crate::script_runtime::CanGc;
56
57const DEFAULT_SELECT_SIZE: u32 = 0;
58
59const SELECT_BOX_STYLE: &str = "
60    display: flex;
61    align-items: center;
62    height: 100%;
63";
64
65const TEXT_CONTAINER_STYLE: &str = "flex: 1;";
66
67const CHEVRON_CONTAINER_STYLE: &str = "
68    font-size: 16px;
69    margin: 4px;
70";
71
72#[derive(JSTraceable, MallocSizeOf)]
73struct OptionsFilter;
74impl CollectionFilter for OptionsFilter {
75    fn filter<'a>(&self, elem: &'a Element, root: &'a Node) -> bool {
76        if !elem.is::<HTMLOptionElement>() {
77            return false;
78        }
79
80        let node = elem.upcast::<Node>();
81        if root.is_parent_of(node) {
82            return true;
83        }
84
85        match node.GetParentNode() {
86            Some(optgroup) => optgroup.is::<HTMLOptGroupElement>() && root.is_parent_of(&optgroup),
87            None => false,
88        }
89    }
90}
91
92#[dom_struct]
93pub(crate) struct HTMLSelectElement {
94    htmlelement: HTMLElement,
95    options: MutNullableDom<HTMLOptionsCollection>,
96    form_owner: MutNullableDom<HTMLFormElement>,
97    labels_node_list: MutNullableDom<NodeList>,
98    validity_state: MutNullableDom<ValidityState>,
99    shadow_tree: DomRefCell<Option<ShadowTree>>,
100}
101
102/// Holds handles to all elements in the UA shadow tree
103#[derive(Clone, JSTraceable, MallocSizeOf)]
104#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
105struct ShadowTree {
106    selected_option: Dom<Text>,
107}
108
109impl HTMLSelectElement {
110    fn new_inherited(
111        local_name: LocalName,
112        prefix: Option<Prefix>,
113        document: &Document,
114    ) -> HTMLSelectElement {
115        HTMLSelectElement {
116            htmlelement: HTMLElement::new_inherited_with_state(
117                ElementState::ENABLED | ElementState::VALID,
118                local_name,
119                prefix,
120                document,
121            ),
122            options: Default::default(),
123            form_owner: Default::default(),
124            labels_node_list: Default::default(),
125            validity_state: Default::default(),
126            shadow_tree: Default::default(),
127        }
128    }
129
130    #[cfg_attr(crown, allow(crown::unrooted_must_root))]
131    pub(crate) fn new(
132        local_name: LocalName,
133        prefix: Option<Prefix>,
134        document: &Document,
135        proto: Option<HandleObject>,
136        can_gc: CanGc,
137    ) -> DomRoot<HTMLSelectElement> {
138        let n = Node::reflect_node_with_proto(
139            Box::new(HTMLSelectElement::new_inherited(
140                local_name, prefix, document,
141            )),
142            document,
143            proto,
144            can_gc,
145        );
146
147        n.upcast::<Node>().set_weird_parser_insertion_mode();
148        n
149    }
150
151    /// <https://html.spec.whatwg.org/multipage/#concept-select-option-list>
152    pub(crate) fn list_of_options(
153        &self,
154    ) -> impl Iterator<Item = DomRoot<HTMLOptionElement>> + use<'_> {
155        self.upcast::<Node>().children().flat_map(|node| {
156            if node.is::<HTMLOptionElement>() {
157                let node = DomRoot::downcast::<HTMLOptionElement>(node).unwrap();
158                Choice3::First(iter::once(node))
159            } else if node.is::<HTMLOptGroupElement>() {
160                Choice3::Second(node.children().filter_map(DomRoot::downcast))
161            } else {
162                Choice3::Third(iter::empty())
163            }
164        })
165    }
166
167    /// <https://html.spec.whatwg.org/multipage/#placeholder-label-option>
168    fn get_placeholder_label_option(&self) -> Option<DomRoot<HTMLOptionElement>> {
169        if self.Required() && !self.Multiple() && self.display_size() == 1 {
170            self.list_of_options().next().filter(|node| {
171                let parent = node.upcast::<Node>().GetParentNode();
172                node.Value().is_empty() && parent.as_deref() == Some(self.upcast())
173            })
174        } else {
175            None
176        }
177    }
178
179    // https://html.spec.whatwg.org/multipage/#the-select-element:concept-form-reset-control
180    pub(crate) fn reset(&self) {
181        for opt in self.list_of_options() {
182            opt.set_selectedness(opt.DefaultSelected());
183            opt.set_dirtiness(false);
184        }
185        self.ask_for_reset();
186    }
187
188    // https://html.spec.whatwg.org/multipage/#ask-for-a-reset
189    pub(crate) fn ask_for_reset(&self) {
190        if self.Multiple() {
191            return;
192        }
193
194        let mut first_enabled: Option<DomRoot<HTMLOptionElement>> = None;
195        let mut last_selected: Option<DomRoot<HTMLOptionElement>> = None;
196
197        for opt in self.list_of_options() {
198            if opt.Selected() {
199                opt.set_selectedness(false);
200                last_selected = Some(DomRoot::from_ref(&opt));
201            }
202            let element = opt.upcast::<Element>();
203            if first_enabled.is_none() && !element.disabled_state() {
204                first_enabled = Some(DomRoot::from_ref(&opt));
205            }
206        }
207
208        if let Some(last_selected) = last_selected {
209            last_selected.set_selectedness(true);
210        } else if self.display_size() == 1 {
211            if let Some(first_enabled) = first_enabled {
212                first_enabled.set_selectedness(true);
213            }
214        }
215    }
216
217    pub(crate) fn push_form_data(&self, data_set: &mut Vec<FormDatum>) {
218        if self.Name().is_empty() {
219            return;
220        }
221        for opt in self.list_of_options() {
222            let element = opt.upcast::<Element>();
223            if opt.Selected() && element.enabled_state() {
224                data_set.push(FormDatum {
225                    ty: self.Type(),
226                    name: self.Name(),
227                    value: FormDatumValue::String(opt.Value()),
228                });
229            }
230        }
231    }
232
233    // https://html.spec.whatwg.org/multipage/#concept-select-pick
234    pub(crate) fn pick_option(&self, picked: &HTMLOptionElement) {
235        if !self.Multiple() {
236            let picked = picked.upcast();
237            for opt in self.list_of_options() {
238                if opt.upcast::<HTMLElement>() != picked {
239                    opt.set_selectedness(false);
240                }
241            }
242        }
243    }
244
245    /// <https://html.spec.whatwg.org/multipage/#concept-select-size>
246    fn display_size(&self) -> u32 {
247        if self.Size() == 0 {
248            if self.Multiple() { 4 } else { 1 }
249        } else {
250            self.Size()
251        }
252    }
253
254    fn create_shadow_tree(&self, can_gc: CanGc) {
255        let document = self.owner_document();
256        let root = self.upcast::<Element>().attach_ua_shadow_root(true, can_gc);
257
258        let select_box = Element::create(
259            QualName::new(None, ns!(html), local_name!("div")),
260            None,
261            &document,
262            ElementCreator::ScriptCreated,
263            CustomElementCreationMode::Asynchronous,
264            None,
265            can_gc,
266        );
267        select_box.set_string_attribute(&local_name!("style"), SELECT_BOX_STYLE.into(), can_gc);
268
269        let text_container = Element::create(
270            QualName::new(None, ns!(html), local_name!("div")),
271            None,
272            &document,
273            ElementCreator::ScriptCreated,
274            CustomElementCreationMode::Asynchronous,
275            None,
276            can_gc,
277        );
278        text_container.set_string_attribute(
279            &local_name!("style"),
280            TEXT_CONTAINER_STYLE.into(),
281            can_gc,
282        );
283        select_box
284            .upcast::<Node>()
285            .AppendChild(text_container.upcast::<Node>(), can_gc)
286            .unwrap();
287
288        let text = Text::new(DOMString::new(), &document, can_gc);
289        let _ = self.shadow_tree.borrow_mut().insert(ShadowTree {
290            selected_option: text.as_traced(),
291        });
292        text_container
293            .upcast::<Node>()
294            .AppendChild(text.upcast::<Node>(), can_gc)
295            .unwrap();
296
297        let chevron_container = Element::create(
298            QualName::new(None, ns!(html), local_name!("div")),
299            None,
300            &document,
301            ElementCreator::ScriptCreated,
302            CustomElementCreationMode::Asynchronous,
303            None,
304            can_gc,
305        );
306        chevron_container.set_string_attribute(
307            &local_name!("style"),
308            CHEVRON_CONTAINER_STYLE.into(),
309            can_gc,
310        );
311        chevron_container
312            .upcast::<Node>()
313            .set_text_content_for_element(Some("▾".into()), can_gc);
314        select_box
315            .upcast::<Node>()
316            .AppendChild(chevron_container.upcast::<Node>(), can_gc)
317            .unwrap();
318
319        root.upcast::<Node>()
320            .AppendChild(select_box.upcast::<Node>(), can_gc)
321            .unwrap();
322    }
323
324    fn shadow_tree(&self, can_gc: CanGc) -> Ref<'_, ShadowTree> {
325        if !self.upcast::<Element>().is_shadow_host() {
326            self.create_shadow_tree(can_gc);
327        }
328
329        Ref::filter_map(self.shadow_tree.borrow(), Option::as_ref)
330            .ok()
331            .expect("UA shadow tree was not created")
332    }
333
334    pub(crate) fn update_shadow_tree(&self, can_gc: CanGc) {
335        let shadow_tree = self.shadow_tree(can_gc);
336
337        let selected_option_text = self
338            .selected_option()
339            .or_else(|| self.list_of_options().next())
340            .map(|option| option.displayed_label())
341            .unwrap_or_default();
342
343        // Replace newlines with whitespace, then collapse and trim whitespace
344        let displayed_text = itertools::join(selected_option_text.str().split_whitespace(), " ");
345
346        shadow_tree
347            .selected_option
348            .upcast::<CharacterData>()
349            .SetData(displayed_text.trim().into());
350    }
351
352    pub(crate) fn selected_option(&self) -> Option<DomRoot<HTMLOptionElement>> {
353        self.list_of_options()
354            .find(|opt_elem| opt_elem.Selected())
355            .or_else(|| self.list_of_options().next())
356    }
357
358    pub(crate) fn show_menu(&self) {
359        // Collect list of optgroups and options
360        let mut index = 0;
361        let mut embedder_option_from_option = |option: &HTMLOptionElement| {
362            let embedder_option = SelectElementOption {
363                id: index,
364                label: option.displayed_label().into(),
365                is_disabled: option.Disabled(),
366            };
367            index += 1;
368            embedder_option
369        };
370        let options = self
371            .upcast::<Node>()
372            .children()
373            .flat_map(|child| {
374                if let Some(option) = child.downcast::<HTMLOptionElement>() {
375                    return Some(embedder_option_from_option(option).into());
376                }
377
378                if let Some(optgroup) = child.downcast::<HTMLOptGroupElement>() {
379                    let options = optgroup
380                        .upcast::<Node>()
381                        .children()
382                        .flat_map(DomRoot::downcast::<HTMLOptionElement>)
383                        .map(|option| embedder_option_from_option(&option))
384                        .collect();
385                    let label = optgroup.Label().into();
386
387                    return Some(SelectElementOptionOrOptgroup::Optgroup { label, options });
388                }
389
390                None
391            })
392            .collect();
393
394        let selected_index = self.list_of_options().position(|option| option.Selected());
395
396        self.owner_document()
397            .embedder_controls()
398            .show_embedder_control(
399                ControlElement::Select(DomRoot::from_ref(self)),
400                EmbedderControlRequest::SelectElement(options, selected_index),
401            );
402    }
403
404    pub(crate) fn handle_menu_response(&self, response: Option<usize>, can_gc: CanGc) {
405        let Some(selected_value) = response else {
406            return;
407        };
408
409        self.SetSelectedIndex(selected_value as i32, can_gc);
410        self.send_update_notifications();
411    }
412
413    /// <https://html.spec.whatwg.org/multipage/#send-select-update-notifications>
414    fn send_update_notifications(&self) {
415        // > When the user agent is to send select update notifications, queue an element task on the
416        // > user interaction task source given the select element to run these steps:
417        let this = Trusted::new(self);
418        self.owner_global()
419            .task_manager()
420            .user_interaction_task_source()
421            .queue(task!(send_select_update_notification: move || {
422                let this = this.root();
423
424                // TODO: Step 1. Set the select element's user validity to true.
425
426                // Step 2. Fire an event named input at the select element, with the bubbles and composed
427                // attributes initialized to true.
428                this.upcast::<EventTarget>()
429                    .fire_event_with_params(
430                        atom!("input"),
431                        EventBubbles::Bubbles,
432                        EventCancelable::NotCancelable,
433                        EventComposed::Composed,
434                        CanGc::note(),
435                    );
436
437                // Step 3. Fire an event named change at the select element, with the bubbles attribute initialized
438                // to true.
439                this.upcast::<EventTarget>()
440                    .fire_bubbling_event(atom!("change"), CanGc::note());
441            }));
442    }
443
444    fn may_have_embedder_control(&self) -> bool {
445        let el = self.upcast::<Element>();
446        !el.disabled_state()
447    }
448}
449
450impl HTMLSelectElementMethods<crate::DomTypeHolder> for HTMLSelectElement {
451    /// <https://html.spec.whatwg.org/multipage/#dom-select-add>
452    fn Add(
453        &self,
454        element: HTMLOptionElementOrHTMLOptGroupElement,
455        before: Option<HTMLElementOrLong>,
456    ) -> ErrorResult {
457        self.Options().Add(element, before)
458    }
459
460    // https://html.spec.whatwg.org/multipage/#dom-fe-disabled
461    make_bool_getter!(Disabled, "disabled");
462
463    // https://html.spec.whatwg.org/multipage/#dom-fe-disabled
464    make_bool_setter!(SetDisabled, "disabled");
465
466    /// <https://html.spec.whatwg.org/multipage/#dom-fae-form>
467    fn GetForm(&self) -> Option<DomRoot<HTMLFormElement>> {
468        self.form_owner()
469    }
470
471    // https://html.spec.whatwg.org/multipage/#dom-select-multiple
472    make_bool_getter!(Multiple, "multiple");
473
474    // https://html.spec.whatwg.org/multipage/#dom-select-multiple
475    make_bool_setter!(SetMultiple, "multiple");
476
477    // https://html.spec.whatwg.org/multipage/#dom-fe-name
478    make_getter!(Name, "name");
479
480    // https://html.spec.whatwg.org/multipage/#dom-fe-name
481    make_atomic_setter!(SetName, "name");
482
483    // https://html.spec.whatwg.org/multipage/#dom-select-required
484    make_bool_getter!(Required, "required");
485
486    // https://html.spec.whatwg.org/multipage/#dom-select-required
487    make_bool_setter!(SetRequired, "required");
488
489    // https://html.spec.whatwg.org/multipage/#dom-select-size
490    make_uint_getter!(Size, "size", DEFAULT_SELECT_SIZE);
491
492    // https://html.spec.whatwg.org/multipage/#dom-select-size
493    make_uint_setter!(SetSize, "size", DEFAULT_SELECT_SIZE);
494
495    /// <https://html.spec.whatwg.org/multipage/#dom-select-type>
496    fn Type(&self) -> DOMString {
497        DOMString::from(if self.Multiple() {
498            "select-multiple"
499        } else {
500            "select-one"
501        })
502    }
503
504    // https://html.spec.whatwg.org/multipage/#dom-lfe-labels
505    make_labels_getter!(Labels, labels_node_list);
506
507    /// <https://html.spec.whatwg.org/multipage/#dom-select-options>
508    fn Options(&self) -> DomRoot<HTMLOptionsCollection> {
509        self.options.or_init(|| {
510            let window = self.owner_window();
511            HTMLOptionsCollection::new(&window, self, Box::new(OptionsFilter), CanGc::note())
512        })
513    }
514
515    /// <https://html.spec.whatwg.org/multipage/#dom-select-length>
516    fn Length(&self) -> u32 {
517        self.Options().Length()
518    }
519
520    /// <https://html.spec.whatwg.org/multipage/#dom-select-length>
521    fn SetLength(&self, length: u32, can_gc: CanGc) {
522        self.Options().SetLength(length, can_gc)
523    }
524
525    /// <https://html.spec.whatwg.org/multipage/#dom-select-item>
526    fn Item(&self, index: u32) -> Option<DomRoot<Element>> {
527        self.Options().upcast().Item(index)
528    }
529
530    /// <https://html.spec.whatwg.org/multipage/#dom-select-item>
531    fn IndexedGetter(&self, index: u32) -> Option<DomRoot<Element>> {
532        self.Options().IndexedGetter(index)
533    }
534
535    /// <https://html.spec.whatwg.org/multipage/#dom-select-setter>
536    fn IndexedSetter(
537        &self,
538        index: u32,
539        value: Option<&HTMLOptionElement>,
540        can_gc: CanGc,
541    ) -> ErrorResult {
542        self.Options().IndexedSetter(index, value, can_gc)
543    }
544
545    /// <https://html.spec.whatwg.org/multipage/#dom-select-nameditem>
546    fn NamedItem(&self, name: DOMString) -> Option<DomRoot<HTMLOptionElement>> {
547        self.Options()
548            .NamedGetter(name)
549            .and_then(DomRoot::downcast::<HTMLOptionElement>)
550    }
551
552    /// <https://html.spec.whatwg.org/multipage/#dom-select-remove>
553    fn Remove_(&self, index: i32) {
554        self.Options().Remove(index)
555    }
556
557    /// <https://html.spec.whatwg.org/multipage/#dom-select-remove>
558    fn Remove(&self) {
559        self.upcast::<Element>().Remove(CanGc::note())
560    }
561
562    /// <https://html.spec.whatwg.org/multipage/#dom-select-value>
563    fn Value(&self) -> DOMString {
564        self.list_of_options()
565            .find(|opt_elem| opt_elem.Selected())
566            .map(|opt_elem| opt_elem.Value())
567            .unwrap_or_default()
568    }
569
570    /// <https://html.spec.whatwg.org/multipage/#dom-select-value>
571    fn SetValue(&self, value: DOMString, can_gc: CanGc) {
572        let mut opt_iter = self.list_of_options();
573        // Reset until we find an <option> with a matching value
574        for opt in opt_iter.by_ref() {
575            if opt.Value() == value {
576                opt.set_selectedness(true);
577                opt.set_dirtiness(true);
578                break;
579            }
580            opt.set_selectedness(false);
581        }
582        // Reset remaining <option> elements
583        for opt in opt_iter {
584            opt.set_selectedness(false);
585        }
586
587        self.validity_state(can_gc)
588            .perform_validation_and_update(ValidationFlags::VALUE_MISSING, can_gc);
589    }
590
591    /// <https://html.spec.whatwg.org/multipage/#dom-select-selectedindex>
592    fn SelectedIndex(&self) -> i32 {
593        self.list_of_options()
594            .enumerate()
595            .filter(|(_, opt_elem)| opt_elem.Selected())
596            .map(|(i, _)| i as i32)
597            .next()
598            .unwrap_or(-1)
599    }
600
601    /// <https://html.spec.whatwg.org/multipage/#dom-select-selectedindex>
602    fn SetSelectedIndex(&self, index: i32, can_gc: CanGc) {
603        let mut selection_did_change = false;
604
605        let mut opt_iter = self.list_of_options();
606        for opt in opt_iter.by_ref().take(index as usize) {
607            selection_did_change |= opt.Selected();
608            opt.set_selectedness(false);
609        }
610        if let Some(selected_option) = opt_iter.next() {
611            selection_did_change |= !selected_option.Selected();
612            selected_option.set_selectedness(true);
613            selected_option.set_dirtiness(true);
614
615            // Reset remaining <option> elements
616            for opt in opt_iter {
617                selection_did_change |= opt.Selected();
618                opt.set_selectedness(false);
619            }
620        }
621
622        if selection_did_change {
623            self.update_shadow_tree(can_gc);
624        }
625    }
626
627    /// <https://html.spec.whatwg.org/multipage/#dom-cva-willvalidate>
628    fn WillValidate(&self) -> bool {
629        self.is_instance_validatable()
630    }
631
632    /// <https://html.spec.whatwg.org/multipage/#dom-cva-validity>
633    fn Validity(&self, can_gc: CanGc) -> DomRoot<ValidityState> {
634        self.validity_state(can_gc)
635    }
636
637    /// <https://html.spec.whatwg.org/multipage/#dom-cva-checkvalidity>
638    fn CheckValidity(&self, can_gc: CanGc) -> bool {
639        self.check_validity(can_gc)
640    }
641
642    /// <https://html.spec.whatwg.org/multipage/#dom-cva-reportvalidity>
643    fn ReportValidity(&self, can_gc: CanGc) -> bool {
644        self.report_validity(can_gc)
645    }
646
647    /// <https://html.spec.whatwg.org/multipage/#dom-cva-validationmessage>
648    fn ValidationMessage(&self) -> DOMString {
649        self.validation_message()
650    }
651
652    /// <https://html.spec.whatwg.org/multipage/#dom-cva-setcustomvalidity>
653    fn SetCustomValidity(&self, error: DOMString, can_gc: CanGc) {
654        self.validity_state(can_gc).set_custom_error_message(error);
655    }
656}
657
658impl VirtualMethods for HTMLSelectElement {
659    fn super_type(&self) -> Option<&dyn VirtualMethods> {
660        Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
661    }
662
663    fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) {
664        let could_have_had_embedder_control = self.may_have_embedder_control();
665        self.super_type()
666            .unwrap()
667            .attribute_mutated(attr, mutation, can_gc);
668        match *attr.local_name() {
669            local_name!("required") => {
670                self.validity_state(can_gc)
671                    .perform_validation_and_update(ValidationFlags::VALUE_MISSING, can_gc);
672            },
673            local_name!("disabled") => {
674                let el = self.upcast::<Element>();
675                match mutation {
676                    AttributeMutation::Set(_) => {
677                        el.set_disabled_state(true);
678                        el.set_enabled_state(false);
679                    },
680                    AttributeMutation::Removed => {
681                        el.set_disabled_state(false);
682                        el.set_enabled_state(true);
683                        el.check_ancestors_disabled_state_for_form_control();
684                    },
685                }
686
687                self.validity_state(can_gc)
688                    .perform_validation_and_update(ValidationFlags::VALUE_MISSING, can_gc);
689            },
690            local_name!("form") => {
691                self.form_attribute_mutated(mutation, can_gc);
692            },
693            _ => {},
694        }
695        if could_have_had_embedder_control && !self.may_have_embedder_control() {
696            self.owner_document()
697                .embedder_controls()
698                .hide_embedder_control(self.upcast());
699        }
700    }
701
702    fn bind_to_tree(&self, context: &BindContext, can_gc: CanGc) {
703        if let Some(s) = self.super_type() {
704            s.bind_to_tree(context, can_gc);
705        }
706
707        self.upcast::<Element>()
708            .check_ancestors_disabled_state_for_form_control();
709    }
710
711    fn unbind_from_tree(&self, context: &UnbindContext, can_gc: CanGc) {
712        self.super_type().unwrap().unbind_from_tree(context, can_gc);
713
714        let node = self.upcast::<Node>();
715        let el = self.upcast::<Element>();
716        if node
717            .ancestors()
718            .any(|ancestor| ancestor.is::<HTMLFieldSetElement>())
719        {
720            el.check_ancestors_disabled_state_for_form_control();
721        } else {
722            el.check_disabled_attribute();
723        }
724
725        self.owner_document()
726            .embedder_controls()
727            .hide_embedder_control(self.upcast());
728    }
729
730    fn children_changed(&self, mutation: &ChildrenMutation, can_gc: CanGc) {
731        if let Some(s) = self.super_type() {
732            s.children_changed(mutation, can_gc);
733        }
734
735        self.update_shadow_tree(can_gc);
736    }
737
738    fn parse_plain_attribute(&self, local_name: &LocalName, value: DOMString) -> AttrValue {
739        match *local_name {
740            local_name!("size") => AttrValue::from_u32(value.into(), DEFAULT_SELECT_SIZE),
741            _ => self
742                .super_type()
743                .unwrap()
744                .parse_plain_attribute(local_name, value),
745        }
746    }
747
748    fn handle_event(&self, event: &Event, can_gc: CanGc) {
749        self.super_type().unwrap().handle_event(event, can_gc);
750        if let Some(event) = event.downcast::<FocusEvent>() {
751            if *event.upcast::<Event>().type_() != *"blur" {
752                self.owner_document()
753                    .embedder_controls()
754                    .hide_embedder_control(self.upcast());
755            }
756        }
757    }
758}
759
760impl FormControl for HTMLSelectElement {
761    fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
762        self.form_owner.get()
763    }
764
765    fn set_form_owner(&self, form: Option<&HTMLFormElement>) {
766        self.form_owner.set(form);
767    }
768
769    fn to_element(&self) -> &Element {
770        self.upcast::<Element>()
771    }
772}
773
774impl Validatable for HTMLSelectElement {
775    fn as_element(&self) -> &Element {
776        self.upcast()
777    }
778
779    fn validity_state(&self, can_gc: CanGc) -> DomRoot<ValidityState> {
780        self.validity_state
781            .or_init(|| ValidityState::new(&self.owner_window(), self.upcast(), can_gc))
782    }
783
784    fn is_instance_validatable(&self) -> bool {
785        // https://html.spec.whatwg.org/multipage/#enabling-and-disabling-form-controls%3A-the-disabled-attribute%3Abarred-from-constraint-validation
786        // https://html.spec.whatwg.org/multipage/#the-datalist-element%3Abarred-from-constraint-validation
787        !self.upcast::<Element>().disabled_state() && !is_barred_by_datalist_ancestor(self.upcast())
788    }
789
790    fn perform_validation(
791        &self,
792        validate_flags: ValidationFlags,
793        _can_gc: CanGc,
794    ) -> ValidationFlags {
795        let mut failed_flags = ValidationFlags::empty();
796
797        // https://html.spec.whatwg.org/multipage/#suffering-from-being-missing
798        // https://html.spec.whatwg.org/multipage/#the-select-element%3Asuffering-from-being-missing
799        if validate_flags.contains(ValidationFlags::VALUE_MISSING) && self.Required() {
800            let placeholder = self.get_placeholder_label_option();
801            let is_value_missing = !self
802                .list_of_options()
803                .any(|e| e.Selected() && placeholder != Some(e));
804            failed_flags.set(ValidationFlags::VALUE_MISSING, is_value_missing);
805        }
806
807        failed_flags
808    }
809}
810
811impl Activatable for HTMLSelectElement {
812    fn as_element(&self) -> &Element {
813        self.upcast()
814    }
815
816    fn is_instance_activatable(&self) -> bool {
817        true
818    }
819
820    fn activation_behavior(&self, _event: &Event, _target: &EventTarget, _can_gc: CanGc) {
821        self.show_menu();
822    }
823}
824
825enum Choice3<I, J, K> {
826    First(I),
827    Second(J),
828    Third(K),
829}
830
831impl<I, J, K, T> Iterator for Choice3<I, J, K>
832where
833    I: Iterator<Item = T>,
834    J: Iterator<Item = T>,
835    K: Iterator<Item = T>,
836{
837    type Item = T;
838
839    fn next(&mut self) -> Option<T> {
840        match *self {
841            Choice3::First(ref mut i) => i.next(),
842            Choice3::Second(ref mut j) => j.next(),
843            Choice3::Third(ref mut k) => k.next(),
844        }
845    }
846
847    fn size_hint(&self) -> (usize, Option<usize>) {
848        match *self {
849            Choice3::First(ref i) => i.size_hint(),
850            Choice3::Second(ref j) => j.size_hint(),
851            Choice3::Third(ref k) => k.size_hint(),
852        }
853    }
854}