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