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::htmlfieldsetelement::HTMLFieldSetElement;
49use crate::dom::html::htmlformelement::{FormControl, FormDatum, FormDatumValue, HTMLFormElement};
50use crate::dom::html::htmloptgroupelement::HTMLOptGroupElement;
51use crate::dom::html::htmloptionelement::HTMLOptionElement;
52use crate::dom::html::htmloptionscollection::HTMLOptionsCollection;
53use crate::dom::node::{BindContext, 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;
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, elem: &'a Element, root: &'a Node) -> bool {
91 if !elem.is::<HTMLOptionElement>() {
92 return false;
93 }
94
95 let node = elem.upcast::<Node>();
96 if root.is_parent_of(node) {
97 return true;
98 }
99
100 match node.GetParentNode() {
101 Some(optgroup) => optgroup.is::<HTMLOptGroupElement>() && root.is_parent_of(&optgroup),
102 None => false,
103 }
104 }
105}
106
107#[derive(JSTraceable, MallocSizeOf)]
110struct SelectedOptionsSource;
111
112impl CollectionSource for SelectedOptionsSource {
113 fn iter<'b>(
114 &'b self,
115 no_gc: &'b NoGC,
116 root: &'b Node,
117 ) -> Box<dyn Iterator<Item = UnrootedDom<'b, Element>> + 'b> {
118 let select = root
119 .downcast::<HTMLSelectElement>()
120 .expect("SelectedOptionsSource must be rooted on an HTMLSelectElement");
121 Box::new(
122 select
123 .list_of_options(no_gc)
124 .filter(|option| option.Selected())
125 .map(UnrootedDom::upcast::<Element>),
126 )
127 }
128}
129
130#[dom_struct]
131pub(crate) struct HTMLSelectElement {
132 htmlelement: HTMLElement,
133 options: MutNullableDom<HTMLOptionsCollection>,
134 selected_options: MutNullableDom<HTMLCollection>,
135 form_owner: MutNullableDom<HTMLFormElement>,
136 labels_node_list: MutNullableDom<NodeList>,
137 validity_state: MutNullableDom<ValidityState>,
138 shadow_tree: DomRefCell<Option<ShadowTree>>,
139}
140
141#[derive(Clone, JSTraceable, MallocSizeOf)]
143#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
144struct ShadowTree {
145 selected_option: Dom<Text>,
146}
147
148impl UAShadowRoot<ShadowTree> for HTMLSelectElement {
149 fn store_for_shadow_tree(&self, cx: &mut JSContext, shadow_root: DomRoot<ShadowRoot>) {
150 let document = self.owner_document();
151
152 let select_box = self.create_element_in_ua_shadowroot(cx, local_name!("div"));
153 select_box.set_string_attribute(cx, &local_name!("style"), SELECT_BOX_STYLE.into());
154 shadow_root
155 .upcast::<Node>()
156 .AppendChild(cx, select_box.upcast::<Node>())
157 .unwrap();
158
159 let text_container = self.create_element_in_ua_shadowroot(cx, local_name!("div"));
160 text_container.set_string_attribute(cx, &local_name!("style"), TEXT_CONTAINER_STYLE.into());
161 select_box
162 .upcast::<Node>()
163 .AppendChild(cx, text_container.upcast::<Node>())
164 .unwrap();
165
166 let text = Text::new(cx, DOMString::new(), &document);
167 text_container
168 .upcast::<Node>()
169 .AppendChild(cx, text.upcast::<Node>())
170 .unwrap();
171
172 let chevron_container = self.create_element_in_ua_shadowroot(cx, local_name!("div"));
173 chevron_container.set_string_attribute(
174 cx,
175 &local_name!("style"),
176 CHEVRON_CONTAINER_STYLE.into(),
177 );
178 select_box
179 .upcast::<Node>()
180 .AppendChild(cx, chevron_container.upcast::<Node>())
181 .unwrap();
182
183 let _ = self.shadow_tree.borrow_mut().insert(ShadowTree {
184 selected_option: text.as_traced(),
185 });
186 }
187
188 fn borrow_for_shadow_tree(&self) -> Ref<'_, Option<ShadowTree>> {
189 self.shadow_tree.borrow()
190 }
191}
192
193impl HTMLSelectElement {
194 fn new_inherited(
195 local_name: LocalName,
196 prefix: Option<Prefix>,
197 document: &Document,
198 ) -> HTMLSelectElement {
199 HTMLSelectElement {
200 htmlelement: HTMLElement::new_inherited_with_state(
201 ElementState::ENABLED | ElementState::VALID,
202 local_name,
203 prefix,
204 document,
205 ),
206 options: Default::default(),
207 selected_options: Default::default(),
208 form_owner: Default::default(),
209 labels_node_list: Default::default(),
210 validity_state: Default::default(),
211 shadow_tree: Default::default(),
212 }
213 }
214
215 pub(crate) fn new(
216 cx: &mut js::context::JSContext,
217 local_name: LocalName,
218 prefix: Option<Prefix>,
219 document: &Document,
220 proto: Option<HandleObject>,
221 ) -> DomRoot<HTMLSelectElement> {
222 let n = Node::reflect_node_with_proto(
223 cx,
224 Box::new(HTMLSelectElement::new_inherited(
225 local_name, prefix, document,
226 )),
227 document,
228 proto,
229 );
230
231 n.upcast::<Node>().set_weird_parser_insertion_mode();
232 n
233 }
234
235 pub(crate) fn list_of_options<'b>(
237 &self,
238 no_gc: &'b NoGC,
239 ) -> impl Iterator<Item = UnrootedDom<'b, HTMLOptionElement>> + use<'b> {
240 self.upcast::<Node>()
241 .children_unrooted(no_gc)
242 .flat_map(|node| {
243 if node.is::<HTMLOptionElement>() {
244 let node = UnrootedDom::downcast::<HTMLOptionElement>(node).unwrap();
245 Choice3::First(iter::once(node))
246 } else if node.is::<HTMLOptGroupElement>() {
247 Choice3::Second(
248 node.children_unrooted(no_gc)
249 .filter_map(UnrootedDom::downcast),
250 )
251 } else {
252 Choice3::Third(iter::empty())
253 }
254 })
255 }
256
257 fn get_placeholder_label_option(&self, no_gc: &NoGC) -> Option<DomRoot<HTMLOptionElement>> {
259 if self.Required() && !self.Multiple() && self.display_size() == 1 {
260 self.list_of_options(no_gc)
261 .next()
262 .filter(|node| {
263 let parent = node.upcast::<Node>().GetParentNode();
264 node.Value().is_empty() && parent.as_deref() == Some(self.upcast())
265 })
266 .map(|node| node.as_rooted())
267 } else {
268 None
269 }
270 }
271
272 pub(crate) fn reset(&self, no_gc: &NoGC) {
274 for opt in self.list_of_options(no_gc) {
275 opt.set_selectedness(no_gc, opt.DefaultSelected());
276 opt.set_dirtiness(false);
277 }
278 self.ask_for_reset(no_gc);
279 }
280
281 pub(crate) fn ask_for_reset(&self, no_gc: &NoGC) {
283 if self.Multiple() {
284 return;
285 }
286
287 let mut first_enabled: Option<DomRoot<HTMLOptionElement>> = None;
288 let mut last_selected: Option<DomRoot<HTMLOptionElement>> = None;
289
290 for opt in self.list_of_options(no_gc) {
291 if opt.Selected() {
292 opt.set_selectedness(no_gc, false);
293 last_selected = Some(DomRoot::from_ref(&opt));
294 }
295 let element = opt.upcast::<Element>();
296 if first_enabled.is_none() && !element.disabled_state() {
297 first_enabled = Some(DomRoot::from_ref(&opt));
298 }
299 }
300
301 if let Some(last_selected) = last_selected {
302 last_selected.set_selectedness(no_gc, true);
303 } else if self.display_size() == 1 &&
304 let Some(first_enabled) = first_enabled
305 {
306 first_enabled.set_selectedness(no_gc, true);
307 }
308 }
309
310 pub(crate) fn push_form_data(&self, no_gc: &NoGC, data_set: &mut Vec<FormDatum>) {
311 if self.Name().is_empty() {
312 return;
313 }
314 for opt in self.list_of_options(no_gc) {
315 let element = opt.upcast::<Element>();
316 if opt.Selected() && element.enabled_state() {
317 data_set.push(FormDatum {
318 ty: self.Type(),
319 name: self.Name(),
320 value: FormDatumValue::String(opt.Value()),
321 });
322 }
323 }
324 }
325
326 pub(crate) fn pick_option(&self, no_gc: &NoGC, picked: &HTMLOptionElement) {
328 if !self.Multiple() {
329 let picked = picked.upcast();
330 for opt in self.list_of_options(no_gc) {
331 if opt.upcast::<HTMLElement>() != picked {
332 opt.set_selectedness(no_gc, false);
333 }
334 }
335 }
336 }
337
338 fn display_size(&self) -> u32 {
340 if self.Size() == 0 {
341 if self.Multiple() { 4 } else { 1 }
342 } else {
343 self.Size()
344 }
345 }
346
347 pub(crate) fn update_shadow_tree(&self, cx: &mut JSContext) {
348 let shadow_tree = self.shadow_tree(cx);
349
350 let selected_options = self.selected_options(cx.no_gc());
351 let selected_options_count = selected_options.len();
352
353 let displayed_text = if selected_options_count == 1 {
354 let first_selected_option = self
355 .selected_option(cx.no_gc())
356 .or_else(|| self.list_of_options(cx.no_gc()).next());
357
358 let first_selected_option_text = first_selected_option
359 .map(|option| option.displayed_label())
360 .unwrap_or_default();
361
362 itertools::join(first_selected_option_text.str().split_whitespace(), " ")
364 } else {
365 format!("{selected_options_count} selected")
366 };
367
368 shadow_tree
369 .selected_option
370 .upcast::<CharacterData>()
371 .SetData(cx, displayed_text.trim().into());
372 }
373
374 pub(crate) fn selected_option<'b>(
375 &self,
376 no_gc: &'b NoGC,
377 ) -> Option<UnrootedDom<'b, HTMLOptionElement>> {
378 self.list_of_options(no_gc)
379 .find(|opt_elem| opt_elem.Selected())
380 .or_else(|| self.list_of_options(no_gc).next())
381 }
382
383 pub(crate) fn selected_options<'b>(
384 &self,
385 no_gc: &'b NoGC,
386 ) -> Vec<UnrootedDom<'b, HTMLOptionElement>> {
387 self.list_of_options(no_gc)
388 .filter(|opt_elem| opt_elem.Selected())
389 .collect()
390 }
391
392 pub(crate) fn show_menu(&self, no_gc: &NoGC) {
393 let mut index = 0;
395 let mut embedder_option_from_option = |option: &HTMLOptionElement| {
396 let embedder_option = SelectElementOption {
397 id: index,
398 label: option.displayed_label().into(),
399 is_disabled: option.Disabled(),
400 };
401 index += 1;
402 embedder_option
403 };
404 let options = self
405 .upcast::<Node>()
406 .children()
407 .flat_map(|child| {
408 if let Some(option) = child.downcast::<HTMLOptionElement>() {
409 return Some(embedder_option_from_option(option).into());
410 }
411
412 if let Some(optgroup) = child.downcast::<HTMLOptGroupElement>() {
413 let options = optgroup
414 .upcast::<Node>()
415 .children()
416 .flat_map(DomRoot::downcast::<HTMLOptionElement>)
417 .map(|option| embedder_option_from_option(&option))
418 .collect();
419 let label = optgroup.Label().into();
420
421 return Some(SelectElementOptionOrOptgroup::Optgroup { label, options });
422 }
423
424 None
425 })
426 .collect();
427
428 let selected_options = self
429 .list_of_options(no_gc)
430 .enumerate()
431 .filter(|(_, option)| option.Selected())
432 .map(|(index, _)| index)
433 .collect();
434
435 self.owner_document()
436 .embedder_controls()
437 .show_embedder_control(
438 ControlElement::Select(Dom::from_ref(self)),
439 EmbedderControlRequest::SelectElement(SelectElementRequest {
440 options,
441 selected_options,
442 allow_select_multiple: self.Multiple(),
443 }),
444 None,
445 );
446 self.upcast::<Element>().set_open_state(true);
447 }
448
449 pub(crate) fn handle_embedder_response(&self, cx: &mut JSContext, selected_values: Vec<usize>) {
450 self.upcast::<Element>().set_open_state(false);
451
452 let selected_values = if self.Multiple() {
453 selected_values
454 } else {
455 selected_values.into_iter().take(1).collect()
456 };
457
458 let mut selection_did_change = false;
459 for (index, option) in self.list_of_options(cx.no_gc()).enumerate() {
460 let should_be_selected = selected_values.contains(&index);
461 let option_selected_did_change = option.Selected() != should_be_selected;
462
463 if option_selected_did_change {
464 selection_did_change = true;
465 }
466
467 option.set_selectedness(cx.no_gc(), should_be_selected);
468
469 if option_selected_did_change {
470 option.set_dirtiness(true);
471 }
472 }
473
474 if selection_did_change {
475 self.update_shadow_tree(cx);
476 self.send_update_notifications();
477 }
478 }
479
480 fn multiple_attribute_mutated(&self, cx: &mut JSContext, mutation: AttributeMutation) {
481 if mutation.is_removal() {
482 let mut first_enabled: Option<DomRoot<HTMLOptionElement>> = None;
483 let mut first_selected: Option<DomRoot<HTMLOptionElement>> = None;
484
485 for option in self.list_of_options(cx.no_gc()) {
486 if first_selected.is_none() && option.Selected() {
487 first_selected = Some(DomRoot::from_ref(&option));
488 }
489 option.set_selectedness(cx.no_gc(), false);
490 let element = option.upcast::<Element>();
491 if first_enabled.is_none() && !element.disabled_state() {
492 first_enabled = Some(DomRoot::from_ref(&option));
493 }
494 }
495
496 if let Some(first_selected) = first_selected {
497 first_selected.set_selectedness(cx.no_gc(), true);
498 } else if self.display_size() == 1 &&
499 let Some(first_enabled) = first_enabled
500 {
501 first_enabled.set_selectedness(cx.no_gc(), true);
502 }
503
504 self.update_shadow_tree(cx);
505 }
506 }
507
508 fn send_update_notifications(&self) {
510 let this = Trusted::new(self);
513 self.owner_global()
514 .task_manager()
515 .user_interaction_task_source()
516 .queue(task!(send_select_update_notification: move |cx| {
517 let this = this.root();
518
519 this.upcast::<EventTarget>()
524 .fire_event_with_params(
525 cx,
526 atom!("input"),
527 EventBubbles::Bubbles,
528 EventCancelable::NotCancelable,
529 EventComposed::Composed,
530 );
531
532 this.upcast::<EventTarget>()
535 .fire_bubbling_event(cx, atom!("change"));
536 }));
537 }
538
539 fn may_have_embedder_control(&self) -> bool {
540 let el = self.upcast::<Element>();
541 !el.disabled_state()
542 }
543
544 pub(crate) fn get_enabled_selectedcontent(&self) -> Option<DomRoot<Element>> {
546 if self.Multiple() {
548 return None;
549 }
550
551 self.upcast::<Node>()
557 .traverse_preorder(ShadowIncluding::No)
558 .skip(1)
559 .filter_map(DomRoot::downcast::<Element>)
560 .find(|element| element.local_name() == &local_name!("selectedcontent"))
561 }
562}
563
564impl HTMLSelectElementMethods<crate::DomTypeHolder> for HTMLSelectElement {
565 fn Add(
567 &self,
568 cx: &mut JSContext,
569 element: HTMLOptionElementOrHTMLOptGroupElement,
570 before: Option<HTMLElementOrLong>,
571 ) -> ErrorResult {
572 self.Options(cx).Add(cx, element, before)
573 }
574
575 make_bool_getter!(Disabled, "disabled");
577
578 make_bool_setter!(SetDisabled, "disabled");
580
581 fn GetForm(&self) -> Option<DomRoot<HTMLFormElement>> {
583 self.form_owner()
584 }
585
586 make_bool_getter!(Multiple, "multiple");
588
589 make_bool_setter!(SetMultiple, "multiple");
591
592 make_getter!(Name, "name");
594
595 make_atomic_setter!(SetName, "name");
597
598 make_bool_getter!(Required, "required");
600
601 make_bool_setter!(SetRequired, "required");
603
604 make_uint_getter!(Size, "size", DEFAULT_SELECT_SIZE);
606
607 make_uint_setter!(SetSize, "size", DEFAULT_SELECT_SIZE);
609
610 fn Type(&self) -> DOMString {
612 DOMString::from(if self.Multiple() {
613 "select-multiple"
614 } else {
615 "select-one"
616 })
617 }
618
619 make_labels_getter!(Labels, labels_node_list);
621
622 fn Options(&self, cx: &mut JSContext) -> DomRoot<HTMLOptionsCollection> {
624 self.options.or_init(|| {
625 let window = self.owner_window();
626 HTMLOptionsCollection::new(cx, &window, self, Box::new(OptionsFilter))
627 })
628 }
629
630 fn SelectedOptions(&self, cx: &mut JSContext) -> DomRoot<HTMLCollection> {
632 self.selected_options.or_init(|| {
633 let window = self.owner_window();
634 HTMLCollection::new_with_source(
635 cx,
636 &window,
637 self.upcast(),
638 Box::new(SelectedOptionsSource),
639 )
640 })
641 }
642
643 fn Length(&self, cx: &mut JSContext) -> u32 {
645 self.Options(cx).Length(cx)
646 }
647
648 fn SetLength(&self, cx: &mut JSContext, length: u32) {
650 self.Options(cx).SetLength(cx, length)
651 }
652
653 fn Item(&self, cx: &mut JSContext, index: u32) -> Option<DomRoot<Element>> {
655 self.Options(cx).upcast().Item(cx, index)
656 }
657
658 fn IndexedGetter(&self, cx: &mut JSContext, index: u32) -> Option<DomRoot<Element>> {
660 self.Options(cx).IndexedGetter(cx, index)
661 }
662
663 fn IndexedSetter(
665 &self,
666 cx: &mut JSContext,
667 index: u32,
668 value: Option<&HTMLOptionElement>,
669 ) -> ErrorResult {
670 self.Options(cx).IndexedSetter(cx, index, value)
671 }
672
673 fn NamedItem(&self, cx: &mut JSContext, name: DOMString) -> Option<DomRoot<HTMLOptionElement>> {
675 self.Options(cx)
676 .NamedGetter(cx, name)
677 .and_then(DomRoot::downcast::<HTMLOptionElement>)
678 }
679
680 fn Remove_(&self, cx: &mut JSContext, index: i32) {
682 self.Options(cx).Remove(cx, index)
683 }
684
685 fn Remove(&self, cx: &mut JSContext) {
687 self.upcast::<Element>().Remove(cx)
688 }
689
690 fn Value(&self, cx: &JSContext) -> DOMString {
692 self.list_of_options(cx.no_gc())
693 .find(|opt_elem| opt_elem.Selected())
694 .map(|opt_elem| opt_elem.Value())
695 .unwrap_or_default()
696 }
697
698 fn SetValue(&self, cx: &mut JSContext, value: DOMString) {
700 let mut opt_iter = self.list_of_options(cx.no_gc());
701 for opt in opt_iter.by_ref() {
703 if opt.Value() == value {
704 opt.set_selectedness(cx.no_gc(), true);
705 opt.set_dirtiness(true);
706 break;
707 }
708 opt.set_selectedness(cx.no_gc(), false);
709 }
710 for opt in opt_iter {
712 opt.set_selectedness(cx.no_gc(), false);
713 }
714
715 self.validity_state(cx)
716 .perform_validation_and_update(cx, ValidationFlags::VALUE_MISSING);
717
718 self.update_shadow_tree(cx);
719 }
720
721 fn SelectedIndex(&self, cx: &JSContext) -> i32 {
723 self.list_of_options(cx.no_gc())
724 .enumerate()
725 .filter(|(_, opt_elem)| opt_elem.Selected())
726 .map(|(i, _)| i as i32)
727 .next()
728 .unwrap_or(-1)
729 }
730
731 fn SetSelectedIndex(&self, cx: &mut JSContext, index: i32) {
733 let mut selection_did_change = false;
734
735 {
736 let mut opt_iter = self.list_of_options(cx.no_gc());
737 for opt in opt_iter.by_ref().take(index as usize) {
738 selection_did_change |= opt.Selected();
739 opt.set_selectedness(cx.no_gc(), false);
740 }
741 if let Some(selected_option) = opt_iter.next() {
742 selection_did_change |= !selected_option.Selected();
743 selected_option.set_selectedness(cx.no_gc(), true);
744 selected_option.set_dirtiness(true);
745
746 for opt in opt_iter {
748 selection_did_change |= opt.Selected();
749 opt.set_selectedness(cx.no_gc(), false);
750 }
751 }
752 }
753
754 if selection_did_change {
755 self.update_shadow_tree(cx);
756 }
757 }
758
759 fn WillValidate(&self, no_gc: &NoGC) -> bool {
761 self.is_instance_validatable(no_gc)
762 }
763
764 fn Validity(&self, cx: &mut JSContext) -> DomRoot<ValidityState> {
766 self.validity_state(cx)
767 }
768
769 fn CheckValidity(&self, cx: &mut JSContext) -> bool {
771 self.check_validity(cx)
772 }
773
774 fn ReportValidity(&self, cx: &mut JSContext) -> bool {
776 self.report_validity(cx)
777 }
778
779 fn ValidationMessage(&self, cx: &mut JSContext) -> DOMString {
781 self.validation_message(cx)
782 }
783
784 fn SetCustomValidity(&self, cx: &mut JSContext, error: DOMString) {
786 self.validity_state(cx).set_custom_error_message(cx, error);
787 }
788}
789
790impl VirtualMethods for HTMLSelectElement {
791 fn super_type(&self) -> Option<&dyn VirtualMethods> {
792 Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
793 }
794
795 fn attribute_mutated(
796 &self,
797 cx: &mut js::context::JSContext,
798 attr: AttrRef<'_>,
799 mutation: AttributeMutation,
800 ) {
801 let could_have_had_embedder_control = self.may_have_embedder_control();
802 self.super_type()
803 .unwrap()
804 .attribute_mutated(cx, attr, mutation);
805 match *attr.local_name() {
806 local_name!("multiple") => {
807 self.multiple_attribute_mutated(cx, mutation);
808 },
809 local_name!("required") => {
810 self.validity_state(cx)
811 .perform_validation_and_update(cx, ValidationFlags::VALUE_MISSING);
812 },
813 local_name!("disabled") => {
814 let el = self.upcast::<Element>();
815 match mutation {
816 AttributeMutation::Set(_) => {
817 el.set_disabled_state(true);
818 el.set_enabled_state(false);
819 },
820 AttributeMutation::Removed => {
821 el.set_disabled_state(false);
822 el.set_enabled_state(true);
823 el.check_ancestors_disabled_state_for_form_control(cx.no_gc());
824 },
825 }
826
827 self.validity_state(cx)
828 .perform_validation_and_update(cx, ValidationFlags::VALUE_MISSING);
829 },
830 local_name!("form") => {
831 self.form_attribute_mutated(cx, mutation);
832 },
833 _ => {},
834 }
835 if could_have_had_embedder_control && !self.may_have_embedder_control() {
836 self.owner_document()
837 .embedder_controls()
838 .hide_embedder_control(self.upcast());
839 }
840 }
841
842 fn bind_to_tree(&self, cx: &mut JSContext, context: &BindContext) {
843 if let Some(s) = self.super_type() {
844 s.bind_to_tree(cx, context);
845 }
846
847 self.upcast::<Element>()
848 .check_ancestors_disabled_state_for_form_control(cx.no_gc());
849 }
850
851 fn unbind_from_tree(&self, cx: &mut JSContext, context: &UnbindContext) {
852 self.super_type().unwrap().unbind_from_tree(cx, context);
853
854 let node = self.upcast::<Node>();
855 let el = self.upcast::<Element>();
856 if node
857 .ancestors()
858 .any(|ancestor| ancestor.is::<HTMLFieldSetElement>())
859 {
860 el.check_ancestors_disabled_state_for_form_control(cx.no_gc());
861 } else {
862 el.check_disabled_attribute();
863 }
864
865 self.owner_document()
866 .embedder_controls()
867 .hide_embedder_control(self.upcast());
868 }
869
870 fn children_changed(&self, cx: &mut JSContext, mutation: &ChildrenMutation) {
871 if let Some(s) = self.super_type() {
872 s.children_changed(cx, mutation);
873 }
874
875 self.update_shadow_tree(cx);
876 }
877
878 fn parse_plain_attribute(&self, local_name: &LocalName, value: DOMString) -> AttrValue {
879 match *local_name {
880 local_name!("size") => AttrValue::from_u32(value.into(), DEFAULT_SELECT_SIZE),
881 _ => self
882 .super_type()
883 .unwrap()
884 .parse_plain_attribute(local_name, value),
885 }
886 }
887
888 fn handle_event(&self, cx: &mut js::context::JSContext, event: &Event) {
889 self.super_type().unwrap().handle_event(cx, event);
890 if let Some(event) = event.downcast::<FocusEvent>() &&
891 *event.upcast::<Event>().type_() != *"blur"
892 {
893 self.owner_document()
894 .embedder_controls()
895 .hide_embedder_control(self.upcast());
896 }
897 }
898}
899
900impl FormControl for HTMLSelectElement {
901 fn form_owner(&self) -> Option<DomRoot<HTMLFormElement>> {
902 self.form_owner.get()
903 }
904
905 fn set_form_owner(&self, _cx: &mut JSContext, form: Option<&HTMLFormElement>) {
906 self.form_owner.set(form);
907 }
908
909 fn to_html_element(&self) -> &HTMLElement {
910 self.upcast::<HTMLElement>()
911 }
912}
913
914impl Validatable for HTMLSelectElement {
915 fn as_element(&self) -> &Element {
916 self.upcast()
917 }
918
919 fn validity_state(&self, cx: &mut JSContext) -> DomRoot<ValidityState> {
920 self.validity_state
921 .or_init(|| ValidityState::new(cx, &self.owner_window(), self.upcast()))
922 }
923
924 fn is_instance_validatable(&self, no_gc: &NoGC) -> bool {
925 !self.upcast::<Element>().disabled_state() &&
928 !is_barred_by_datalist_ancestor(no_gc, self.upcast())
929 }
930
931 fn perform_validation(
932 &self,
933 cx: &mut JSContext,
934 validate_flags: ValidationFlags,
935 ) -> ValidationFlags {
936 let mut failed_flags = ValidationFlags::empty();
937
938 if validate_flags.contains(ValidationFlags::VALUE_MISSING) && self.Required() {
941 let placeholder = self.get_placeholder_label_option(cx.no_gc());
942 let is_value_missing = !self.list_of_options(cx.no_gc()).any(|e| {
943 e.Selected() &&
944 placeholder
945 .as_ref()
946 .map(|placeholder| **placeholder != **e)
947 .unwrap_or(true)
948 });
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(cx.no_gc());
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}