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 "nsIDocument.h"
16 #include "nsIFormControl.h"
17 #include "RadioNodeList.h"
18 #include "jsfriendapi.h"
24 HTMLFormControlsCollection::ShouldBeInElements(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 NS_FORM_BUTTON_BUTTON
:
32 case NS_FORM_BUTTON_RESET
:
33 case NS_FORM_BUTTON_SUBMIT
:
34 case NS_FORM_INPUT_BUTTON
:
35 case NS_FORM_INPUT_CHECKBOX
:
36 case NS_FORM_INPUT_COLOR
:
37 case NS_FORM_INPUT_EMAIL
:
38 case NS_FORM_INPUT_FILE
:
39 case NS_FORM_INPUT_HIDDEN
:
40 case NS_FORM_INPUT_RESET
:
41 case NS_FORM_INPUT_PASSWORD
:
42 case NS_FORM_INPUT_RADIO
:
43 case NS_FORM_INPUT_SEARCH
:
44 case NS_FORM_INPUT_SUBMIT
:
45 case NS_FORM_INPUT_TEXT
:
46 case NS_FORM_INPUT_TEL
:
47 case NS_FORM_INPUT_URL
:
48 case NS_FORM_INPUT_NUMBER
:
49 case NS_FORM_INPUT_RANGE
:
50 case NS_FORM_INPUT_DATE
:
51 case NS_FORM_INPUT_TIME
:
52 case NS_FORM_INPUT_MONTH
:
53 case NS_FORM_INPUT_WEEK
:
54 case NS_FORM_INPUT_DATETIME_LOCAL
:
56 case NS_FORM_TEXTAREA
:
57 case NS_FORM_FIELDSET
:
63 // These form control types are not supposed to end up in the
64 // form.elements array
66 // NS_FORM_INPUT_IMAGE
68 // XXXbz maybe we should just check for that type here instead of the big
74 HTMLFormControlsCollection::HTMLFormControlsCollection(HTMLFormElement
* aForm
)
76 // Initialize the elements list to have an initial capacity
77 // of 8 to reduce allocations on small forms.
79 , mNameLookupTable(HTMLFormElement::FORM_CONTROL_LIST_HASHTABLE_LENGTH
)
83 HTMLFormControlsCollection::~HTMLFormControlsCollection()
90 HTMLFormControlsCollection::DropFormReference()
97 HTMLFormControlsCollection::Clear()
99 // Null out childrens' pointer to me. No refcounting here
100 for (int32_t i
= mElements
.Length() - 1; i
>= 0; i
--) {
101 mElements
[i
]->ClearForm(false, false);
105 for (int32_t i
= mNotInElements
.Length() - 1; i
>= 0; i
--) {
106 mNotInElements
[i
]->ClearForm(false, false);
108 mNotInElements
.Clear();
110 mNameLookupTable
.Clear();
114 HTMLFormControlsCollection::FlushPendingNotifications()
117 nsIDocument
* doc
= mForm
->GetUncomposedDoc();
119 doc
->FlushPendingNotifications(FlushType::Content
);
124 NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLFormControlsCollection
)
126 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(HTMLFormControlsCollection
)
127 // Note: We intentionally don't set tmp->mForm to nullptr here, since doing
128 // so may result in crashes because of inconsistent null-checking after the
129 // object gets unlinked.
131 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
132 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
133 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(HTMLFormControlsCollection
)
134 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNameLookupTable
)
135 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
136 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(HTMLFormControlsCollection
)
137 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
138 NS_IMPL_CYCLE_COLLECTION_TRACE_END
140 // XPConnect interface list for HTMLFormControlsCollection
141 NS_INTERFACE_TABLE_HEAD(HTMLFormControlsCollection
)
142 NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY
143 NS_INTERFACE_TABLE(HTMLFormControlsCollection
,
145 NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(HTMLFormControlsCollection
)
149 NS_IMPL_CYCLE_COLLECTING_ADDREF(HTMLFormControlsCollection
)
150 NS_IMPL_CYCLE_COLLECTING_RELEASE(HTMLFormControlsCollection
)
153 // nsIHTMLCollection interface
156 HTMLFormControlsCollection::Length()
158 FlushPendingNotifications();
159 return mElements
.Length();
163 HTMLFormControlsCollection::NamedItemInternal(const nsAString
& aName
,
167 FlushPendingNotifications();
170 return mNameLookupTable
.GetWeak(aName
);
174 HTMLFormControlsCollection::AddElementToTable(nsGenericHTMLFormElement
* aChild
,
175 const nsAString
& aName
)
177 if (!ShouldBeInElements(aChild
)) {
181 return mForm
->AddElementToTableInternal(mNameLookupTable
, aChild
, aName
);
185 HTMLFormControlsCollection::IndexOfControl(nsIFormControl
* aControl
,
188 // Note -- not a DOM method; callers should handle flushing themselves
190 NS_ENSURE_ARG_POINTER(aIndex
);
192 *aIndex
= mElements
.IndexOf(aControl
);
198 HTMLFormControlsCollection::RemoveElementFromTable(
199 nsGenericHTMLFormElement
* aChild
, const nsAString
& aName
)
201 if (!ShouldBeInElements(aChild
)) {
205 return mForm
->RemoveElementFromTableInternal(mNameLookupTable
, aChild
, aName
);
209 HTMLFormControlsCollection::GetSortedControls(
210 nsTArray
<RefPtr
<nsGenericHTMLFormElement
>>& aControls
) const
213 HTMLFormElement::AssertDocumentOrder(mElements
, mForm
);
214 HTMLFormElement::AssertDocumentOrder(mNotInElements
, mForm
);
219 // Merge the elements list and the not in elements list. Both lists are
221 uint32_t elementsLen
= mElements
.Length();
222 uint32_t notInElementsLen
= mNotInElements
.Length();
223 aControls
.SetCapacity(elementsLen
+ notInElementsLen
);
225 uint32_t elementsIdx
= 0;
226 uint32_t notInElementsIdx
= 0;
228 while (elementsIdx
< elementsLen
|| notInElementsIdx
< notInElementsLen
) {
229 // Check whether we're done with mElements
230 if (elementsIdx
== elementsLen
) {
231 NS_ASSERTION(notInElementsIdx
< notInElementsLen
,
232 "Should have remaining not-in-elements");
233 // Append the remaining mNotInElements elements
234 if (!aControls
.AppendElements(mNotInElements
.Elements() +
238 return NS_ERROR_OUT_OF_MEMORY
;
242 // Check whether we're done with mNotInElements
243 if (notInElementsIdx
== notInElementsLen
) {
244 NS_ASSERTION(elementsIdx
< elementsLen
,
245 "Should have remaining in-elements");
246 // Append the remaining mElements elements
247 if (!aControls
.AppendElements(mElements
.Elements() +
251 return NS_ERROR_OUT_OF_MEMORY
;
255 // Both lists have elements left.
256 NS_ASSERTION(mElements
[elementsIdx
] &&
257 mNotInElements
[notInElementsIdx
],
258 "Should have remaining elements");
259 // Determine which of the two elements should be ordered
260 // first and add it to the end of the list.
261 nsGenericHTMLFormElement
* elementToAdd
;
262 if (HTMLFormElement::CompareFormControlPosition(
263 mElements
[elementsIdx
], mNotInElements
[notInElementsIdx
], mForm
) < 0) {
264 elementToAdd
= mElements
[elementsIdx
];
267 elementToAdd
= mNotInElements
[notInElementsIdx
];
270 // Add the first element to the list.
271 if (!aControls
.AppendElement(elementToAdd
)) {
272 return NS_ERROR_OUT_OF_MEMORY
;
276 NS_ASSERTION(aControls
.Length() == elementsLen
+ notInElementsLen
,
277 "Not all form controls were added to the sorted list");
279 HTMLFormElement::AssertDocumentOrder(aControls
, mForm
);
286 HTMLFormControlsCollection::GetElementAt(uint32_t aIndex
)
288 FlushPendingNotifications();
290 return mElements
.SafeElementAt(aIndex
, nullptr);
293 /* virtual */ nsINode
*
294 HTMLFormControlsCollection::GetParentObject()
299 /* virtual */ Element
*
300 HTMLFormControlsCollection::GetFirstNamedElement(const nsAString
& aName
, bool& aFound
)
302 Nullable
<OwningRadioNodeListOrElement
> maybeResult
;
303 NamedGetter(aName
, aFound
, maybeResult
);
307 MOZ_ASSERT(!maybeResult
.IsNull());
308 const OwningRadioNodeListOrElement
& result
= maybeResult
.Value();
309 if (result
.IsElement()) {
310 return result
.GetAsElement().get();
312 if (result
.IsRadioNodeList()) {
313 RadioNodeList
& nodelist
= result
.GetAsRadioNodeList();
314 return nodelist
.Item(0)->AsElement();
316 MOZ_ASSERT_UNREACHABLE("Should only have Elements and NodeLists here.");
321 HTMLFormControlsCollection::NamedGetter(const nsAString
& aName
,
323 Nullable
<OwningRadioNodeListOrElement
>& aResult
)
325 nsISupports
* item
= NamedItemInternal(aName
, true);
331 if (nsCOMPtr
<Element
> element
= do_QueryInterface(item
)) {
332 aResult
.SetValue().SetAsElement() = element
;
335 if (nsCOMPtr
<RadioNodeList
> nodelist
= do_QueryInterface(item
)) {
336 aResult
.SetValue().SetAsRadioNodeList() = nodelist
;
339 MOZ_ASSERT_UNREACHABLE("Should only have Elements and NodeLists here.");
343 HTMLFormControlsCollection::GetSupportedNames(nsTArray
<nsString
>& aNames
)
345 FlushPendingNotifications();
346 // Just enumerate mNameLookupTable. This won't guarantee order, but
347 // that's OK, because the HTML5 spec doesn't define an order for
349 for (auto iter
= mNameLookupTable
.Iter(); !iter
.Done(); iter
.Next()) {
350 aNames
.AppendElement(iter
.Key());
354 /* virtual */ JSObject
*
355 HTMLFormControlsCollection::WrapObject(JSContext
* aCx
, JS::Handle
<JSObject
*> aGivenProto
)
357 return HTMLFormControlsCollection_Binding::Wrap(aCx
, this, aGivenProto
);
361 } // namespace mozilla