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