1use std::default::Default;
6use std::iter;
7
8use dom_struct::dom_struct;
9use embedder_traits::{EmbedderControlRequest, SelectElementRequest};
10use embedder_traits::{SelectElementOption, SelectElementOptionOrOptgroup};
11use html5ever::{local_name, LocalName, Prefix};
12use js::context::{JSContext, NoGC};
13use js::rust::HandleObject;
14use script_bindings::cell::{DomRefCell, Ref};
15use script_bindings::dom::UnrootedDom;
16use style::attr::AttrValue;
17use stylo_dom::ElementState;
18
19use crate::dom::activation::Activatable;
20use crate::dom::element::attributes::storage::AttrRef;
21use crate::dom::iterators::ShadowIncluding;
22use crate::dom::bindings::codegen::Bindings::EventBinding::EventMethods;
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::GenericBindings::HTMLOptGroupElementBinding::HTMLOptGroupElement_Binding::HTMLOptGroupElementMethods;
31use crate::dom::bindings::codegen::UnionTypes::{
32 HTMLElementOrLong, HTMLOptionElementOrHTMLOptGroupElement,
33};
34use crate::dom::bindings::error::ErrorResult;
35use crate::dom::bindings::inheritance::Castable;
36use crate::dom::bindings::refcounted::Trusted;
37use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
38use crate::dom::bindings::str::DOMString;
39use crate::dom::characterdata::CharacterData;
40use crate::dom::document::Document;
41use crate::dom::document_embedder_controls::ControlElement;
42use crate::dom::element::{AttributeMutation, Element};
43use crate::dom::event::Event;
44use crate::dom::event::{EventBubbles, EventCancelable, EventComposed};
45use crate::dom::eventtarget::EventTarget;
46use crate::dom::html::htmlcollection::{CollectionFilter, CollectionSource, HTMLCollection};
47use crate::dom::html::htmlelement::HTMLElement;
48use crate::dom::html::htmlformelement::{FormControl, FormDatum, FormDatumValue, HTMLFormElement};
49use crate::dom::html::htmloptgroupelement::HTMLOptGroupElement;
50use crate::dom::html::htmloptionelement::HTMLOptionElement;
51use crate::dom::html::htmlhrelement::HTMLHRElement;
52use crate::dom::html::htmloptionscollection::HTMLOptionsCollection;
53use crate::dom::node::{ChildrenMutation, Node, NodeTraits, UnbindContext};
54use crate::dom::nodelist::NodeList;
55use crate::dom::shadowroot::ShadowRoot;
56use crate::dom::shadowroot::ua_shadowroot::{CreateUAShadowRootForSelf, UAShadowRoot};
57use crate::dom::text::Text;
58use crate::dom::types::{FocusEvent, HTMLDataListElement};
59use crate::dom::validation::{is_barred_by_datalist_ancestor, Validatable};
60use crate::dom::validitystate::{ValidationFlags, ValidityState};
61use crate::dom::node::virtualmethods::VirtualMethods;
62
63const DEFAULT_SELECT_SIZE: u32 = 0;
64
65const SELECT_BOX_STYLE: &str = "
66 display: flex;
67 align-items: center;
68 height: 100%;
69 gap: 4px;
70";
71
72const TEXT_CONTAINER_STYLE: &str = "flex: 1;";
73
74const CHEVRON_CONTAINER_STYLE: &str = "
75 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>');
76 background-size: 100%;
77 background-repeat: no-repeat;
78 background-position: center;
79
80 vertical-align: middle;
81 line-height: 1;
82 display: inline-block;
83 width: 0.75em;
84 height: 0.75em;
85";
86
87#[derive(JSTraceable, MallocSizeOf)]
88struct OptionsFilter;
89impl CollectionFilter for OptionsFilter {
90 fn filter<'a>(&self, element: &'a Element, root: &'a Node) -> bool {
91 let Some(option) = element.downcast::<HTMLOptionElement>() else {
92 return false;
93 };
94 let Some(select) = option.cached_nearest_ancestor_select() else {
95 return false;
96 };
97 root == select.upcast()
98 }
99}
100
101#[derive(JSTraceable, MallocSizeOf)]
103struct SelectedOptionsSource;
104
105impl CollectionSource for SelectedOptionsSource {
106 fn iter<'b>(
107 &'b self,
108 no_gc: &'b NoGC,
109 root: &'b Node,
110 ) -> Box<dyn Iterator<Item = UnrootedDom<'b, Element>> + 'b> {
111 let select = root
112 .downcast::<HTMLSelectElement>()
113 .expect("SelectedOptionsSource must be rooted on an HTMLSelectElement");
114 Box::new(
115 select
116 .list_of_options(no_gc)
117 .filter(|option| option.Selected())
118 .map(UnrootedDom::upcast::<Element>),
119 )
120 }
121}
122
123#[dom_struct]
124pub(crate) struct HTMLSelectElement {
125 htmlelement: HTMLElement,
126 options: MutNullableDom<HTMLOptionsCollection>,
127 selected_options: MutNullableDom<HTMLCollection>,
128 form_owner: MutNullableDom<HTMLFormElement>,
129 labels_node_list: MutNullableDom<NodeList>,
130 validity_state: MutNullableDom<ValidityState>,
131 shadow_tree: DomRefCell<Option<ShadowTree>>,
132}
133
134#[derive(Clone, JSTraceable, MallocSizeOf)]
136#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
137struct ShadowTree {
138 selected_option: Dom<Text>,
139}
140
141impl UAShadowRoot<ShadowTree> for HTMLSelectElement {
142 fn store_for_shadow_tree(&self, cx: &mut JSContext, shadow_root: DomRoot<ShadowRoot>) {
143 let document = self.owner_document();
144
145 let select_box = self.create_element_in_ua_shadowroot(cx, local_name!("div"));
146 select_box.set_string_attribute(cx, &local_name!("style"), SELECT_BOX_STYLE.into());
147 shadow_root
148 .upcast::<Node>()
149 .AppendChild(cx, select_box.upcast::<Node>())
150 .unwrap();
151
152 let text_container = self.create_element_in_ua_shadowroot(cx, local_name!("div"));
153 text_container.set_string_attribute(cx, &local_name!("style"), TEXT_CONTAINER_STYLE.into());
154 select_box
155 .upcast::<Node>()
156 .AppendChild(cx, text_container.upcast::<Node>())
157 .unwrap();
158
159 let text = Text::new(cx, DOMString::new(), &document);
160 text_container
161 .upcast::<Node>()
162 .AppendChild(cx, text.upcast::<Node>())
163 .unwrap();
164
165 let chevron_container = self.create_element_in_ua_shadowroot(cx, local_name!("div"));
166 chevron_container.set_string_attribute(
167 cx,
168 &local_name!("style"),
169 CHEVRON_CONTAINER_STYLE.into(),
170 );
171 select_box
172 .upcast::<Node>()
173 .AppendChild(cx, chevron_container.upcast::<Node>())
174 .unwrap();
175
176 let _ = self
177 .shadow_tree
178 .safe_borrow_mut(cx.no_gc())
179 .insert(ShadowTree {
180 selected_option: text.as_traced(),
181 });
182 }
183
184 fn borrow_for_shadow_tree(&self) -> Ref<'_, Option<ShadowTree>> {
185 self.shadow_tree.borrow()
186 }
187}
188
189impl HTMLSelectElement {
190 fn new_inherited(
191 local_name: LocalName,
192 prefix: Option<Prefix>,
193 document: &Document,
194 ) -> HTMLSelectElement {
195 HTMLSelectElement {
196 htmlelement: HTMLElement::new_inherited_with_state(
197 ElementState::ENABLED | ElementState::VALID,
198 local_name,
199 prefix,
200 document,
201 ),
202 options: Default::default(),
203 selected_options: Default::default(),
204 form_owner: Default::default(),
205 labels_node_list: Default::default(),
206 validity_state: Default::default(),
207 shadow_tree: Default::default(),
208 }
209 }
210
211 pub(crate) fn new(
212 cx: &mut js::context::JSContext,
213 local_name: LocalName,
214 prefix: Option<Prefix>,
215 document: &Document,
216 proto: Option<HandleObject>,
217 ) -> DomRoot<HTMLSelectElement> {
218 let n = Node::reflect_node_with_proto(
219 cx,
220 Box::new(HTMLSelectElement::new_inherited(
221 local_name, prefix, document,
222 )),
223 document,
224 proto,
225 );
226
227 n.upcast::<Node>().set_weird_parser_insertion_mode();
228 n
229 }
230
231 pub(crate) fn list_of_options<'b>(
233 &self,
234 no_gc: &'b NoGC,
235 ) -> impl Iterator<Item = UnrootedDom<'b, HTMLOptionElement>> + use<'b> {
236 let self_node = self.upcast::<Node>();
237
238 let mut iterator = self_node.traverse_preorder_unrooted(no_gc, ShadowIncluding::No);
240
241 iterator.next();
243
244 iter::from_fn(move || {
246 loop {
247 let node = iterator.peek()?.clone();
248
249 if skip_descendants_for_list_of_options(no_gc, &node) {
259 iterator.next_skipping_children();
260 } else {
261 iterator.next();
264 }
265
266 if let Some(option) = UnrootedDom::downcast::<HTMLOptionElement>(node) {
268 return Some(option);
269 }
270 }
271 })
272 }
273
274 fn get_placeholder_label_option(&self, no_gc: &NoGC) -> Option<DomRoot<HTMLOptionElement>> {
285 if self.Required() && !self.Multiple() && self.display_size() == 1 {
286 self.list_of_options(no_gc)
287 .next()
288 .filter(|node| {
289 node.Value().is_empty() &&
290 !has_ancestor_optgroup_between_self_and_select(no_gc, node.upcast())
291 })
292 .map(|node| node.as_rooted())
293 } else {
294 None
295 }
296 }
297
298 pub(crate) fn reset(&self, no_gc: &NoGC) {
300 for opt in self.list_of_options(no_gc) {
301 opt.set_selectedness(no_gc, opt.DefaultSelected());
302 opt.set_dirtiness(false);
303 }
304 self.selectedness_setting_algorithm(no_gc);
305 }
306
307 pub(crate) fn selectedness_setting_algorithm(&self, no_gc: &NoGC) -> bool {
309 if self.Multiple() {
311 return false;
312 }
313
314 let mut has_changed = false;
316 let mut first_enabled: Option<DomRoot<HTMLOptionElement>> = None;
318 let mut last_selected: Option<DomRoot<HTMLOptionElement>> = None;
320
321 for option in self.list_of_options(no_gc) {
323 if option.Selected() {
325 if let Some(last_selected) = &last_selected {
327 last_selected.set_selectedness(no_gc, false);
329 has_changed = true;
331 }
332 last_selected = Some(option.as_rooted());
334 }
335 let element = option.upcast::<Element>();
338 if first_enabled.is_none() && !element.disabled_state() {
339 first_enabled = Some(option.as_rooted());
340 }
341 }
342
343 if last_selected.is_none() &&
346 let Some(first_enabled) = first_enabled &&
347 self.display_size() == 1
348 {
349 first_enabled.set_selectedness(no_gc, true);
351 has_changed = true;
353 }
354
355 has_changed
357 }
358
359 pub(crate) fn push_form_data(&self, no_gc: &NoGC, data_set: &mut Vec<FormDatum>) {
360 if self.Name().is_empty() {
361 return;
362 }
363 for opt in self.list_of_options(no_gc) {
364 let element = opt.upcast::<Element>();
365 if opt.Selected() && element.enabled_state() {
366 data_set.push(FormDatum {
367 ty: self.Type(),
368 name: self.Name(),
369 value: FormDatumValue::String(opt.Value()),
370 });
371 }
372 }
373 }
374
375 pub(crate) fn pick_option(&self, no_gc: &NoGC, picked: &HTMLOptionElement) {
377 if !self.Multiple() {
378 let picked = picked.upcast();
379 for opt in self.list_of_options(no_gc) {
380 if opt.upcast::<HTMLElement>() != picked {
381 opt.set_selectedness(no_gc, false);
382 }
383 }
384 }
385 }
386
387 fn display_size(&self) -> u32 {
389 if self.Size() == 0 {
390 if self.Multiple() { 4 } else { 1 }
391 } else {
392 self.Size()
393 }
394 }
395
396 pub(crate) fn update_shadow_tree(&self, cx: &mut JSContext) {
397 let shadow_tree = self.shadow_tree(cx);
398
399 let selected_options = self.selected_options(cx.no_gc());
400 let selected_options_count = selected_options.len();
401
402 let displayed_text = if selected_options_count == 1 {
403 let first_selected_option = self
404 .selected_option(cx.no_gc())
405 .or_else(|| self.list_of_options(cx.no_gc()).next());
406
407 let first_selected_option_text = first_selected_option
408 .map(|option| option.displayed_label())
409 .unwrap_or_default();
410
411 itertools::join(first_selected_option_text.str().split_whitespace(), " ")
413 } else {
414 format!("{selected_options_count} selected")
415 };
416
417 shadow_tree
418 .selected_option
419 .upcast::<CharacterData>()
420 .SetData(cx, displayed_text.trim().into());
421 }
422
423 pub(crate) fn selected_option<'b>(
424 &self,
425 no_gc: &'b NoGC,
426 ) -> Option<UnrootedDom<'b, HTMLOptionElement>> {
427 self.list_of_options(no_gc)
428 .find(|opt_elem| opt_elem.Selected())
429 .or_else(|| self.list_of_options(no_gc).next())
430 }
431
432 pub(crate) fn selected_options<'b>(
433 &self,
434 no_gc: &'b NoGC,
435 ) -> Vec<UnrootedDom<'b, HTMLOptionElement>> {
436 self.list_of_options(no_gc)
437 .filter(|opt_elem| opt_elem.Selected())
438 .collect()
439 }
440
441 pub(crate) fn show_menu(&self, cx: &mut JSContext) {
442 let mut index = 0;
444 let mut embedder_option_from_option = |option: &HTMLOptionElement| {
445 let embedder_option = SelectElementOption {
446 id: index,
447 label: option.displayed_label().into(),
448 is_disabled: option.Disabled(),
449 };
450 index += 1;
451 embedder_option
452 };
453 let options = self
454 .upcast::<Node>()
455 .children()
456 .flat_map(|child| {
457 if let Some(option) = child.downcast::<HTMLOptionElement>() {
458 return Some(embedder_option_from_option(option).into());
459 }
460
461 if let Some(optgroup) = child.downcast::<HTMLOptGroupElement>() {
462 let options = optgroup
463 .upcast::<Node>()
464 .children()
465 .flat_map(DomRoot::downcast::<HTMLOptionElement>)
466 .map(|option| embedder_option_from_option(&option))
467 .collect();
468 let label = optgroup.Label().into();
469
470 return Some(SelectElementOptionOrOptgroup::Optgroup { label, options });
471 }
472
473 None
474 })
475 .collect();
476
477 let selected_options = self
478 .list_of_options(cx.no_gc())
479 .enumerate()
480 .filter(|(_, option)| option.Selected())
481 .map(|(index, _)| index)
482 .collect();
483
484 self.owner_document()
485 .embedder_controls()
486 .show_embedder_control(
487 cx,
488 ControlElement::Select(Dom::from_ref(self)),
489 EmbedderControlRequest::SelectElement(SelectElementRequest {
490 options,
491 selected_options,
492 allow_select_multiple: self.Multiple(),
493 }),
494 None,
495 );
496 self.upcast::<Element>().set_open_state(cx.no_gc(), true);
497 }
498
499 pub(crate) fn handle_embedder_response(&self, cx: &mut JSContext, selected_values: Vec<usize>) {
500 self.upcast::<Element>().set_open_state(cx.no_gc(), false);
501
502 let selected_values = if self.Multiple() {
503 selected_values
504 } else {
505 selected_values.into_iter().take(1).collect()
506 };
507
508 let mut selection_did_change = false;
509 for (index, option) in self.list_of_options(cx.no_gc()).enumerate() {
510 let should_be_selected = selected_values.contains(&index);
511 let option_selected_did_change = option.Selected() != should_be_selected;
512
513 if option_selected_did_change {
514 selection_did_change = true;
515 }
516
517 option.set_selectedness(cx.no_gc(), should_be_selected);
518
519 if option_selected_did_change {
520 option.set_dirtiness(true);
521 }
522 }
523
524 if selection_did_change {
525 self.update_shadow_tree(cx);
526 self.send_update_notifications();
527 }
528 }
529
530 fn multiple_attribute_mutated(&self, cx: &mut JSContext, mutation: AttributeMutation) {
531 if mutation.is_removal() {
532 let mut first_enabled: Option<DomRoot<HTMLOptionElement>> = None;
533 let mut first_selected: Option<DomRoot<HTMLOptionElement>> = None;
534
535 for option in self.list_of_options(cx.no_gc()) {
536 if first_selected.is_none() && option.Selected() {
537 first_selected = Some(DomRoot::from_ref(&option));
538 }
539 option.set_selectedness(cx.no_gc(), false);
540 let element = option.upcast::<Element>();
541 if first_enabled.is_none() && !element.disabled_state() {
542 first_enabled = Some(DomRoot::from_ref(&option));
543 }
544 }
545
546 if let Some(first_selected) = first_selected {
547 first_selected.set_selectedness(cx.no_gc(), true);
548 } else if self.display_size() == 1 &&
549 let Some(first_enabled) = first_enabled
550 {
551 first_enabled.set_selectedness(cx.no_gc(), true);
552 }
553
554 self.update_shadow_tree(cx);
555 }
556 }
557
558 fn send_update_notifications(&self) {
560 let this = Trusted::new(self);
563 self.owner_global()
564 .task_manager()
565 .user_interaction_task_source()
566 .queue(task!(send_select_update_notification: move |cx| {
567 let this = this.root();
568
569 this.upcast::<EventTarget>()
574 .fire_event_with_params(
575 cx,
576 atom!("input"),
577 EventBubbles::Bubbles,
578 EventCancelable::NotCancelable,
579 EventComposed::Composed,
580 );
581
582 this.upcast::<EventTarget>()
585 .fire_bubbling_event(cx, atom!("change"));
586 }));
587 }
588
589 fn may_have_embedder_control(&self) -> bool {
590 let el = self.upcast::<Element>();
591 !el.disabled_state()
592 }
593
594 pub(crate) fn get_enabled_selectedcontent(&self) -> Option<DomRoot<Element>> {
596 if self.Multiple() {
598 return None;
599 }
600
601 self.upcast::<Node>()
607 .traverse_preorder(ShadowIncluding::No)
608 .skip(1)
609 .filter_map(DomRoot::downcast::<Element>)
610 .find(|element| element.local_name() == &local_name!("selectedcontent"))
611 }
612
613 pub(crate) fn update_a_selects_descendant_selectedcontent_elements(&self, cx: &mut JSContext) {
615 if self.Multiple() {
617 return;
618 }
619
620 let descendant_selected_contents = self.get_enabled_selectedcontent();
623
624 if let Some(selected_content) = descendant_selected_contents {
630 self.update_a_selected_content(cx, &selected_content);
631 }
632 }
633
634 fn update_a_selected_content(&self, cx: &mut JSContext, selected_content: &Element) {
636 let option = self
643 .list_of_options(cx.no_gc())
644 .find(|option| option.Selected())
645 .map(|option| option.as_rooted());
646
647 match option {
648 None => {
649 Node::replace_all(cx, None, selected_content.upcast());
653 },
654 Some(option) => {
655 option.clone_an_option_into_selectedcontent(cx, selected_content);
658 },
659 }
660 }
661}
662
663impl HTMLSelectElementMethods<crate::DomTypeHolder> for HTMLSelectElement {
664 fn Add(
666 &self,
667 cx: &mut JSContext,
668 element: HTMLOptionElementOrHTMLOptGroupElement,
669 before: Option<HTMLElementOrLong>,
670 ) -> ErrorResult {
671 self.Options(cx).Add(cx, element, before)
672 }
673
674 make_bool_getter!(Disabled, "disabled");
676
677 make_bool_setter!(SetDisabled, "disabled");
679
680 fn GetForm(&self) -> Option<DomRoot<HTMLFormElement>> {
682 self.form_owner()
683 }
684
685 make_bool_getter!(Multiple, "multiple");
687
688 make_bool_setter!(SetMultiple, "multiple");
690
691 make_getter!(Name, "name");
693
694 make_atomic_setter!(SetName, "name");
696
697 make_bool_getter!(Required, "required");
699
700 make_bool_setter!(SetRequired, "required");
702
703 make_uint_getter!(Size, "size", DEFAULT_SELECT_SIZE);
705
706 make_uint_setter!(SetSize, "size", DEFAULT_SELECT_SIZE);
708
709 fn Type(&self) -> DOMString {
711 DOMString::from(if self.Multiple() {
712 "select-multiple"
713 } else {
714 "select-one"
715 })
716 }
717
718 make_labels_getter!(Labels, labels_node_list);
720
721 fn Options(&self, cx: &mut JSContext) -> DomRoot<HTMLOptionsCollection> {
723 self.options.or_init(|| {
724 let window = self.owner_window();
725 HTMLOptionsCollection::new(cx, &window, self, Box::new(OptionsFilter))
726 })
727 }
728
729 fn SelectedOptions(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
731 self.selected_options.or_init(|| {
732 let window = self.owner_window();
733 HTMLCollection::new_with_source(
734 cx,
735 &window,
736 self.upcast(),
737 Box::new(SelectedOptionsSource),
738 )
739 })
740 }
741
742 fn Length(&self, cx: &mut JSContext) -> u32 {
744 self.Options(cx).Length(cx)
745 }
746
747 fn SetLength(&self, cx: &mut JSContext, length: u32) {
749 self.Options(cx).SetLength(cx, length)
750 }
751
752 fn Item(&self, cx: &mut JSContext, index: u32) -> Option<DomRoot<Element>> {
754 self.Options(cx).upcast().Item(cx, index)
755 }
756
757 fn IndexedGetter(&self, cx: &mut JSContext, index: u32) -> Option<DomRoot<Element>> {
759 self.Options(cx).IndexedGetter(cx, index)
760 }
761
762 fn IndexedSetter(
764 &self,
765 cx: &mut JSContext,
766 index: u32,
767 value: Option<&HTMLOptionElement>,
768 ) -> ErrorResult {
769 self.Options(cx).IndexedSetter(cx, index, value)
770 }
771
772 fn NamedItem(&self, cx: &mut JSContext, name: DOMString) -> Option<DomRoot<HTMLOptionElement>> {
774 self.Options(cx)
775 .NamedGetter(cx, name)
776 .and_then(DomRoot::downcast::<HTMLOptionElement>)
777 }
778
779 fn Remove_(&self, cx: &mut JSContext, index: i32) {
781 self.Options(cx).Remove(cx, index)
782 }
783
784 fn Remove(&self, cx: &mut JSContext) {
786 self.upcast::<Element>().Remove(cx)
787 }
788
789 fn Value(&self, cx: &JSContext) -> DOMString {
791 self.list_of_options(cx.no_gc())
792 .find(|opt_elem| opt_elem.Selected())
793 .map(|opt_elem| opt_elem.Value())
794 .unwrap_or_default()
795 }
796
797 fn SetValue(&self, cx: &mut JSContext, value: DOMString) {
799 let mut opt_iter = self.list_of_options(cx.no_gc());
800 for opt in opt_iter.by_ref() {
802 if opt.Value() == value {
803 opt.set_selectedness(cx.no_gc(), true);
804 opt.set_dirtiness(true);
805 break;
806 }
807 opt.set_selectedness(cx.no_gc(), false);
808 }
809 for opt in opt_iter {
811 opt.set_selectedness(cx.no_gc(), false);
812 }
813
814 self.validity_state(cx)
815 .perform_validation_and_update(cx, ValidationFlags::VALUE_MISSING);
816
817 self.update_shadow_tree(cx);
818 }
819
820 fn SelectedIndex(&self, cx: &JSContext) -> i32 {
822 self.list_of_options(cx.no_gc())
823 .enumerate()
824 .filter(|(_, opt_elem)| opt_elem.Selected())
825 .map(|(i, _)| i as i32)
826 .next()
827 .unwrap_or(-1)
828 }
829
830 fn SetSelectedIndex(&self, cx: &mut JSContext, index: i32) {
832 let mut selection_did_change = false;
833
834 {
835 let mut opt_iter = self.list_of_options(cx.no_gc());
836 for opt in opt_iter.by_ref().take(index as usize) {
837 selection_did_change |= opt.Selected();
838 opt.set_selectedness(cx.no_gc(), false);
839 }
840 if let Some(selected_option) = opt_iter.next() {
841 selection_did_change |= !selected_option.Selected();
842 selected_option.set_selectedness(cx.no_gc(), true);
843 selected_option.set_dirtiness(true);
844
845 for opt in opt_iter {
847 selection_did_change |= opt.Selected();
848 opt.set_selectedness(cx.no_gc(), false);
849 }
850 }
851 }
852
853 if selection_did_change {
854 self.update_shadow_tree(cx);
855 }
856 }
857
858 fn WillValidate(&self, no_gc: &NoGC) -> bool {
860 self.is_instance_validatable(no_gc)
861 }
862
863 fn Validity(&self, cx: &mut JSContext) -> DomRoot<ValidityState> {
865 self.validity_state(cx)
866 }
867
868 fn CheckValidity(&self, cx: &mut JSContext) -> bool {
870 self.check_validity(cx)
871 }
872
873 fn ReportValidity(&self, cx: &mut JSContext) -> bool {
875 self.report_validity(cx)
876 }
877
878 fn ValidationMessage(&self, cx: &mut JSContext) -> DOMString {
880 self.validation_message(cx)
881 }
882
883 fn SetCustomValidity(&self, cx: &mut JSContext, error: DOMString) {
885 self.validity_state(cx).set_custom_error_message(cx, error);
886 }
887}
888
889impl VirtualMethods for HTMLSelectElement {
890 fn super_type(&self) -> Option<&dyn VirtualMethods> {
891 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
892 }
893
894 fn attribute_mutated(
895 &self,
896 cx: &mut js::context::JSContext,
897 attr: AttrRef<'_>,
898 mutation: AttributeMutation,
899 ) {
900 let could_have_had_embedder_control = self.may_have_embedder_control();
901 self.super_type()
902 .unwrap()
903 .attribute_mutated(cx, attr, mutation);
904 match *attr.local_name() {
905 local_name!("multiple") => {
906 self.multiple_attribute_mutated(cx, mutation);
907 },
908 local_name!("required") => {
909 self.validity_state(cx)
910 .perform_validation_and_update(cx, ValidationFlags::VALUE_MISSING);
911 },
912 local_name!("disabled") => {
913 self.validity_state(cx)
914 .perform_validation_and_update(cx, ValidationFlags::VALUE_MISSING);
915 },
916 local_name!("form") => {
917 self.form_attribute_mutated(cx, mutation);
918 },
919 _ => {},
920 }
921 if could_have_had_embedder_control && !self.may_have_embedder_control() {
922 self.owner_document()
923 .embedder_controls()
924 .hide_embedder_control(self.upcast());
925 }
926 }
927
928 fn unbind_from_tree(&self, cx: &mut JSContext, context: &UnbindContext) {
929 self.super_type().unwrap().unbind_from_tree(cx, context);
930 self.owner_document_unrooted(cx.no_gc())
931 .embedder_controls()
932 .hide_embedder_control(self.upcast());
933 }
934
935 fn children_changed(&self, cx: &mut JSContext, mutation: &ChildrenMutation) {
936 if let Some(s) = self.super_type() {
937 s.children_changed(cx, mutation);
938 }
939
940 self.update_shadow_tree(cx);
941 }
942
943 fn parse_plain_attribute(&self, local_name: &LocalName, value: DOMString) -> AttrValue {
944 match *local_name {
945 local_name!("size") => AttrValue::from_u32(value.into(), DEFAULT_SELECT_SIZE),
946 _ => self
947 .super_type()
948 .unwrap()
949 .parse_plain_attribute(local_name, value),
950 }
951 }
952
953 fn handle_event(&self, cx: &mut js::context::JSContext, event: &Event) {
954 self.super_type().unwrap().handle_event(cx, event);
955 if let Some(event) = event.downcast::<FocusEvent>() &&
956 *event.upcast::<Event>().type_() != *"blur"
957 {
958 self.owner_document()
959 .embedder_controls()
960 .hide_embedder_control(self.upcast());
961 }
962 }
963}
964
965impl FormControl for HTMLSelectElement {
966 fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
967 self.form_owner.get()
968 }
969
970 fn set_form_owner(&self, _cx: &mut JSContext, form: Option<&HTMLFormElement>) {
971 self.form_owner.set(form);
972 }
973
974 fn to_html_element(&self) -> &HTMLElement {
975 self.upcast::<HTMLElement>()
976 }
977}
978
979impl Validatable for HTMLSelectElement {
980 fn as_element(&self) -> &Element {
981 self.upcast()
982 }
983
984 fn validity_state(&self, cx: &mut JSContext) -> DomRoot<ValidityState> {
985 self.validity_state
986 .or_init(|| ValidityState::new(cx, &self.owner_window(), self.upcast()))
987 }
988
989 fn is_instance_validatable(&self, no_gc: &NoGC) -> bool {
990 !self.upcast::<Element>().disabled_state() &&
993 !is_barred_by_datalist_ancestor(no_gc, self.upcast())
994 }
995
996 fn perform_validation(
997 &self,
998 cx: &mut JSContext,
999 validate_flags: ValidationFlags,
1000 ) -> ValidationFlags {
1001 let mut failed_flags = ValidationFlags::empty();
1002
1003 if validate_flags.contains(ValidationFlags::VALUE_MISSING) && self.Required() {
1006 let placeholder = self.get_placeholder_label_option(cx.no_gc());
1007 let is_value_missing = !self.list_of_options(cx.no_gc()).any(|e| {
1008 e.Selected() &&
1009 placeholder
1010 .as_ref()
1011 .map(|placeholder| **placeholder != **e)
1012 .unwrap_or(true)
1013 });
1014 failed_flags.set(ValidationFlags::VALUE_MISSING, is_value_missing);
1015 }
1016
1017 failed_flags
1018 }
1019}
1020
1021impl Activatable for HTMLSelectElement {
1022 fn as_element(&self) -> &Element {
1023 self.upcast()
1024 }
1025
1026 fn is_instance_activatable(&self) -> bool {
1027 !self.upcast::<Element>().disabled_state()
1028 }
1029
1030 fn activation_behavior(
1031 &self,
1032 cx: &mut js::context::JSContext,
1033 event: &Event,
1034 _target: &EventTarget,
1035 ) {
1036 if !event.IsTrusted() {
1037 return;
1038 }
1039
1040 self.show_menu(cx);
1041 }
1042}
1043
1044fn skip_descendants_for_list_of_options(no_gc: &NoGC, node: &Node) -> bool {
1046 if node.is::<HTMLSelectElement>() ||
1052 node.is::<HTMLHRElement>() ||
1053 node.is::<HTMLOptionElement>() ||
1054 node.is::<HTMLDataListElement>()
1055 {
1056 return true;
1057 }
1058
1059 if node.is::<HTMLOptGroupElement>() &&
1065 has_ancestor_optgroup_between_self_and_select(no_gc, node)
1066 {
1067 return true;
1068 }
1069
1070 false
1071}
1072
1073fn has_ancestor_optgroup_between_self_and_select(no_gc: &NoGC, node: &Node) -> bool {
1074 for ancestor in node.ancestors_unrooted(no_gc) {
1075 if ancestor.is::<HTMLSelectElement>() {
1076 return false;
1077 }
1078 if ancestor.is::<HTMLOptGroupElement>() {
1079 return true;
1080 }
1081 }
1082 false
1083}