Backed out changeset 62f7af8fe549 (bug 1843981) for causing valgrind bustage. CLOSED...
[gecko.git] / dom / html / HTMLFormControlsCollection.cpp
blob406a00a1ea7c3fb6109008c167a842c8e956607b
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 {
23 /* static */
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
28 // form.
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:
61 return true;
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:
68 break;
70 return false;
73 HTMLFormControlsCollection::HTMLFormControlsCollection(HTMLFormElement* aForm)
74 : mForm(aForm)
75 // Initialize the elements list to have an initial capacity
76 // of 8 to reduce allocations on small forms.
78 mElements(8),
79 mNameLookupTable(HTMLFormElement::FORM_CONTROL_LIST_HASHTABLE_LENGTH) {}
81 HTMLFormControlsCollection::~HTMLFormControlsCollection() {
82 mForm = nullptr;
83 Clear();
86 void HTMLFormControlsCollection::DropFormReference() {
87 mForm = nullptr;
88 Clear();
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);
98 mElements.Clear();
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.
116 tmp->Clear();
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)
131 NS_INTERFACE_MAP_END
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)) {
150 return NS_OK;
153 return mForm->AddElementToTableInternal(mNameLookupTable, aChild, aName);
156 nsresult HTMLFormControlsCollection::IndexOfContent(nsIContent* aContent,
157 int32_t* aIndex) {
158 // Note -- not a DOM method; callers should handle flushing themselves
160 NS_ENSURE_ARG_POINTER(aIndex);
162 *aIndex = mElements.IndexOf(aContent);
164 return NS_OK;
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)) {
172 return NS_OK;
175 return mForm->RemoveElementFromTableInternal(mNameLookupTable, aChild, aName);
178 nsresult HTMLFormControlsCollection::GetSortedControls(
179 nsTArray<RefPtr<nsGenericHTMLFormElement>>& aControls) const {
180 #ifdef DEBUG
181 HTMLFormElement::AssertDocumentOrder(mElements, mForm);
182 HTMLFormElement::AssertDocumentOrder(mNotInElements, mForm);
183 #endif
185 aControls.Clear();
187 // Merge the elements list and the not in elements list. Both lists are
188 // already sorted.
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);
206 break;
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);
217 break;
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],
227 mForm) < 0) {
228 elementToAdd = mElements[elementsIdx];
229 ++elementsIdx;
230 } else {
231 elementToAdd = mNotInElements[notInElementsIdx];
232 ++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");
242 #ifdef DEBUG
243 HTMLFormElement::AssertDocumentOrder(aControls, mForm);
244 #endif
246 return NS_OK;
249 Element* HTMLFormControlsCollection::GetElementAt(uint32_t aIndex) {
250 return mElements.SafeElementAt(aIndex, nullptr);
253 /* virtual */
254 nsINode* HTMLFormControlsCollection::GetParentObject() { return mForm; }
256 /* virtual */
257 Element* HTMLFormControlsCollection::GetFirstNamedElement(
258 const nsAString& aName, bool& aFound) {
259 Nullable<OwningRadioNodeListOrElement> maybeResult;
260 NamedGetter(aName, aFound, maybeResult);
261 if (!aFound) {
262 return nullptr;
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.");
274 return nullptr;
277 void HTMLFormControlsCollection::NamedGetter(
278 const nsAString& aName, bool& aFound,
279 Nullable<OwningRadioNodeListOrElement>& aResult) {
280 nsISupports* item = NamedItemInternal(aName);
281 if (!item) {
282 aFound = false;
283 return;
285 aFound = true;
286 if (nsCOMPtr<Element> element = do_QueryInterface(item)) {
287 aResult.SetValue().SetAsElement() = element;
288 return;
290 if (nsCOMPtr<RadioNodeList> nodelist = do_QueryInterface(item)) {
291 aResult.SetValue().SetAsRadioNodeList() = nodelist;
292 return;
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
300 // this enumeration.
301 AppendToArray(aNames, mNameLookupTable.Keys());
304 /* virtual */
305 JSObject* HTMLFormControlsCollection::WrapObject(
306 JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
307 return HTMLFormControlsCollection_Binding::Wrap(aCx, this, aGivenProto);
310 } // namespace mozilla::dom