Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "mozilla/dom/HTMLOptionsCollection.h"
8 :
9 : #include "HTMLOptGroupElement.h"
10 : #include "mozAutoDocUpdate.h"
11 : #include "mozilla/dom/BindingUtils.h"
12 : #include "mozilla/dom/Element.h"
13 : #include "mozilla/GenericSpecifiedValuesInlines.h"
14 : #include "mozilla/dom/HTMLFormSubmission.h"
15 : #include "mozilla/dom/HTMLOptionElement.h"
16 : #include "mozilla/dom/HTMLOptionsCollectionBinding.h"
17 : #include "mozilla/dom/HTMLSelectElement.h"
18 : #include "nsContentCreatorFunctions.h"
19 : #include "nsError.h"
20 : #include "nsGkAtoms.h"
21 : #include "nsIComboboxControlFrame.h"
22 : #include "nsIDocument.h"
23 : #include "nsIDOMHTMLOptGroupElement.h"
24 : #include "nsIFormControlFrame.h"
25 : #include "nsIForm.h"
26 : #include "nsIFormProcessor.h"
27 : #include "nsIListControlFrame.h"
28 : #include "nsLayoutUtils.h"
29 : #include "nsMappedAttributes.h"
30 : #include "nsRuleData.h"
31 : #include "nsServiceManagerUtils.h"
32 : #include "nsStyleConsts.h"
33 : #include "jsfriendapi.h"
34 :
35 : namespace mozilla {
36 : namespace dom {
37 :
38 0 : HTMLOptionsCollection::HTMLOptionsCollection(HTMLSelectElement* aSelect)
39 : {
40 : // Do not maintain a reference counted reference. When
41 : // the select goes away, it will let us know.
42 0 : mSelect = aSelect;
43 0 : }
44 :
45 0 : HTMLOptionsCollection::~HTMLOptionsCollection()
46 : {
47 0 : DropReference();
48 0 : }
49 :
50 : void
51 0 : HTMLOptionsCollection::DropReference()
52 : {
53 : // Drop our (non ref-counted) reference
54 0 : mSelect = nullptr;
55 0 : }
56 :
57 : nsresult
58 0 : HTMLOptionsCollection::GetOptionIndex(Element* aOption,
59 : int32_t aStartIndex,
60 : bool aForward,
61 : int32_t* aIndex)
62 : {
63 : // NOTE: aIndex shouldn't be set if the returned value isn't NS_OK.
64 :
65 : int32_t index;
66 :
67 : // Make the common case fast
68 0 : if (aStartIndex == 0 && aForward) {
69 0 : index = mElements.IndexOf(aOption);
70 0 : if (index == -1) {
71 0 : return NS_ERROR_FAILURE;
72 : }
73 :
74 0 : *aIndex = index;
75 0 : return NS_OK;
76 : }
77 :
78 0 : int32_t high = mElements.Length();
79 0 : int32_t step = aForward ? 1 : -1;
80 :
81 0 : for (index = aStartIndex; index < high && index > -1; index += step) {
82 0 : if (mElements[index] == aOption) {
83 0 : *aIndex = index;
84 0 : return NS_OK;
85 : }
86 : }
87 :
88 0 : return NS_ERROR_FAILURE;
89 : }
90 :
91 :
92 0 : NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(HTMLOptionsCollection, mElements)
93 :
94 : // nsISupports
95 :
96 : // QueryInterface implementation for HTMLOptionsCollection
97 0 : NS_INTERFACE_TABLE_HEAD(HTMLOptionsCollection)
98 0 : NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY
99 0 : NS_INTERFACE_TABLE(HTMLOptionsCollection,
100 : nsIHTMLCollection,
101 : nsIDOMHTMLOptionsCollection,
102 : nsIDOMHTMLCollection)
103 0 : NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(HTMLOptionsCollection)
104 0 : NS_INTERFACE_MAP_END
105 :
106 :
107 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(HTMLOptionsCollection)
108 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(HTMLOptionsCollection)
109 :
110 :
111 : JSObject*
112 0 : HTMLOptionsCollection::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
113 : {
114 0 : return HTMLOptionsCollectionBinding::Wrap(aCx, this, aGivenProto);
115 : }
116 :
117 : NS_IMETHODIMP
118 0 : HTMLOptionsCollection::GetLength(uint32_t* aLength)
119 : {
120 0 : *aLength = mElements.Length();
121 :
122 0 : return NS_OK;
123 : }
124 :
125 : NS_IMETHODIMP
126 0 : HTMLOptionsCollection::SetLength(uint32_t aLength)
127 : {
128 0 : if (!mSelect) {
129 0 : return NS_ERROR_UNEXPECTED;
130 : }
131 :
132 0 : return mSelect->SetLength(aLength);
133 : }
134 :
135 : void
136 0 : HTMLOptionsCollection::IndexedSetter(uint32_t aIndex,
137 : HTMLOptionElement* aOption,
138 : ErrorResult& aError)
139 : {
140 0 : if (!mSelect) {
141 0 : return;
142 : }
143 :
144 : // if the new option is null, just remove this option. Note that it's safe
145 : // to pass a too-large aIndex in here.
146 0 : if (!aOption) {
147 0 : mSelect->Remove(aIndex);
148 :
149 : // We're done.
150 0 : return;
151 : }
152 :
153 : // Now we're going to be setting an option in our collection
154 0 : if (aIndex > mElements.Length()) {
155 : // Fill our array with blank options up to (but not including, since we're
156 : // about to change it) aIndex, for compat with other browsers.
157 0 : nsresult rv = SetLength(aIndex);
158 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
159 0 : aError.Throw(rv);
160 0 : return;
161 : }
162 : }
163 :
164 0 : NS_ASSERTION(aIndex <= mElements.Length(), "SetLength lied");
165 :
166 0 : if (aIndex == mElements.Length()) {
167 0 : mSelect->AppendChild(*aOption, aError);
168 0 : return;
169 : }
170 :
171 : // Find the option they're talking about and replace it
172 : // hold a strong reference to follow COM rules.
173 0 : RefPtr<HTMLOptionElement> refChild = ItemAsOption(aIndex);
174 0 : if (!refChild) {
175 0 : aError.Throw(NS_ERROR_UNEXPECTED);
176 0 : return;
177 : }
178 :
179 0 : nsCOMPtr<nsINode> parent = refChild->GetParent();
180 0 : if (!parent) {
181 0 : return;
182 : }
183 :
184 0 : parent->ReplaceChild(*aOption, *refChild, aError);
185 : }
186 :
187 : int32_t
188 0 : HTMLOptionsCollection::GetSelectedIndex(ErrorResult& aError)
189 : {
190 0 : if (!mSelect) {
191 0 : aError.Throw(NS_ERROR_UNEXPECTED);
192 0 : return 0;
193 : }
194 :
195 : int32_t selectedIndex;
196 0 : aError = mSelect->GetSelectedIndex(&selectedIndex);
197 0 : return selectedIndex;
198 : }
199 :
200 : NS_IMETHODIMP
201 0 : HTMLOptionsCollection::GetSelectedIndex(int32_t* aSelectedIndex)
202 : {
203 0 : ErrorResult rv;
204 0 : *aSelectedIndex = GetSelectedIndex(rv);
205 0 : return rv.StealNSResult();
206 : }
207 :
208 : void
209 0 : HTMLOptionsCollection::SetSelectedIndex(int32_t aSelectedIndex,
210 : ErrorResult& aError)
211 : {
212 0 : if (!mSelect) {
213 0 : aError.Throw(NS_ERROR_UNEXPECTED);
214 0 : return;
215 : }
216 :
217 0 : aError = mSelect->SetSelectedIndex(aSelectedIndex);
218 : }
219 :
220 : NS_IMETHODIMP
221 0 : HTMLOptionsCollection::SetSelectedIndex(int32_t aSelectedIndex)
222 : {
223 0 : ErrorResult rv;
224 0 : SetSelectedIndex(aSelectedIndex, rv);
225 0 : return rv.StealNSResult();
226 : }
227 :
228 : NS_IMETHODIMP
229 0 : HTMLOptionsCollection::Item(uint32_t aIndex, nsIDOMNode** aReturn)
230 : {
231 0 : nsISupports* item = GetElementAt(aIndex);
232 0 : if (!item) {
233 0 : *aReturn = nullptr;
234 :
235 0 : return NS_OK;
236 : }
237 :
238 0 : return CallQueryInterface(item, aReturn);
239 : }
240 :
241 : Element*
242 0 : HTMLOptionsCollection::GetElementAt(uint32_t aIndex)
243 : {
244 0 : return ItemAsOption(aIndex);
245 : }
246 :
247 : HTMLOptionElement*
248 0 : HTMLOptionsCollection::NamedGetter(const nsAString& aName, bool& aFound)
249 : {
250 0 : uint32_t count = mElements.Length();
251 0 : for (uint32_t i = 0; i < count; i++) {
252 0 : HTMLOptionElement* content = mElements.ElementAt(i);
253 0 : if (content &&
254 0 : (content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name, aName,
255 0 : eCaseMatters) ||
256 0 : content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::id, aName,
257 : eCaseMatters))) {
258 0 : aFound = true;
259 0 : return content;
260 : }
261 : }
262 :
263 0 : aFound = false;
264 0 : return nullptr;
265 : }
266 :
267 : nsINode*
268 0 : HTMLOptionsCollection::GetParentObject()
269 : {
270 0 : return mSelect;
271 : }
272 :
273 : NS_IMETHODIMP
274 0 : HTMLOptionsCollection::NamedItem(const nsAString& aName,
275 : nsIDOMNode** aReturn)
276 : {
277 0 : NS_IF_ADDREF(*aReturn = GetNamedItem(aName));
278 :
279 0 : return NS_OK;
280 : }
281 :
282 : void
283 0 : HTMLOptionsCollection::GetSupportedNames(nsTArray<nsString>& aNames)
284 : {
285 0 : AutoTArray<nsIAtom*, 8> atoms;
286 0 : for (uint32_t i = 0; i < mElements.Length(); ++i) {
287 0 : HTMLOptionElement* content = mElements.ElementAt(i);
288 0 : if (content) {
289 : // Note: HasName means the names is exposed on the document,
290 : // which is false for options, so we don't check it here.
291 0 : const nsAttrValue* val = content->GetParsedAttr(nsGkAtoms::name);
292 0 : if (val && val->Type() == nsAttrValue::eAtom) {
293 0 : nsIAtom* name = val->GetAtomValue();
294 0 : if (!atoms.Contains(name)) {
295 0 : atoms.AppendElement(name);
296 : }
297 : }
298 0 : if (content->HasID()) {
299 0 : nsIAtom* id = content->GetID();
300 0 : if (!atoms.Contains(id)) {
301 0 : atoms.AppendElement(id);
302 : }
303 : }
304 : }
305 : }
306 :
307 0 : uint32_t atomsLen = atoms.Length();
308 0 : nsString* names = aNames.AppendElements(atomsLen);
309 0 : for (uint32_t i = 0; i < atomsLen; ++i) {
310 0 : atoms[i]->ToString(names[i]);
311 : }
312 0 : }
313 :
314 : NS_IMETHODIMP
315 0 : HTMLOptionsCollection::GetSelect(nsIDOMHTMLSelectElement** aReturn)
316 : {
317 0 : NS_IF_ADDREF(*aReturn = mSelect);
318 0 : return NS_OK;
319 : }
320 :
321 : NS_IMETHODIMP
322 0 : HTMLOptionsCollection::Add(nsIDOMHTMLOptionElement* aOption,
323 : nsIVariant* aBefore)
324 : {
325 0 : if (!aOption) {
326 0 : return NS_ERROR_INVALID_ARG;
327 : }
328 :
329 0 : if (!mSelect) {
330 0 : return NS_ERROR_NOT_INITIALIZED;
331 : }
332 :
333 0 : nsCOMPtr<nsIDOMHTMLElement> elem = do_QueryInterface(aOption);
334 0 : return mSelect->Add(elem, aBefore);
335 : }
336 :
337 : void
338 0 : HTMLOptionsCollection::Add(const HTMLOptionOrOptGroupElement& aElement,
339 : const Nullable<HTMLElementOrLong>& aBefore,
340 : ErrorResult& aError)
341 : {
342 0 : if (!mSelect) {
343 0 : aError.Throw(NS_ERROR_NOT_INITIALIZED);
344 0 : return;
345 : }
346 :
347 0 : mSelect->Add(aElement, aBefore, aError);
348 : }
349 :
350 : void
351 0 : HTMLOptionsCollection::Remove(int32_t aIndex, ErrorResult& aError)
352 : {
353 0 : if (!mSelect) {
354 0 : aError.Throw(NS_ERROR_UNEXPECTED);
355 0 : return;
356 : }
357 :
358 0 : uint32_t len = 0;
359 0 : mSelect->GetLength(&len);
360 0 : if (aIndex < 0 || (uint32_t)aIndex >= len)
361 0 : aIndex = 0;
362 :
363 0 : aError = mSelect->Remove(aIndex);
364 : }
365 :
366 : NS_IMETHODIMP
367 0 : HTMLOptionsCollection::Remove(int32_t aIndex)
368 : {
369 0 : ErrorResult rv;
370 0 : Remove(aIndex, rv);
371 0 : return rv.StealNSResult();
372 : }
373 :
374 : } // namespace dom
375 : } // namespace mozilla
|