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/. */
7 #include "mozilla/dom/HTMLOptionsCollection.h"
9 #include "HTMLOptGroupElement.h"
10 #include "mozAutoDocUpdate.h"
11 #include "mozilla/dom/BindingUtils.h"
12 #include "mozilla/dom/Element.h"
13 #include "mozilla/MappedDeclarations.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"
20 #include "nsGkAtoms.h"
21 #include "mozilla/dom/Document.h"
22 #include "nsIFormControlFrame.h"
23 #include "nsLayoutUtils.h"
24 #include "nsMappedAttributes.h"
25 #include "nsServiceManagerUtils.h"
26 #include "nsStyleConsts.h"
27 #include "jsfriendapi.h"
29 namespace mozilla::dom
{
31 HTMLOptionsCollection::HTMLOptionsCollection(HTMLSelectElement
* aSelect
)
34 nsresult
HTMLOptionsCollection::GetOptionIndex(Element
* aOption
,
36 bool aForward
, int32_t* aIndex
) {
37 // NOTE: aIndex shouldn't be set if the returned value isn't NS_OK.
41 // Make the common case fast
42 if (aStartIndex
== 0 && aForward
) {
43 index
= mElements
.IndexOf(aOption
);
45 return NS_ERROR_FAILURE
;
52 int32_t high
= mElements
.Length();
53 int32_t step
= aForward
? 1 : -1;
55 for (index
= aStartIndex
; index
< high
&& index
> -1; index
+= step
) {
56 if (mElements
[index
] == aOption
) {
62 return NS_ERROR_FAILURE
;
65 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(HTMLOptionsCollection
, mElements
, mSelect
)
69 // QueryInterface implementation for HTMLOptionsCollection
70 NS_INTERFACE_TABLE_HEAD(HTMLOptionsCollection
)
71 NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY
72 NS_INTERFACE_TABLE(HTMLOptionsCollection
, nsIHTMLCollection
)
73 NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(HTMLOptionsCollection
)
76 NS_IMPL_CYCLE_COLLECTING_ADDREF(HTMLOptionsCollection
)
77 NS_IMPL_CYCLE_COLLECTING_RELEASE(HTMLOptionsCollection
)
79 JSObject
* HTMLOptionsCollection::WrapObject(JSContext
* aCx
,
80 JS::Handle
<JSObject
*> aGivenProto
) {
81 return HTMLOptionsCollection_Binding::Wrap(aCx
, this, aGivenProto
);
84 uint32_t HTMLOptionsCollection::Length() { return mElements
.Length(); }
86 void HTMLOptionsCollection::SetLength(uint32_t aLength
, ErrorResult
& aError
) {
87 mSelect
->SetLength(aLength
, aError
);
90 void HTMLOptionsCollection::IndexedSetter(uint32_t aIndex
,
91 HTMLOptionElement
* aOption
,
92 ErrorResult
& aError
) {
93 // if the new option is null, just remove this option. Note that it's safe
94 // to pass a too-large aIndex in here.
96 mSelect
->Remove(aIndex
);
102 // Now we're going to be setting an option in our collection
103 if (aIndex
> mElements
.Length()) {
104 // Fill our array with blank options up to (but not including, since we're
105 // about to change it) aIndex, for compat with other browsers.
106 SetLength(aIndex
, aError
);
107 ENSURE_SUCCESS_VOID(aError
);
110 NS_ASSERTION(aIndex
<= mElements
.Length(), "SetLength lied");
112 if (aIndex
== mElements
.Length()) {
113 mSelect
->AppendChild(*aOption
, aError
);
117 // Find the option they're talking about and replace it
118 // hold a strong reference to follow COM rules.
119 RefPtr
<HTMLOptionElement
> refChild
= ItemAsOption(aIndex
);
121 aError
.Throw(NS_ERROR_UNEXPECTED
);
125 nsCOMPtr
<nsINode
> parent
= refChild
->GetParent();
130 parent
->ReplaceChild(*aOption
, *refChild
, aError
);
133 int32_t HTMLOptionsCollection::SelectedIndex() {
134 return mSelect
->SelectedIndex();
137 void HTMLOptionsCollection::SetSelectedIndex(int32_t aSelectedIndex
) {
138 mSelect
->SetSelectedIndex(aSelectedIndex
);
141 Element
* HTMLOptionsCollection::GetElementAt(uint32_t aIndex
) {
142 return ItemAsOption(aIndex
);
145 HTMLOptionElement
* HTMLOptionsCollection::NamedGetter(const nsAString
& aName
,
147 uint32_t count
= mElements
.Length();
148 for (uint32_t i
= 0; i
< count
; i
++) {
149 HTMLOptionElement
* content
= mElements
.ElementAt(i
);
150 if (content
&& (content
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::name
,
151 aName
, eCaseMatters
) ||
152 content
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::id
,
153 aName
, eCaseMatters
))) {
163 nsINode
* HTMLOptionsCollection::GetParentObject() { return mSelect
; }
165 DocGroup
* HTMLOptionsCollection::GetDocGroup() const {
166 return mSelect
? mSelect
->GetDocGroup() : nullptr;
169 void HTMLOptionsCollection::GetSupportedNames(nsTArray
<nsString
>& aNames
) {
170 AutoTArray
<nsAtom
*, 8> atoms
;
171 for (uint32_t i
= 0; i
< mElements
.Length(); ++i
) {
172 HTMLOptionElement
* content
= mElements
.ElementAt(i
);
174 // Note: HasName means the names is exposed on the document,
175 // which is false for options, so we don't check it here.
176 const nsAttrValue
* val
= content
->GetParsedAttr(nsGkAtoms::name
);
177 if (val
&& val
->Type() == nsAttrValue::eAtom
) {
178 nsAtom
* name
= val
->GetAtomValue();
179 if (!atoms
.Contains(name
)) {
180 atoms
.AppendElement(name
);
183 if (content
->HasID()) {
184 nsAtom
* id
= content
->GetID();
185 if (!atoms
.Contains(id
)) {
186 atoms
.AppendElement(id
);
192 uint32_t atomsLen
= atoms
.Length();
193 nsString
* names
= aNames
.AppendElements(atomsLen
);
194 for (uint32_t i
= 0; i
< atomsLen
; ++i
) {
195 atoms
[i
]->ToString(names
[i
]);
199 void HTMLOptionsCollection::Add(const HTMLOptionOrOptGroupElement
& aElement
,
200 const Nullable
<HTMLElementOrLong
>& aBefore
,
201 ErrorResult
& aError
) {
202 mSelect
->Add(aElement
, aBefore
, aError
);
205 void HTMLOptionsCollection::Remove(int32_t aIndex
) { mSelect
->Remove(aIndex
); }
207 } // namespace mozilla::dom