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 mNameLookupTable(HTMLFormElement::FORM_CONTROL_LIST_HASHTABLE_LENGTH
) {}
77 HTMLFormControlsCollection::~HTMLFormControlsCollection() {
82 void HTMLFormControlsCollection::DropFormReference() {
87 void HTMLFormControlsCollection::Clear() {
88 // Null out childrens' pointer to me. No refcounting here
89 for (nsGenericHTMLFormElement
* element
: Reversed(mElements
.AsList())) {
90 nsCOMPtr
<nsIFormControl
> formControl
= do_QueryObject(element
);
91 MOZ_ASSERT(formControl
);
92 formControl
->ClearForm(false, false);
96 for (nsGenericHTMLFormElement
* element
: Reversed(mNotInElements
.AsList())) {
97 nsCOMPtr
<nsIFormControl
> formControl
= do_QueryObject(element
);
98 MOZ_ASSERT(formControl
);
99 formControl
->ClearForm(false, false);
101 mNotInElements
.Clear();
103 mNameLookupTable
.Clear();
106 NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLFormControlsCollection
)
108 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(HTMLFormControlsCollection
)
109 // Note: We intentionally don't set tmp->mForm to nullptr here, since doing
110 // so may result in crashes because of inconsistent null-checking after the
111 // object gets unlinked.
113 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
114 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
115 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(HTMLFormControlsCollection
)
116 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNameLookupTable
)
117 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
118 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(HTMLFormControlsCollection
)
119 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
120 NS_IMPL_CYCLE_COLLECTION_TRACE_END
122 // XPConnect interface list for HTMLFormControlsCollection
123 NS_INTERFACE_TABLE_HEAD(HTMLFormControlsCollection
)
124 NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY
125 NS_INTERFACE_TABLE(HTMLFormControlsCollection
, nsIHTMLCollection
)
126 NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(HTMLFormControlsCollection
)
129 NS_IMPL_CYCLE_COLLECTING_ADDREF(HTMLFormControlsCollection
)
130 NS_IMPL_CYCLE_COLLECTING_RELEASE(HTMLFormControlsCollection
)
132 // nsIHTMLCollection interfac
134 uint32_t HTMLFormControlsCollection::Length() { return mElements
->Length(); }
136 nsISupports
* HTMLFormControlsCollection::NamedItemInternal(
137 const nsAString
& aName
) {
138 return mNameLookupTable
.GetWeak(aName
);
141 nsresult
HTMLFormControlsCollection::AddElementToTable(
142 nsGenericHTMLFormElement
* aChild
, const nsAString
& aName
) {
143 nsCOMPtr
<nsIFormControl
> formControl
= do_QueryObject(aChild
);
144 MOZ_ASSERT(formControl
);
145 if (!ShouldBeInElements(formControl
)) {
149 return mForm
->AddElementToTableInternal(mNameLookupTable
, aChild
, aName
);
152 nsresult
HTMLFormControlsCollection::IndexOfContent(nsIContent
* aContent
,
154 // Note -- not a DOM method; callers should handle flushing themselves
156 NS_ENSURE_ARG_POINTER(aIndex
);
157 *aIndex
= mElements
->IndexOf(aContent
);
161 nsresult
HTMLFormControlsCollection::RemoveElementFromTable(
162 nsGenericHTMLFormElement
* aChild
, const nsAString
& aName
) {
163 nsCOMPtr
<nsIFormControl
> formControl
= do_QueryObject(aChild
);
164 MOZ_ASSERT(formControl
);
165 if (!ShouldBeInElements(formControl
)) {
169 return mForm
->RemoveElementFromTableInternal(mNameLookupTable
, aChild
, aName
);
172 nsresult
HTMLFormControlsCollection::GetSortedControls(
173 nsTArray
<RefPtr
<nsGenericHTMLFormElement
>>& aControls
) const {
175 HTMLFormElement::AssertDocumentOrder(mElements
, mForm
);
176 HTMLFormElement::AssertDocumentOrder(mNotInElements
, mForm
);
181 // Merge the elements list and the not in elements list. Both lists are
183 uint32_t elementsLen
= mElements
->Length();
184 uint32_t notInElementsLen
= mNotInElements
->Length();
185 aControls
.SetCapacity(elementsLen
+ notInElementsLen
);
187 uint32_t elementsIdx
= 0;
188 uint32_t notInElementsIdx
= 0;
190 while (elementsIdx
< elementsLen
|| notInElementsIdx
< notInElementsLen
) {
191 // Check whether we're done with mElements
192 if (elementsIdx
== elementsLen
) {
193 NS_ASSERTION(notInElementsIdx
< notInElementsLen
,
194 "Should have remaining not-in-elements");
195 // Append the remaining mNotInElements elements
196 // XXX(Bug 1631371) Check if this should use a fallible operation as it
197 // pretended earlier.
198 aControls
.AppendElements(mNotInElements
->Elements() + notInElementsIdx
,
199 notInElementsLen
- notInElementsIdx
);
202 // Check whether we're done with mNotInElements
203 if (notInElementsIdx
== notInElementsLen
) {
204 NS_ASSERTION(elementsIdx
< elementsLen
,
205 "Should have remaining in-elements");
206 // Append the remaining mElements elements
207 // XXX(Bug 1631371) Check if this should use a fallible operation as it
208 // pretended earlier.
209 aControls
.AppendElements(mElements
->Elements() + elementsIdx
,
210 elementsLen
- elementsIdx
);
213 // Both lists have elements left.
214 NS_ASSERTION(mElements
->ElementAt(elementsIdx
) &&
215 mNotInElements
->ElementAt(notInElementsIdx
),
216 "Should have remaining elements");
217 // Determine which of the two elements should be ordered
218 // first and add it to the end of the list.
219 nsGenericHTMLFormElement
* elementToAdd
;
220 if (nsContentUtils::CompareTreePosition
<TreeKind::DOM
>(
221 mElements
->ElementAt(elementsIdx
),
222 mNotInElements
->ElementAt(notInElementsIdx
), mForm
) < 0) {
223 elementToAdd
= mElements
->ElementAt(elementsIdx
);
226 elementToAdd
= mNotInElements
->ElementAt(notInElementsIdx
);
229 // Add the first element to the list.
230 // XXX(Bug 1631371) Check if this should use a fallible operation as it
231 // pretended earlier.
232 aControls
.AppendElement(elementToAdd
);
235 NS_ASSERTION(aControls
.Length() == elementsLen
+ notInElementsLen
,
236 "Not all form controls were added to the sorted list");
238 HTMLFormElement::AssertDocumentOrder(aControls
, mForm
);
244 Element
* HTMLFormControlsCollection::GetElementAt(uint32_t aIndex
) {
245 return mElements
->SafeElementAt(aIndex
, nullptr);
249 nsINode
* HTMLFormControlsCollection::GetParentObject() { return mForm
; }
252 Element
* HTMLFormControlsCollection::GetFirstNamedElement(
253 const nsAString
& aName
, bool& aFound
) {
254 Nullable
<OwningRadioNodeListOrElement
> maybeResult
;
255 NamedGetter(aName
, aFound
, maybeResult
);
259 MOZ_ASSERT(!maybeResult
.IsNull());
260 const OwningRadioNodeListOrElement
& result
= maybeResult
.Value();
261 if (result
.IsElement()) {
262 return result
.GetAsElement().get();
264 if (result
.IsRadioNodeList()) {
265 RadioNodeList
& nodelist
= result
.GetAsRadioNodeList();
266 return nodelist
.Item(0)->AsElement();
268 MOZ_ASSERT_UNREACHABLE("Should only have Elements and NodeLists here.");
272 void HTMLFormControlsCollection::NamedGetter(
273 const nsAString
& aName
, bool& aFound
,
274 Nullable
<OwningRadioNodeListOrElement
>& aResult
) {
275 nsISupports
* item
= NamedItemInternal(aName
);
281 if (nsCOMPtr
<Element
> element
= do_QueryInterface(item
)) {
282 aResult
.SetValue().SetAsElement() = element
;
285 if (nsCOMPtr
<RadioNodeList
> nodelist
= do_QueryInterface(item
)) {
286 aResult
.SetValue().SetAsRadioNodeList() = nodelist
;
289 MOZ_ASSERT_UNREACHABLE("Should only have Elements and NodeLists here.");
292 void HTMLFormControlsCollection::GetSupportedNames(nsTArray
<nsString
>& aNames
) {
293 // Just enumerate mNameLookupTable. This won't guarantee order, but
294 // that's OK, because the HTML5 spec doesn't define an order for
296 AppendToArray(aNames
, mNameLookupTable
.Keys());
300 JSObject
* HTMLFormControlsCollection::WrapObject(
301 JSContext
* aCx
, JS::Handle
<JSObject
*> aGivenProto
) {
302 return HTMLFormControlsCollection_Binding::Wrap(aCx
, this, aGivenProto
);
305 } // namespace mozilla::dom