1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "mozilla/dom/HTMLOptionsCollection.h"
8 #include "HTMLOptGroupElement.h"
9 #include "mozAutoDocUpdate.h"
10 #include "mozilla/dom/BindingUtils.h"
11 #include "mozilla/dom/Element.h"
12 #include "mozilla/dom/HTMLOptionElement.h"
13 #include "mozilla/dom/HTMLOptionsCollectionBinding.h"
14 #include "mozilla/dom/HTMLSelectElement.h"
15 #include "nsContentCreatorFunctions.h"
17 #include "nsFormSubmission.h"
18 #include "nsGkAtoms.h"
19 #include "nsIComboboxControlFrame.h"
20 #include "nsIDocument.h"
21 #include "nsIDOMHTMLOptGroupElement.h"
22 #include "nsIFormControlFrame.h"
24 #include "nsIFormProcessor.h"
25 #include "nsIListControlFrame.h"
26 #include "nsLayoutUtils.h"
27 #include "nsMappedAttributes.h"
28 #include "nsRuleData.h"
29 #include "nsServiceManagerUtils.h"
30 #include "nsStyleConsts.h"
31 #include "jsfriendapi.h"
36 HTMLOptionsCollection::HTMLOptionsCollection(HTMLSelectElement
* aSelect
)
38 // Do not maintain a reference counted reference. When
39 // the select goes away, it will let us know.
43 HTMLOptionsCollection::~HTMLOptionsCollection()
49 HTMLOptionsCollection::DropReference()
51 // Drop our (non ref-counted) reference
56 HTMLOptionsCollection::GetOptionIndex(Element
* aOption
,
61 // NOTE: aIndex shouldn't be set if the returned value isn't NS_OK.
65 // Make the common case fast
66 if (aStartIndex
== 0 && aForward
) {
67 index
= mElements
.IndexOf(aOption
);
69 return NS_ERROR_FAILURE
;
76 int32_t high
= mElements
.Length();
77 int32_t step
= aForward
? 1 : -1;
79 for (index
= aStartIndex
; index
< high
&& index
> -1; index
+= step
) {
80 if (mElements
[index
] == aOption
) {
86 return NS_ERROR_FAILURE
;
90 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(HTMLOptionsCollection
, mElements
)
94 // QueryInterface implementation for HTMLOptionsCollection
95 NS_INTERFACE_TABLE_HEAD(HTMLOptionsCollection
)
96 NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY
97 NS_INTERFACE_TABLE(HTMLOptionsCollection
,
99 nsIDOMHTMLOptionsCollection
,
100 nsIDOMHTMLCollection
)
101 NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(HTMLOptionsCollection
)
105 NS_IMPL_CYCLE_COLLECTING_ADDREF(HTMLOptionsCollection
)
106 NS_IMPL_CYCLE_COLLECTING_RELEASE(HTMLOptionsCollection
)
110 HTMLOptionsCollection::WrapObject(JSContext
* aCx
)
112 return HTMLOptionsCollectionBinding::Wrap(aCx
, this);
116 HTMLOptionsCollection::GetLength(uint32_t* aLength
)
118 *aLength
= mElements
.Length();
124 HTMLOptionsCollection::SetLength(uint32_t aLength
)
127 return NS_ERROR_UNEXPECTED
;
130 return mSelect
->SetLength(aLength
);
134 HTMLOptionsCollection::SetOption(uint32_t aIndex
,
135 nsIDOMHTMLOptionElement
* aOption
)
141 // if the new option is null, just remove this option. Note that it's safe
142 // to pass a too-large aIndex in here.
144 mSelect
->Remove(aIndex
);
152 uint32_t index
= uint32_t(aIndex
);
154 // Now we're going to be setting an option in our collection
155 if (index
> mElements
.Length()) {
156 // Fill our array with blank options up to (but not including, since we're
157 // about to change it) aIndex, for compat with other browsers.
158 rv
= SetLength(index
);
159 NS_ENSURE_SUCCESS(rv
, rv
);
162 NS_ASSERTION(index
<= mElements
.Length(), "SetLength lied");
164 nsCOMPtr
<nsIDOMNode
> ret
;
165 if (index
== mElements
.Length()) {
166 nsCOMPtr
<nsIDOMNode
> node
= do_QueryInterface(aOption
);
167 rv
= mSelect
->AppendChild(node
, getter_AddRefs(ret
));
169 // Find the option they're talking about and replace it
170 // hold a strong reference to follow COM rules.
171 nsRefPtr
<HTMLOptionElement
> refChild
= ItemAsOption(index
);
172 NS_ENSURE_TRUE(refChild
, NS_ERROR_UNEXPECTED
);
174 nsCOMPtr
<nsINode
> parent
= refChild
->GetParent();
176 nsCOMPtr
<nsINode
> node
= do_QueryInterface(aOption
);
178 parent
->ReplaceChild(*node
, *refChild
, res
);
179 rv
= res
.ErrorCode();
187 HTMLOptionsCollection::GetSelectedIndex(ErrorResult
& aError
)
190 aError
.Throw(NS_ERROR_UNEXPECTED
);
194 int32_t selectedIndex
;
195 aError
= mSelect
->GetSelectedIndex(&selectedIndex
);
196 return selectedIndex
;
200 HTMLOptionsCollection::GetSelectedIndex(int32_t* aSelectedIndex
)
203 *aSelectedIndex
= GetSelectedIndex(rv
);
204 return rv
.ErrorCode();
208 HTMLOptionsCollection::SetSelectedIndex(int32_t aSelectedIndex
,
212 aError
.Throw(NS_ERROR_UNEXPECTED
);
216 aError
= mSelect
->SetSelectedIndex(aSelectedIndex
);
220 HTMLOptionsCollection::SetSelectedIndex(int32_t aSelectedIndex
)
223 SetSelectedIndex(aSelectedIndex
, rv
);
224 return rv
.ErrorCode();
228 HTMLOptionsCollection::Item(uint32_t aIndex
, nsIDOMNode
** aReturn
)
230 nsISupports
* item
= GetElementAt(aIndex
);
237 return CallQueryInterface(item
, aReturn
);
241 HTMLOptionsCollection::GetElementAt(uint32_t aIndex
)
243 return ItemAsOption(aIndex
);
247 HTMLOptionsCollection::NamedGetter(const nsAString
& aName
, bool& aFound
)
249 uint32_t count
= mElements
.Length();
250 for (uint32_t i
= 0; i
< count
; i
++) {
251 HTMLOptionElement
* content
= mElements
.ElementAt(i
);
253 (content
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::name
, aName
,
255 content
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::id
, aName
,
267 HTMLOptionsCollection::GetParentObject()
273 HTMLOptionsCollection::NamedItem(const nsAString
& aName
,
274 nsIDOMNode
** aReturn
)
276 NS_IF_ADDREF(*aReturn
= GetNamedItem(aName
));
282 HTMLOptionsCollection::GetSupportedNames(unsigned aFlags
,
283 nsTArray
<nsString
>& aNames
)
285 if (!(aFlags
& JSITER_HIDDEN
)) {
289 nsAutoTArray
<nsIAtom
*, 8> atoms
;
290 for (uint32_t i
= 0; i
< mElements
.Length(); ++i
) {
291 HTMLOptionElement
* content
= mElements
.ElementAt(i
);
293 // Note: HasName means the names is exposed on the document,
294 // which is false for options, so we don't check it here.
295 const nsAttrValue
* val
= content
->GetParsedAttr(nsGkAtoms::name
);
296 if (val
&& val
->Type() == nsAttrValue::eAtom
) {
297 nsIAtom
* name
= val
->GetAtomValue();
298 if (!atoms
.Contains(name
)) {
299 atoms
.AppendElement(name
);
302 if (content
->HasID()) {
303 nsIAtom
* id
= content
->GetID();
304 if (!atoms
.Contains(id
)) {
305 atoms
.AppendElement(id
);
311 aNames
.SetCapacity(atoms
.Length());
312 for (uint32_t i
= 0; i
< atoms
.Length(); ++i
) {
313 aNames
.AppendElement(nsDependentAtomString(atoms
[i
]));
318 HTMLOptionsCollection::GetSelect(nsIDOMHTMLSelectElement
** aReturn
)
320 NS_IF_ADDREF(*aReturn
= mSelect
);
325 HTMLOptionsCollection::Add(nsIDOMHTMLOptionElement
* aOption
,
329 return NS_ERROR_INVALID_ARG
;
333 return NS_ERROR_NOT_INITIALIZED
;
336 nsCOMPtr
<nsIDOMHTMLElement
> elem
= do_QueryInterface(aOption
);
337 return mSelect
->Add(elem
, aBefore
);
341 HTMLOptionsCollection::Add(const HTMLOptionOrOptGroupElement
& aElement
,
342 const Nullable
<HTMLElementOrLong
>& aBefore
,
346 aError
.Throw(NS_ERROR_NOT_INITIALIZED
);
350 mSelect
->Add(aElement
, aBefore
, aError
);
354 HTMLOptionsCollection::Remove(int32_t aIndex
, ErrorResult
& aError
)
357 aError
.Throw(NS_ERROR_UNEXPECTED
);
362 mSelect
->GetLength(&len
);
363 if (aIndex
< 0 || (uint32_t)aIndex
>= len
)
366 aError
= mSelect
->Remove(aIndex
);
370 HTMLOptionsCollection::Remove(int32_t aIndex
)
374 return rv
.ErrorCode();
378 } // namespace mozilla