Bug 1494333 - index crons just like artifacts r=Callek
[gecko.git] / dom / html / HTMLFormControlsCollection.cpp
blobd1c7d9e531f6f6d748cfd6dce461865db2d1605b
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"
20 namespace mozilla {
21 namespace dom {
23 /* static */ bool
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
28 // form.
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 :
55 case NS_FORM_SELECT :
56 case NS_FORM_TEXTAREA :
57 case NS_FORM_FIELDSET :
58 case NS_FORM_OBJECT :
59 case NS_FORM_OUTPUT :
60 return true;
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
69 // switch?
71 return false;
74 HTMLFormControlsCollection::HTMLFormControlsCollection(HTMLFormElement* aForm)
75 : mForm(aForm)
76 // Initialize the elements list to have an initial capacity
77 // of 8 to reduce allocations on small forms.
78 , mElements(8)
79 , mNameLookupTable(HTMLFormElement::FORM_CONTROL_LIST_HASHTABLE_LENGTH)
83 HTMLFormControlsCollection::~HTMLFormControlsCollection()
85 mForm = nullptr;
86 Clear();
89 void
90 HTMLFormControlsCollection::DropFormReference()
92 mForm = nullptr;
93 Clear();
96 void
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);
103 mElements.Clear();
105 for (int32_t i = mNotInElements.Length() - 1; i >= 0; i--) {
106 mNotInElements[i]->ClearForm(false, false);
108 mNotInElements.Clear();
110 mNameLookupTable.Clear();
113 void
114 HTMLFormControlsCollection::FlushPendingNotifications()
116 if (mForm) {
117 nsIDocument* doc = mForm->GetUncomposedDoc();
118 if (doc) {
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.
130 tmp->Clear();
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,
144 nsIHTMLCollection)
145 NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(HTMLFormControlsCollection)
146 NS_INTERFACE_MAP_END
149 NS_IMPL_CYCLE_COLLECTING_ADDREF(HTMLFormControlsCollection)
150 NS_IMPL_CYCLE_COLLECTING_RELEASE(HTMLFormControlsCollection)
153 // nsIHTMLCollection interface
155 uint32_t
156 HTMLFormControlsCollection::Length()
158 FlushPendingNotifications();
159 return mElements.Length();
162 nsISupports*
163 HTMLFormControlsCollection::NamedItemInternal(const nsAString& aName,
164 bool aFlushContent)
166 if (aFlushContent) {
167 FlushPendingNotifications();
170 return mNameLookupTable.GetWeak(aName);
173 nsresult
174 HTMLFormControlsCollection::AddElementToTable(nsGenericHTMLFormElement* aChild,
175 const nsAString& aName)
177 if (!ShouldBeInElements(aChild)) {
178 return NS_OK;
181 return mForm->AddElementToTableInternal(mNameLookupTable, aChild, aName);
184 nsresult
185 HTMLFormControlsCollection::IndexOfControl(nsIFormControl* aControl,
186 int32_t* aIndex)
188 // Note -- not a DOM method; callers should handle flushing themselves
190 NS_ENSURE_ARG_POINTER(aIndex);
192 *aIndex = mElements.IndexOf(aControl);
194 return NS_OK;
197 nsresult
198 HTMLFormControlsCollection::RemoveElementFromTable(
199 nsGenericHTMLFormElement* aChild, const nsAString& aName)
201 if (!ShouldBeInElements(aChild)) {
202 return NS_OK;
205 return mForm->RemoveElementFromTableInternal(mNameLookupTable, aChild, aName);
208 nsresult
209 HTMLFormControlsCollection::GetSortedControls(
210 nsTArray<RefPtr<nsGenericHTMLFormElement>>& aControls) const
212 #ifdef DEBUG
213 HTMLFormElement::AssertDocumentOrder(mElements, mForm);
214 HTMLFormElement::AssertDocumentOrder(mNotInElements, mForm);
215 #endif
217 aControls.Clear();
219 // Merge the elements list and the not in elements list. Both lists are
220 // already sorted.
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() +
235 notInElementsIdx,
236 notInElementsLen -
237 notInElementsIdx)) {
238 return NS_ERROR_OUT_OF_MEMORY;
240 break;
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() +
248 elementsIdx,
249 elementsLen -
250 elementsIdx)) {
251 return NS_ERROR_OUT_OF_MEMORY;
253 break;
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];
265 ++elementsIdx;
266 } else {
267 elementToAdd = mNotInElements[notInElementsIdx];
268 ++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");
278 #ifdef DEBUG
279 HTMLFormElement::AssertDocumentOrder(aControls, mForm);
280 #endif
282 return NS_OK;
285 Element*
286 HTMLFormControlsCollection::GetElementAt(uint32_t aIndex)
288 FlushPendingNotifications();
290 return mElements.SafeElementAt(aIndex, nullptr);
293 /* virtual */ nsINode*
294 HTMLFormControlsCollection::GetParentObject()
296 return mForm;
299 /* virtual */ Element*
300 HTMLFormControlsCollection::GetFirstNamedElement(const nsAString& aName, bool& aFound)
302 Nullable<OwningRadioNodeListOrElement> maybeResult;
303 NamedGetter(aName, aFound, maybeResult);
304 if (!aFound) {
305 return nullptr;
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.");
317 return nullptr;
320 void
321 HTMLFormControlsCollection::NamedGetter(const nsAString& aName,
322 bool& aFound,
323 Nullable<OwningRadioNodeListOrElement>& aResult)
325 nsISupports* item = NamedItemInternal(aName, true);
326 if (!item) {
327 aFound = false;
328 return;
330 aFound = true;
331 if (nsCOMPtr<Element> element = do_QueryInterface(item)) {
332 aResult.SetValue().SetAsElement() = element;
333 return;
335 if (nsCOMPtr<RadioNodeList> nodelist = do_QueryInterface(item)) {
336 aResult.SetValue().SetAsRadioNodeList() = nodelist;
337 return;
339 MOZ_ASSERT_UNREACHABLE("Should only have Elements and NodeLists here.");
342 void
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
348 // this enumeration.
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);
360 } // namespace dom
361 } // namespace mozilla