Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / dom / html / HTMLFormControlsCollection.cpp
blobaa10daceeba1025d30c615d4113ea3fe86621f9e
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 mNameLookupTable(HTMLFormElement::FORM_CONTROL_LIST_HASHTABLE_LENGTH) {}
77 HTMLFormControlsCollection::~HTMLFormControlsCollection() {
78 mForm = nullptr;
79 Clear();
82 void HTMLFormControlsCollection::DropFormReference() {
83 mForm = nullptr;
84 Clear();
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);
94 mElements.Clear();
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.
112 tmp->Clear();
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)
127 NS_INTERFACE_MAP_END
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)) {
146 return NS_OK;
149 return mForm->AddElementToTableInternal(mNameLookupTable, aChild, aName);
152 nsresult HTMLFormControlsCollection::IndexOfContent(nsIContent* aContent,
153 int32_t* aIndex) {
154 // Note -- not a DOM method; callers should handle flushing themselves
156 NS_ENSURE_ARG_POINTER(aIndex);
157 *aIndex = mElements->IndexOf(aContent);
158 return NS_OK;
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)) {
166 return NS_OK;
169 return mForm->RemoveElementFromTableInternal(mNameLookupTable, aChild, aName);
172 nsresult HTMLFormControlsCollection::GetSortedControls(
173 nsTArray<RefPtr<nsGenericHTMLFormElement>>& aControls) const {
174 #ifdef DEBUG
175 HTMLFormElement::AssertDocumentOrder(mElements, mForm);
176 HTMLFormElement::AssertDocumentOrder(mNotInElements, mForm);
177 #endif
179 aControls.Clear();
181 // Merge the elements list and the not in elements list. Both lists are
182 // already sorted.
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);
200 break;
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);
211 break;
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);
224 ++elementsIdx;
225 } else {
226 elementToAdd = mNotInElements->ElementAt(notInElementsIdx);
227 ++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");
237 #ifdef DEBUG
238 HTMLFormElement::AssertDocumentOrder(aControls, mForm);
239 #endif
241 return NS_OK;
244 Element* HTMLFormControlsCollection::GetElementAt(uint32_t aIndex) {
245 return mElements->SafeElementAt(aIndex, nullptr);
248 /* virtual */
249 nsINode* HTMLFormControlsCollection::GetParentObject() { return mForm; }
251 /* virtual */
252 Element* HTMLFormControlsCollection::GetFirstNamedElement(
253 const nsAString& aName, bool& aFound) {
254 Nullable<OwningRadioNodeListOrElement> maybeResult;
255 NamedGetter(aName, aFound, maybeResult);
256 if (!aFound) {
257 return nullptr;
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.");
269 return nullptr;
272 void HTMLFormControlsCollection::NamedGetter(
273 const nsAString& aName, bool& aFound,
274 Nullable<OwningRadioNodeListOrElement>& aResult) {
275 nsISupports* item = NamedItemInternal(aName);
276 if (!item) {
277 aFound = false;
278 return;
280 aFound = true;
281 if (nsCOMPtr<Element> element = do_QueryInterface(item)) {
282 aResult.SetValue().SetAsElement() = element;
283 return;
285 if (nsCOMPtr<RadioNodeList> nodelist = do_QueryInterface(item)) {
286 aResult.SetValue().SetAsRadioNodeList() = nodelist;
287 return;
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
295 // this enumeration.
296 AppendToArray(aNames, mNameLookupTable.Keys());
299 /* virtual */
300 JSObject* HTMLFormControlsCollection::WrapObject(
301 JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
302 return HTMLFormControlsCollection_Binding::Wrap(aCx, this, aGivenProto);
305 } // namespace mozilla::dom