script/dom/html/
htmloptionscollection.rs1use std::cmp::Ordering;
6
7use dom_struct::dom_struct;
8use html5ever::{QualName, local_name, ns};
9
10use crate::dom::bindings::codegen::Bindings::ElementBinding::ElementMethods;
11use crate::dom::bindings::codegen::Bindings::HTMLCollectionBinding::HTMLCollectionMethods;
12use crate::dom::bindings::codegen::Bindings::HTMLOptionsCollectionBinding::HTMLOptionsCollectionMethods;
13use crate::dom::bindings::codegen::Bindings::HTMLSelectElementBinding::HTMLSelectElementMethods;
14use crate::dom::bindings::codegen::Bindings::NodeBinding::Node_Binding::NodeMethods;
15use crate::dom::bindings::codegen::UnionTypes::{
16 HTMLElementOrLong, HTMLOptionElementOrHTMLOptGroupElement,
17};
18use crate::dom::bindings::error::{Error, ErrorResult};
19use crate::dom::bindings::inheritance::Castable;
20use crate::dom::bindings::reflector::reflect_dom_object;
21use crate::dom::bindings::root::DomRoot;
22use crate::dom::bindings::str::DOMString;
23use crate::dom::element::{CustomElementCreationMode, Element, ElementCreator};
24use crate::dom::html::htmlcollection::{CollectionFilter, HTMLCollection};
25use crate::dom::html::htmloptionelement::HTMLOptionElement;
26use crate::dom::html::htmlselectelement::HTMLSelectElement;
27use crate::dom::node::{Node, NodeTraits};
28use crate::dom::window::Window;
29use crate::script_runtime::CanGc;
30
31#[dom_struct]
32pub(crate) struct HTMLOptionsCollection {
33 collection: HTMLCollection,
34}
35
36impl HTMLOptionsCollection {
37 fn new_inherited(
38 select: &HTMLSelectElement,
39 filter: Box<dyn CollectionFilter + 'static>,
40 ) -> HTMLOptionsCollection {
41 HTMLOptionsCollection {
42 collection: HTMLCollection::new_inherited(select.upcast(), filter),
43 }
44 }
45
46 pub(crate) fn new(
47 window: &Window,
48 select: &HTMLSelectElement,
49 filter: Box<dyn CollectionFilter + 'static>,
50 can_gc: CanGc,
51 ) -> DomRoot<HTMLOptionsCollection> {
52 reflect_dom_object(
53 Box::new(HTMLOptionsCollection::new_inherited(select, filter)),
54 window,
55 can_gc,
56 )
57 }
58
59 fn add_new_elements(&self, count: u32, can_gc: CanGc) -> ErrorResult {
60 let root = self.upcast().root_node();
61 let document = root.owner_document();
62
63 for _ in 0..count {
64 let element = Element::create(
65 QualName::new(None, ns!(html), local_name!("option")),
66 None,
67 &document,
68 ElementCreator::ScriptCreated,
69 CustomElementCreationMode::Asynchronous,
70 None,
71 can_gc,
72 );
73 let node = element.upcast::<Node>();
74 root.AppendChild(node, can_gc)?;
75 }
76 Ok(())
77 }
78}
79
80impl HTMLOptionsCollectionMethods<crate::DomTypeHolder> for HTMLOptionsCollection {
81 fn NamedGetter(&self, name: DOMString) -> Option<DomRoot<Element>> {
87 self.upcast().NamedItem(name)
88 }
89
90 fn SupportedPropertyNames(&self) -> Vec<DOMString> {
92 self.upcast().SupportedPropertyNames()
93 }
94
95 fn IndexedGetter(&self, index: u32) -> Option<DomRoot<Element>> {
101 self.upcast().IndexedGetter(index)
102 }
103
104 fn IndexedSetter(
106 &self,
107 index: u32,
108 value: Option<&HTMLOptionElement>,
109 can_gc: CanGc,
110 ) -> ErrorResult {
111 if let Some(value) = value {
112 let length = self.upcast().Length();
114
115 let n = index as i32 - length as i32;
117
118 if n > 0 {
120 self.add_new_elements(n as u32, can_gc)?;
121 }
122
123 let node = value.upcast::<Node>();
125 let root = self.upcast().root_node();
126 if n >= 0 {
127 Node::pre_insert(node, &root, None, can_gc).map(|_| ())
128 } else {
129 let child = self.upcast().IndexedGetter(index).unwrap();
130 let child_node = child.upcast::<Node>();
131
132 root.ReplaceChild(node, child_node, can_gc).map(|_| ())
133 }
134 } else {
135 self.Remove(index as i32);
137 Ok(())
138 }
139 }
140
141 fn Length(&self) -> u32 {
143 self.upcast().Length()
144 }
145
146 fn SetLength(&self, length: u32, can_gc: CanGc) {
148 let current = self.upcast().Length();
150
151 match length.cmp(¤t) {
152 Ordering::Greater => {
154 if length > 100_000 {
156 return;
157 }
158
159 let n = length - current;
161
162 self.add_new_elements(n, can_gc).unwrap();
165 },
166 Ordering::Less => {
168 for index in (length..current).rev() {
171 self.Remove(index as i32)
172 }
173 },
174 _ => {},
175 }
176 }
177
178 fn Add(
180 &self,
181 element: HTMLOptionElementOrHTMLOptGroupElement,
182 before: Option<HTMLElementOrLong>,
183 ) -> ErrorResult {
184 let root = self.upcast().root_node();
185
186 let node: &Node = match element {
187 HTMLOptionElementOrHTMLOptGroupElement::HTMLOptionElement(ref element) => {
188 element.upcast()
189 },
190 HTMLOptionElementOrHTMLOptGroupElement::HTMLOptGroupElement(ref element) => {
191 element.upcast()
192 },
193 };
194
195 if node.is_ancestor_of(&root) {
197 return Err(Error::HierarchyRequest);
198 }
199
200 if let Some(HTMLElementOrLong::HTMLElement(ref before_element)) = before {
201 let before_node = before_element.upcast::<Node>();
203 if !root.is_ancestor_of(before_node) {
204 return Err(Error::NotFound(None));
205 }
206
207 if node == before_node {
209 return Ok(());
210 }
211 }
212
213 let reference_node = before.and_then(|before| match before {
215 HTMLElementOrLong::HTMLElement(element) => Some(DomRoot::upcast::<Node>(element)),
216 HTMLElementOrLong::Long(index) => self
217 .upcast()
218 .IndexedGetter(index as u32)
219 .map(DomRoot::upcast::<Node>),
220 });
221
222 let parent = if let Some(ref reference_node) = reference_node {
224 reference_node.GetParentNode().unwrap()
225 } else {
226 root
227 };
228
229 Node::pre_insert(node, &parent, reference_node.as_deref(), CanGc::note()).map(|_| ())
231 }
232
233 fn Remove(&self, index: i32) {
235 if let Some(element) = self.upcast().IndexedGetter(index as u32) {
236 element.Remove(CanGc::note());
237 }
238 }
239
240 fn SelectedIndex(&self) -> i32 {
242 self.upcast()
243 .root_node()
244 .downcast::<HTMLSelectElement>()
245 .expect("HTMLOptionsCollection not rooted on a HTMLSelectElement")
246 .SelectedIndex()
247 }
248
249 fn SetSelectedIndex(&self, index: i32, can_gc: CanGc) {
251 self.upcast()
252 .root_node()
253 .downcast::<HTMLSelectElement>()
254 .expect("HTMLOptionsCollection not rooted on a HTMLSelectElement")
255 .SetSelectedIndex(index, can_gc)
256 }
257}