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