HTMLFormControlsCollection.cpp (10542B)
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/. */ 6 7 #include "mozilla/dom/HTMLFormControlsCollection.h" 8 9 #include "RadioNodeList.h" 10 #include "jsfriendapi.h" 11 #include "mozilla/FlushType.h" 12 #include "mozilla/dom/BindingUtils.h" 13 #include "mozilla/dom/Document.h" 14 #include "mozilla/dom/Element.h" 15 #include "mozilla/dom/HTMLFormControlsCollectionBinding.h" 16 #include "mozilla/dom/HTMLFormElement.h" 17 #include "nsGenericHTMLElement.h" // nsGenericHTMLFormElement 18 #include "nsIFormControl.h" 19 #include "nsQueryObject.h" 20 21 namespace mozilla::dom { 22 23 /* static */ 24 bool HTMLFormControlsCollection::ShouldBeInElements( 25 const 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. 29 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; 62 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; 69 } 70 return false; 71 } 72 73 HTMLFormControlsCollection::HTMLFormControlsCollection(HTMLFormElement* aForm) 74 : mForm(aForm), 75 mNameLookupTable(HTMLFormElement::FORM_CONTROL_LIST_HASHTABLE_LENGTH) {} 76 77 HTMLFormControlsCollection::~HTMLFormControlsCollection() { 78 mForm = nullptr; 79 Clear(); 80 } 81 82 void HTMLFormControlsCollection::DropFormReference() { 83 mForm = nullptr; 84 Clear(); 85 } 86 87 void HTMLFormControlsCollection::Clear() { 88 // Null out childrens' pointer to me. No refcounting here. 89 for (nsGenericHTMLFormElement* element : mElements.AsSpan()) { 90 nsCOMPtr<nsIFormControl> formControl = nsIFormControl::FromNode(element); 91 MOZ_ASSERT(formControl); 92 formControl->ClearForm(false, false); 93 } 94 mElements.Clear(); 95 96 for (nsGenericHTMLFormElement* element : mNotInElements.AsSpan()) { 97 nsCOMPtr<nsIFormControl> formControl = nsIFormControl::FromNode(element); 98 MOZ_ASSERT(formControl); 99 formControl->ClearForm(false, false); 100 } 101 mNotInElements.Clear(); 102 103 mNameLookupTable.Clear(); 104 } 105 106 NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLFormControlsCollection) 107 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 121 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 128 129 NS_IMPL_CYCLE_COLLECTING_ADDREF(HTMLFormControlsCollection) 130 NS_IMPL_CYCLE_COLLECTING_RELEASE(HTMLFormControlsCollection) 131 132 // nsIHTMLCollection interfac 133 134 uint32_t HTMLFormControlsCollection::Length() { return mElements.Length(); } 135 136 nsISupports* HTMLFormControlsCollection::NamedItemInternal( 137 const nsAString& aName) { 138 return mNameLookupTable.GetWeak(aName); 139 } 140 141 nsresult HTMLFormControlsCollection::AddElementToTable( 142 nsGenericHTMLFormElement* aChild, const nsAString& aName) { 143 const auto* formControl = nsIFormControl::FromNode(aChild); 144 MOZ_ASSERT(formControl); 145 if (!ShouldBeInElements(formControl)) { 146 return NS_OK; 147 } 148 149 return mForm->AddElementToTableInternal(mNameLookupTable, aChild, aName); 150 } 151 152 nsresult HTMLFormControlsCollection::IndexOfContent(nsIContent* aContent, 153 int32_t* aIndex) { 154 // Note -- not a DOM method; callers should handle flushing themselves 155 156 NS_ENSURE_ARG_POINTER(aIndex); 157 *aIndex = mElements.IndexOf(aContent); 158 return NS_OK; 159 } 160 161 nsresult HTMLFormControlsCollection::RemoveElementFromTable( 162 nsGenericHTMLFormElement* aChild, const nsAString& aName) { 163 const auto* formControl = nsIFormControl::FromNode(aChild); 164 MOZ_ASSERT(formControl); 165 if (!ShouldBeInElements(formControl)) { 166 return NS_OK; 167 } 168 169 return mForm->RemoveElementFromTableInternal(mNameLookupTable, aChild, aName); 170 } 171 172 nsresult HTMLFormControlsCollection::GetSortedControls( 173 nsTArray<RefPtr<nsGenericHTMLFormElement>>& aControls) const { 174 aControls.Clear(); 175 176 // Merge the elements list and the not in elements list. Both lists are 177 // already sorted. 178 auto elements = mElements.AsSpan(); 179 auto notInElements = mNotInElements.AsSpan(); 180 uint32_t elementsLen = elements.Length(); 181 uint32_t notInElementsLen = notInElements.Length(); 182 aControls.SetCapacity(elementsLen + notInElementsLen); 183 184 uint32_t elementsIdx = 0; 185 uint32_t notInElementsIdx = 0; 186 187 nsContentUtils::NodeIndexCache indexCache; 188 while (elementsIdx < elementsLen || notInElementsIdx < notInElementsLen) { 189 // Check whether we're done with mElements 190 if (elementsIdx == elementsLen) { 191 NS_ASSERTION(notInElementsIdx < notInElementsLen, 192 "Should have remaining not-in-elements"); 193 // Append the remaining mNotInElements elements 194 // XXX(Bug 1631371) Check if this should use a fallible operation as it 195 // pretended earlier. 196 aControls.AppendElements(notInElements.From(notInElementsIdx)); 197 break; 198 } 199 // Check whether we're done with mNotInElements 200 if (notInElementsIdx == notInElementsLen) { 201 NS_ASSERTION(elementsIdx < elementsLen, 202 "Should have remaining in-elements"); 203 // Append the remaining mElements elements 204 // XXX(Bug 1631371) Check if this should use a fallible operation as it 205 // pretended earlier. 206 aControls.AppendElements(elements.From(elementsIdx)); 207 break; 208 } 209 // Both lists have elements left. 210 NS_ASSERTION(elements[elementsIdx] && notInElements[notInElementsIdx], 211 "Should have remaining elements"); 212 // Determine which of the two elements should be ordered 213 // first and add it to the end of the list. 214 nsGenericHTMLFormElement* elementToAdd; 215 if (nsContentUtils::CompareTreePosition<TreeKind::DOM>( 216 elements[elementsIdx], notInElements[notInElementsIdx], mForm, 217 &indexCache) < 0) { 218 elementToAdd = elements[elementsIdx]; 219 ++elementsIdx; 220 } else { 221 elementToAdd = notInElements[notInElementsIdx]; 222 ++notInElementsIdx; 223 } 224 // Add the first element to the list. 225 // XXX(Bug 1631371) Check if this should use a fallible operation as it 226 // pretended earlier. 227 aControls.AppendElement(elementToAdd); 228 } 229 230 NS_ASSERTION(aControls.Length() == elementsLen + notInElementsLen, 231 "Not all form controls were added to the sorted list"); 232 return NS_OK; 233 } 234 235 Element* HTMLFormControlsCollection::GetElementAt(uint32_t aIndex) { 236 return mElements.SafeElementAt(aIndex, nullptr); 237 } 238 239 /* virtual */ 240 nsINode* HTMLFormControlsCollection::GetParentObject() { return mForm; } 241 242 /* virtual */ 243 Element* HTMLFormControlsCollection::GetFirstNamedElement( 244 const nsAString& aName, bool& aFound) { 245 Nullable<OwningRadioNodeListOrElement> maybeResult; 246 NamedGetter(aName, aFound, maybeResult); 247 if (!aFound) { 248 return nullptr; 249 } 250 MOZ_ASSERT(!maybeResult.IsNull()); 251 const OwningRadioNodeListOrElement& result = maybeResult.Value(); 252 if (result.IsElement()) { 253 return result.GetAsElement().get(); 254 } 255 if (result.IsRadioNodeList()) { 256 RadioNodeList& nodelist = result.GetAsRadioNodeList(); 257 return nodelist.Item(0)->AsElement(); 258 } 259 MOZ_ASSERT_UNREACHABLE("Should only have Elements and NodeLists here."); 260 return nullptr; 261 } 262 263 void HTMLFormControlsCollection::NamedGetter( 264 const nsAString& aName, bool& aFound, 265 Nullable<OwningRadioNodeListOrElement>& aResult) { 266 nsISupports* item = NamedItemInternal(aName); 267 if (!item) { 268 aFound = false; 269 return; 270 } 271 aFound = true; 272 if (nsCOMPtr<Element> element = do_QueryInterface(item)) { 273 aResult.SetValue().SetAsElement() = element; 274 return; 275 } 276 if (nsCOMPtr<RadioNodeList> nodelist = do_QueryInterface(item)) { 277 aResult.SetValue().SetAsRadioNodeList() = nodelist; 278 return; 279 } 280 MOZ_ASSERT_UNREACHABLE("Should only have Elements and NodeLists here."); 281 } 282 283 void HTMLFormControlsCollection::GetSupportedNames(nsTArray<nsString>& aNames) { 284 // Just enumerate mNameLookupTable. This won't guarantee order, but 285 // that's OK, because the HTML5 spec doesn't define an order for 286 // this enumeration. 287 AppendToArray(aNames, mNameLookupTable.Keys()); 288 } 289 290 /* virtual */ 291 JSObject* HTMLFormControlsCollection::WrapObject( 292 JSContext* aCx, JS::Handle<JSObject*> aGivenProto) { 293 return HTMLFormControlsCollection_Binding::Wrap(aCx, this, aGivenProto); 294 } 295 296 } // namespace mozilla::dom