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/HTMLFormControlsCollection.h"
9 #include "mozilla/FlushType.h"
10 #include "mozilla/dom/BindingUtils.h"
11 #include "mozilla/dom/Document.h"
12 #include "mozilla/dom/Element.h"
13 #include "mozilla/dom/HTMLFormControlsCollectionBinding.h"
14 #include "mozilla/dom/HTMLFormElement.h"
15 #include "nsGenericHTMLElement.h" // nsGenericHTMLFormElement
16 #include "nsQueryObject.h"
17 #include "nsIFormControl.h"
18 #include "RadioNodeList.h"
19 #include "jsfriendapi.h"
21 namespace mozilla::dom
{
24 bool HTMLFormControlsCollection::ShouldBeInElements(
25 nsIFormControl
* aFormControl
) {
26 // For backwards compatibility (with 4.x and IE) we must not add
27 // <input type=image> elements to the list of form controls in a
30 switch (aFormControl
->ControlType()) {
31 case FormControlType::ButtonButton
:
32 case FormControlType::ButtonReset
:
33 case FormControlType::ButtonSubmit
:
34 case FormControlType::InputButton
:
35 case FormControlType::InputCheckbox
:
36 case FormControlType::InputColor
:
37 case FormControlType::InputEmail
:
38 case FormControlType::InputFile
:
39 case FormControlType::InputHidden
:
40 case FormControlType::InputReset
:
41 case FormControlType::InputPassword
:
42 case FormControlType::InputRadio
:
43 case FormControlType::InputSearch
:
44 case FormControlType::InputSubmit
:
45 case FormControlType::InputText
:
46 case FormControlType::InputTel
:
47 case FormControlType::InputUrl
:
48 case FormControlType::InputNumber
:
49 case FormControlType::InputRange
:
50 case FormControlType::InputDate
:
51 case FormControlType::InputTime
:
52 case FormControlType::InputMonth
:
53 case FormControlType::InputWeek
:
54 case FormControlType::InputDatetimeLocal
:
55 case FormControlType::Select
:
56 case FormControlType::Textarea
:
57 case FormControlType::Fieldset
:
58 case FormControlType::Object
:
59 case FormControlType::Output
:
60 case FormControlType::FormAssociatedCustomElement
:
63 // These form control types are not supposed to end up in the
64 // form.elements array
65 // XXXbz maybe we should just return aType != InputImage or something
66 // instead of the big switch?
67 case FormControlType::InputImage
:
73 HTMLFormControlsCollection::HTMLFormControlsCollection(HTMLFormElement
* aForm
)
75 // Initialize the elements list to have an initial capacity
76 // of 8 to reduce allocations on small forms.
79 mNameLookupTable(HTMLFormElement::FORM_CONTROL_LIST_HASHTABLE_LENGTH
) {}
81 HTMLFormControlsCollection::~HTMLFormControlsCollection() {
86 void HTMLFormControlsCollection::DropFormReference() {
91 void HTMLFormControlsCollection::Clear() {
92 // Null out childrens' pointer to me. No refcounting here
93 for (int32_t i
= mElements
.Length() - 1; i
>= 0; i
--) {
94 nsCOMPtr
<nsIFormControl
> formControl
= do_QueryObject(mElements
[i
]);
95 MOZ_ASSERT(formControl
);
96 formControl
->ClearForm(false, false);
100 for (int32_t i
= mNotInElements
.Length() - 1; i
>= 0; i
--) {
101 nsCOMPtr
<nsIFormControl
> formControl
= do_QueryObject(mNotInElements
[i
]);
102 MOZ_ASSERT(formControl
);
103 formControl
->ClearForm(false, false);
105 mNotInElements
.Clear();
107 mNameLookupTable
.Clear();
110 NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLFormControlsCollection
)
112 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(HTMLFormControlsCollection
)
113 // Note: We intentionally don't set tmp->mForm to nullptr here, since doing
114 // so may result in crashes because of inconsistent null-checking after the
115 // object gets unlinked.
117 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
118 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
119 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(HTMLFormControlsCollection
)
120 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNameLookupTable
)
121 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
122 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(HTMLFormControlsCollection
)
123 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
124 NS_IMPL_CYCLE_COLLECTION_TRACE_END
126 // XPConnect interface list for HTMLFormControlsCollection
127 NS_INTERFACE_TABLE_HEAD(HTMLFormControlsCollection
)
128 NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY
129 NS_INTERFACE_TABLE(HTMLFormControlsCollection
, nsIHTMLCollection
)
130 NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(HTMLFormControlsCollection
)
133 NS_IMPL_CYCLE_COLLECTING_ADDREF(HTMLFormControlsCollection
)
134 NS_IMPL_CYCLE_COLLECTING_RELEASE(HTMLFormControlsCollection
)
136 // nsIHTMLCollection interface
138 uint32_t HTMLFormControlsCollection::Length() { return mElements
.Length(); }
140 nsISupports
* HTMLFormControlsCollection::NamedItemInternal(
141 const nsAString
& aName
) {
142 return mNameLookupTable
.GetWeak(aName
);
145 nsresult
HTMLFormControlsCollection::AddElementToTable(
146 nsGenericHTMLFormElement
* aChild
, const nsAString
& aName
) {
147 nsCOMPtr
<nsIFormControl
> formControl
= do_QueryObject(aChild
);
148 MOZ_ASSERT(formControl
);
149 if (!ShouldBeInElements(formControl
)) {
153 return mForm
->AddElementToTableInternal(mNameLookupTable
, aChild
, aName
);
156 nsresult
HTMLFormControlsCollection::IndexOfContent(nsIContent
* aContent
,
158 // Note -- not a DOM method; callers should handle flushing themselves
160 NS_ENSURE_ARG_POINTER(aIndex
);
162 *aIndex
= mElements
.IndexOf(aContent
);
167 nsresult
HTMLFormControlsCollection::RemoveElementFromTable(
168 nsGenericHTMLFormElement
* aChild
, const nsAString
& aName
) {
169 nsCOMPtr
<nsIFormControl
> formControl
= do_QueryObject(aChild
);
170 MOZ_ASSERT(formControl
);
171 if (!ShouldBeInElements(formControl
)) {
175 return mForm
->RemoveElementFromTableInternal(mNameLookupTable
, aChild
, aName
);
178 nsresult
HTMLFormControlsCollection::GetSortedControls(
179 nsTArray
<RefPtr
<nsGenericHTMLFormElement
>>& aControls
) const {
181 HTMLFormElement::AssertDocumentOrder(mElements
, mForm
);
182 HTMLFormElement::AssertDocumentOrder(mNotInElements
, mForm
);
187 // Merge the elements list and the not in elements list. Both lists are
189 uint32_t elementsLen
= mElements
.Length();
190 uint32_t notInElementsLen
= mNotInElements
.Length();
191 aControls
.SetCapacity(elementsLen
+ notInElementsLen
);
193 uint32_t elementsIdx
= 0;
194 uint32_t notInElementsIdx
= 0;
196 while (elementsIdx
< elementsLen
|| notInElementsIdx
< notInElementsLen
) {
197 // Check whether we're done with mElements
198 if (elementsIdx
== elementsLen
) {
199 NS_ASSERTION(notInElementsIdx
< notInElementsLen
,
200 "Should have remaining not-in-elements");
201 // Append the remaining mNotInElements elements
202 // XXX(Bug 1631371) Check if this should use a fallible operation as it
203 // pretended earlier.
204 aControls
.AppendElements(mNotInElements
.Elements() + notInElementsIdx
,
205 notInElementsLen
- notInElementsIdx
);
208 // Check whether we're done with mNotInElements
209 if (notInElementsIdx
== notInElementsLen
) {
210 NS_ASSERTION(elementsIdx
< elementsLen
,
211 "Should have remaining in-elements");
212 // Append the remaining mElements elements
213 // XXX(Bug 1631371) Check if this should use a fallible operation as it
214 // pretended earlier.
215 aControls
.AppendElements(mElements
.Elements() + elementsIdx
,
216 elementsLen
- elementsIdx
);
219 // Both lists have elements left.
220 NS_ASSERTION(mElements
[elementsIdx
] && mNotInElements
[notInElementsIdx
],
221 "Should have remaining elements");
222 // Determine which of the two elements should be ordered
223 // first and add it to the end of the list.
224 nsGenericHTMLFormElement
* elementToAdd
;
225 if (nsContentUtils::CompareTreePosition(mElements
[elementsIdx
],
226 mNotInElements
[notInElementsIdx
],
228 elementToAdd
= mElements
[elementsIdx
];
231 elementToAdd
= mNotInElements
[notInElementsIdx
];
234 // Add the first element to the list.
235 // XXX(Bug 1631371) Check if this should use a fallible operation as it
236 // pretended earlier.
237 aControls
.AppendElement(elementToAdd
);
240 NS_ASSERTION(aControls
.Length() == elementsLen
+ notInElementsLen
,
241 "Not all form controls were added to the sorted list");
243 HTMLFormElement::AssertDocumentOrder(aControls
, mForm
);
249 Element
* HTMLFormControlsCollection::GetElementAt(uint32_t aIndex
) {
250 return mElements
.SafeElementAt(aIndex
, nullptr);
254 nsINode
* HTMLFormControlsCollection::GetParentObject() { return mForm
; }
257 Element
* HTMLFormControlsCollection::GetFirstNamedElement(
258 const nsAString
& aName
, bool& aFound
) {
259 Nullable
<OwningRadioNodeListOrElement
> maybeResult
;
260 NamedGetter(aName
, aFound
, maybeResult
);
264 MOZ_ASSERT(!maybeResult
.IsNull());
265 const OwningRadioNodeListOrElement
& result
= maybeResult
.Value();
266 if (result
.IsElement()) {
267 return result
.GetAsElement().get();
269 if (result
.IsRadioNodeList()) {
270 RadioNodeList
& nodelist
= result
.GetAsRadioNodeList();
271 return nodelist
.Item(0)->AsElement();
273 MOZ_ASSERT_UNREACHABLE("Should only have Elements and NodeLists here.");
277 void HTMLFormControlsCollection::NamedGetter(
278 const nsAString
& aName
, bool& aFound
,
279 Nullable
<OwningRadioNodeListOrElement
>& aResult
) {
280 nsISupports
* item
= NamedItemInternal(aName
);
286 if (nsCOMPtr
<Element
> element
= do_QueryInterface(item
)) {
287 aResult
.SetValue().SetAsElement() = element
;
290 if (nsCOMPtr
<RadioNodeList
> nodelist
= do_QueryInterface(item
)) {
291 aResult
.SetValue().SetAsRadioNodeList() = nodelist
;
294 MOZ_ASSERT_UNREACHABLE("Should only have Elements and NodeLists here.");
297 void HTMLFormControlsCollection::GetSupportedNames(nsTArray
<nsString
>& aNames
) {
298 // Just enumerate mNameLookupTable. This won't guarantee order, but
299 // that's OK, because the HTML5 spec doesn't define an order for
301 AppendToArray(aNames
, mNameLookupTable
.Keys());
305 JSObject
* HTMLFormControlsCollection::WrapObject(
306 JSContext
* aCx
, JS::Handle
<JSObject
*> aGivenProto
) {
307 return HTMLFormControlsCollection_Binding::Wrap(aCx
, this, aGivenProto
);
310 } // namespace mozilla::dom