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