Bug 1685822 [wpt PR 27117] - [Import Maps] Add tests for rejecting multiple import...
[gecko.git] / dom / html / HTMLFormControlsCollection.cpp
blob5f7af7acf4f1ebb333a1c7e8a013c5d55053bb19
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 {
22 /* static */
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
27 // form.
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:
54 case NS_FORM_SELECT:
55 case NS_FORM_TEXTAREA:
56 case NS_FORM_FIELDSET:
57 case NS_FORM_OBJECT:
58 case NS_FORM_OUTPUT:
59 return true;
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
68 // switch?
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 mElements[i]->ClearForm(false, false);
96 mElements.Clear();
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() {
107 if (mForm) {
108 Document* doc = mForm->GetUncomposedDoc();
109 if (doc) {
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.
121 tmp->Clear();
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)
136 NS_INTERFACE_MAP_END
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) {
150 if (aFlushContent) {
151 FlushPendingNotifications();
154 return mNameLookupTable.GetWeak(aName);
157 nsresult HTMLFormControlsCollection::AddElementToTable(
158 nsGenericHTMLFormElement* aChild, const nsAString& aName) {
159 if (!ShouldBeInElements(aChild)) {
160 return NS_OK;
163 return mForm->AddElementToTableInternal(mNameLookupTable, aChild, aName);
166 nsresult HTMLFormControlsCollection::IndexOfControl(nsIFormControl* aControl,
167 int32_t* aIndex) {
168 // Note -- not a DOM method; callers should handle flushing themselves
170 NS_ENSURE_ARG_POINTER(aIndex);
172 *aIndex = mElements.IndexOf(aControl);
174 return NS_OK;
177 nsresult HTMLFormControlsCollection::RemoveElementFromTable(
178 nsGenericHTMLFormElement* aChild, const nsAString& aName) {
179 if (!ShouldBeInElements(aChild)) {
180 return NS_OK;
183 return mForm->RemoveElementFromTableInternal(mNameLookupTable, aChild, aName);
186 nsresult HTMLFormControlsCollection::GetSortedControls(
187 nsTArray<RefPtr<nsGenericHTMLFormElement>>& aControls) const {
188 #ifdef DEBUG
189 HTMLFormElement::AssertDocumentOrder(mElements, mForm);
190 HTMLFormElement::AssertDocumentOrder(mNotInElements, mForm);
191 #endif
193 aControls.Clear();
195 // Merge the elements list and the not in elements list. Both lists are
196 // already sorted.
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);
214 break;
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);
225 break;
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) <
235 0) {
236 elementToAdd = mElements[elementsIdx];
237 ++elementsIdx;
238 } else {
239 elementToAdd = mNotInElements[notInElementsIdx];
240 ++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");
250 #ifdef DEBUG
251 HTMLFormElement::AssertDocumentOrder(aControls, mForm);
252 #endif
254 return NS_OK;
257 Element* HTMLFormControlsCollection::GetElementAt(uint32_t aIndex) {
258 FlushPendingNotifications();
260 return mElements.SafeElementAt(aIndex, nullptr);
263 /* virtual */
264 nsINode* HTMLFormControlsCollection::GetParentObject() { return mForm; }
266 /* virtual */
267 Element* HTMLFormControlsCollection::GetFirstNamedElement(
268 const nsAString& aName, bool& aFound) {
269 Nullable<OwningRadioNodeListOrElement> maybeResult;
270 NamedGetter(aName, aFound, maybeResult);
271 if (!aFound) {
272 return nullptr;
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.");
284 return nullptr;
287 void HTMLFormControlsCollection::NamedGetter(
288 const nsAString& aName, bool& aFound,
289 Nullable<OwningRadioNodeListOrElement>& aResult) {
290 nsISupports* item = NamedItemInternal(aName, true);
291 if (!item) {
292 aFound = false;
293 return;
295 aFound = true;
296 if (nsCOMPtr<Element> element = do_QueryInterface(item)) {
297 aResult.SetValue().SetAsElement() = element;
298 return;
300 if (nsCOMPtr<RadioNodeList> nodelist = do_QueryInterface(item)) {
301 aResult.SetValue().SetAsRadioNodeList() = nodelist;
302 return;
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
311 // this enumeration.
312 for (auto iter = mNameLookupTable.Iter(); !iter.Done(); iter.Next()) {
313 aNames.AppendElement(iter.Key());
317 /* virtual */
318 JSObject* HTMLFormControlsCollection::WrapObject(
319 JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
320 return HTMLFormControlsCollection_Binding::Wrap(aCx, this, aGivenProto);
323 } // namespace mozilla::dom