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/Element.h"
12 #include "mozilla/dom/HTMLFormControlsCollectionBinding.h"
13 #include "mozilla/dom/HTMLFormElement.h"
14 #include "nsGenericHTMLElement.h" // nsGenericHTMLFormElement
15 #include "mozilla/dom/Document.h"
16 #include "nsIFormControl.h"
17 #include "RadioNodeList.h"
18 #include "jsfriendapi.h"
20 namespace mozilla::dom
{
23 bool HTMLFormControlsCollection::ShouldBeInElements(
24 nsIFormControl
* aFormControl
) {
25 // For backwards compatibility (with 4.x and IE) we must not add
26 // <input type=image> elements to the list of form controls in a
29 switch (aFormControl
->ControlType()) {
30 case NS_FORM_BUTTON_BUTTON
:
31 case NS_FORM_BUTTON_RESET
:
32 case NS_FORM_BUTTON_SUBMIT
:
33 case NS_FORM_INPUT_BUTTON
:
34 case NS_FORM_INPUT_CHECKBOX
:
35 case NS_FORM_INPUT_COLOR
:
36 case NS_FORM_INPUT_EMAIL
:
37 case NS_FORM_INPUT_FILE
:
38 case NS_FORM_INPUT_HIDDEN
:
39 case NS_FORM_INPUT_RESET
:
40 case NS_FORM_INPUT_PASSWORD
:
41 case NS_FORM_INPUT_RADIO
:
42 case NS_FORM_INPUT_SEARCH
:
43 case NS_FORM_INPUT_SUBMIT
:
44 case NS_FORM_INPUT_TEXT
:
45 case NS_FORM_INPUT_TEL
:
46 case NS_FORM_INPUT_URL
:
47 case NS_FORM_INPUT_NUMBER
:
48 case NS_FORM_INPUT_RANGE
:
49 case NS_FORM_INPUT_DATE
:
50 case NS_FORM_INPUT_TIME
:
51 case NS_FORM_INPUT_MONTH
:
52 case NS_FORM_INPUT_WEEK
:
53 case NS_FORM_INPUT_DATETIME_LOCAL
:
55 case NS_FORM_TEXTAREA
:
56 case NS_FORM_FIELDSET
:
62 // These form control types are not supposed to end up in the
63 // form.elements array
65 // NS_FORM_INPUT_IMAGE
67 // XXXbz maybe we should just check for that type here instead of the big
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 mElements
[i
]->ClearForm(false, false);
98 for (int32_t i
= mNotInElements
.Length() - 1; i
>= 0; i
--) {
99 mNotInElements
[i
]->ClearForm(false, false);
101 mNotInElements
.Clear();
103 mNameLookupTable
.Clear();
106 void HTMLFormControlsCollection::FlushPendingNotifications() {
108 Document
* doc
= mForm
->GetUncomposedDoc();
110 doc
->FlushPendingNotifications(FlushType::Content
);
115 NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLFormControlsCollection
)
117 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(HTMLFormControlsCollection
)
118 // Note: We intentionally don't set tmp->mForm to nullptr here, since doing
119 // so may result in crashes because of inconsistent null-checking after the
120 // object gets unlinked.
122 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
123 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
124 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(HTMLFormControlsCollection
)
125 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNameLookupTable
)
126 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
127 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(HTMLFormControlsCollection
)
128 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
129 NS_IMPL_CYCLE_COLLECTION_TRACE_END
131 // XPConnect interface list for HTMLFormControlsCollection
132 NS_INTERFACE_TABLE_HEAD(HTMLFormControlsCollection
)
133 NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY
134 NS_INTERFACE_TABLE(HTMLFormControlsCollection
, nsIHTMLCollection
)
135 NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(HTMLFormControlsCollection
)
138 NS_IMPL_CYCLE_COLLECTING_ADDREF(HTMLFormControlsCollection
)
139 NS_IMPL_CYCLE_COLLECTING_RELEASE(HTMLFormControlsCollection
)
141 // nsIHTMLCollection interface
143 uint32_t HTMLFormControlsCollection::Length() {
144 FlushPendingNotifications();
145 return mElements
.Length();
148 nsISupports
* HTMLFormControlsCollection::NamedItemInternal(
149 const nsAString
& aName
, bool aFlushContent
) {
151 FlushPendingNotifications();
154 return mNameLookupTable
.GetWeak(aName
);
157 nsresult
HTMLFormControlsCollection::AddElementToTable(
158 nsGenericHTMLFormElement
* aChild
, const nsAString
& aName
) {
159 if (!ShouldBeInElements(aChild
)) {
163 return mForm
->AddElementToTableInternal(mNameLookupTable
, aChild
, aName
);
166 nsresult
HTMLFormControlsCollection::IndexOfControl(nsIFormControl
* aControl
,
168 // Note -- not a DOM method; callers should handle flushing themselves
170 NS_ENSURE_ARG_POINTER(aIndex
);
172 *aIndex
= mElements
.IndexOf(aControl
);
177 nsresult
HTMLFormControlsCollection::RemoveElementFromTable(
178 nsGenericHTMLFormElement
* aChild
, const nsAString
& aName
) {
179 if (!ShouldBeInElements(aChild
)) {
183 return mForm
->RemoveElementFromTableInternal(mNameLookupTable
, aChild
, aName
);
186 nsresult
HTMLFormControlsCollection::GetSortedControls(
187 nsTArray
<RefPtr
<nsGenericHTMLFormElement
>>& aControls
) const {
189 HTMLFormElement::AssertDocumentOrder(mElements
, mForm
);
190 HTMLFormElement::AssertDocumentOrder(mNotInElements
, mForm
);
195 // Merge the elements list and the not in elements list. Both lists are
197 uint32_t elementsLen
= mElements
.Length();
198 uint32_t notInElementsLen
= mNotInElements
.Length();
199 aControls
.SetCapacity(elementsLen
+ notInElementsLen
);
201 uint32_t elementsIdx
= 0;
202 uint32_t notInElementsIdx
= 0;
204 while (elementsIdx
< elementsLen
|| notInElementsIdx
< notInElementsLen
) {
205 // Check whether we're done with mElements
206 if (elementsIdx
== elementsLen
) {
207 NS_ASSERTION(notInElementsIdx
< notInElementsLen
,
208 "Should have remaining not-in-elements");
209 // Append the remaining mNotInElements elements
210 // XXX(Bug 1631371) Check if this should use a fallible operation as it
211 // pretended earlier.
212 aControls
.AppendElements(mNotInElements
.Elements() + notInElementsIdx
,
213 notInElementsLen
- notInElementsIdx
);
216 // Check whether we're done with mNotInElements
217 if (notInElementsIdx
== notInElementsLen
) {
218 NS_ASSERTION(elementsIdx
< elementsLen
,
219 "Should have remaining in-elements");
220 // Append the remaining mElements elements
221 // XXX(Bug 1631371) Check if this should use a fallible operation as it
222 // pretended earlier.
223 aControls
.AppendElements(mElements
.Elements() + elementsIdx
,
224 elementsLen
- elementsIdx
);
227 // Both lists have elements left.
228 NS_ASSERTION(mElements
[elementsIdx
] && mNotInElements
[notInElementsIdx
],
229 "Should have remaining elements");
230 // Determine which of the two elements should be ordered
231 // first and add it to the end of the list.
232 nsGenericHTMLFormElement
* elementToAdd
;
233 if (HTMLFormElement::CompareFormControlPosition(
234 mElements
[elementsIdx
], mNotInElements
[notInElementsIdx
], mForm
) <
236 elementToAdd
= mElements
[elementsIdx
];
239 elementToAdd
= mNotInElements
[notInElementsIdx
];
242 // Add the first element to the list.
243 // XXX(Bug 1631371) Check if this should use a fallible operation as it
244 // pretended earlier.
245 aControls
.AppendElement(elementToAdd
);
248 NS_ASSERTION(aControls
.Length() == elementsLen
+ notInElementsLen
,
249 "Not all form controls were added to the sorted list");
251 HTMLFormElement::AssertDocumentOrder(aControls
, mForm
);
257 Element
* HTMLFormControlsCollection::GetElementAt(uint32_t aIndex
) {
258 FlushPendingNotifications();
260 return mElements
.SafeElementAt(aIndex
, nullptr);
264 nsINode
* HTMLFormControlsCollection::GetParentObject() { return mForm
; }
267 Element
* HTMLFormControlsCollection::GetFirstNamedElement(
268 const nsAString
& aName
, bool& aFound
) {
269 Nullable
<OwningRadioNodeListOrElement
> maybeResult
;
270 NamedGetter(aName
, aFound
, maybeResult
);
274 MOZ_ASSERT(!maybeResult
.IsNull());
275 const OwningRadioNodeListOrElement
& result
= maybeResult
.Value();
276 if (result
.IsElement()) {
277 return result
.GetAsElement().get();
279 if (result
.IsRadioNodeList()) {
280 RadioNodeList
& nodelist
= result
.GetAsRadioNodeList();
281 return nodelist
.Item(0)->AsElement();
283 MOZ_ASSERT_UNREACHABLE("Should only have Elements and NodeLists here.");
287 void HTMLFormControlsCollection::NamedGetter(
288 const nsAString
& aName
, bool& aFound
,
289 Nullable
<OwningRadioNodeListOrElement
>& aResult
) {
290 nsISupports
* item
= NamedItemInternal(aName
, true);
296 if (nsCOMPtr
<Element
> element
= do_QueryInterface(item
)) {
297 aResult
.SetValue().SetAsElement() = element
;
300 if (nsCOMPtr
<RadioNodeList
> nodelist
= do_QueryInterface(item
)) {
301 aResult
.SetValue().SetAsRadioNodeList() = nodelist
;
304 MOZ_ASSERT_UNREACHABLE("Should only have Elements and NodeLists here.");
307 void HTMLFormControlsCollection::GetSupportedNames(nsTArray
<nsString
>& aNames
) {
308 FlushPendingNotifications();
309 // Just enumerate mNameLookupTable. This won't guarantee order, but
310 // that's OK, because the HTML5 spec doesn't define an order for
312 for (auto iter
= mNameLookupTable
.Iter(); !iter
.Done(); iter
.Next()) {
313 aNames
.AppendElement(iter
.Key());
318 JSObject
* HTMLFormControlsCollection::WrapObject(
319 JSContext
* aCx
, JS::Handle
<JSObject
*> aGivenProto
) {
320 return HTMLFormControlsCollection_Binding::Wrap(aCx
, this, aGivenProto
);
323 } // namespace mozilla::dom