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