HTMLOptionsCollection.cpp (6195B)
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/HTMLOptionsCollection.h" 8 9 #include "mozilla/dom/HTMLOptionElement.h" 10 #include "mozilla/dom/HTMLOptionsCollectionBinding.h" 11 #include "mozilla/dom/HTMLSelectElement.h" 12 13 namespace mozilla::dom { 14 15 HTMLOptionsCollection::HTMLOptionsCollection(HTMLSelectElement* aSelect) 16 : mSelect(aSelect) {} 17 18 nsresult HTMLOptionsCollection::GetOptionIndex(Element* aOption, 19 int32_t aStartIndex, 20 bool aForward, int32_t* aIndex) { 21 // NOTE: aIndex shouldn't be set if the returned value isn't NS_OK. 22 23 int32_t index; 24 25 // Make the common case fast 26 if (aStartIndex == 0 && aForward) { 27 index = mElements.IndexOf(aOption); 28 if (index == -1) { 29 return NS_ERROR_FAILURE; 30 } 31 32 *aIndex = index; 33 return NS_OK; 34 } 35 36 int32_t high = mElements.Length(); 37 int32_t step = aForward ? 1 : -1; 38 39 for (index = aStartIndex; index < high && index > -1; index += step) { 40 if (mElements[index] == aOption) { 41 *aIndex = index; 42 return NS_OK; 43 } 44 } 45 46 return NS_ERROR_FAILURE; 47 } 48 49 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(HTMLOptionsCollection, mElements, mSelect) 50 51 // nsISupports 52 53 // QueryInterface implementation for HTMLOptionsCollection 54 NS_INTERFACE_TABLE_HEAD(HTMLOptionsCollection) 55 NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY 56 NS_INTERFACE_TABLE(HTMLOptionsCollection, nsIHTMLCollection) 57 NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(HTMLOptionsCollection) 58 NS_INTERFACE_MAP_END 59 60 NS_IMPL_CYCLE_COLLECTING_ADDREF(HTMLOptionsCollection) 61 NS_IMPL_CYCLE_COLLECTING_RELEASE(HTMLOptionsCollection) 62 63 JSObject* HTMLOptionsCollection::WrapObject(JSContext* aCx, 64 JS::Handle<JSObject*> aGivenProto) { 65 return HTMLOptionsCollection_Binding::Wrap(aCx, this, aGivenProto); 66 } 67 68 uint32_t HTMLOptionsCollection::Length() { return mElements.Length(); } 69 70 void HTMLOptionsCollection::SetLength(uint32_t aLength, ErrorResult& aError) { 71 mSelect->SetLength(aLength, aError); 72 } 73 74 void HTMLOptionsCollection::IndexedSetter(uint32_t aIndex, 75 HTMLOptionElement* aOption, 76 ErrorResult& aError) { 77 // if the new option is null, just remove this option. Note that it's safe 78 // to pass a too-large aIndex in here. 79 if (!aOption) { 80 mSelect->Remove(aIndex); 81 82 // We're done. 83 return; 84 } 85 86 // Now we're going to be setting an option in our collection 87 if (aIndex > mElements.Length()) { 88 // Fill our array with blank options up to (but not including, since we're 89 // about to change it) aIndex, for compat with other browsers. 90 SetLength(aIndex, aError); 91 if (NS_WARN_IF(aError.Failed())) { 92 return; 93 } 94 } 95 96 NS_ASSERTION(aIndex <= mElements.Length(), "SetLength lied"); 97 98 if (aIndex == mElements.Length()) { 99 mSelect->AppendChild(*aOption, aError); 100 return; 101 } 102 103 // Find the option they're talking about and replace it 104 // hold a strong reference to follow COM rules. 105 RefPtr<HTMLOptionElement> refChild = ItemAsOption(aIndex); 106 if (!refChild) { 107 aError.Throw(NS_ERROR_UNEXPECTED); 108 return; 109 } 110 111 nsCOMPtr<nsINode> parent = refChild->GetParent(); 112 if (!parent) { 113 return; 114 } 115 116 parent->ReplaceChild(*aOption, *refChild, aError); 117 } 118 119 int32_t HTMLOptionsCollection::SelectedIndex() { 120 return mSelect->SelectedIndex(); 121 } 122 123 void HTMLOptionsCollection::SetSelectedIndex(int32_t aSelectedIndex) { 124 mSelect->SetSelectedIndex(aSelectedIndex); 125 } 126 127 Element* HTMLOptionsCollection::GetElementAt(uint32_t aIndex) { 128 return ItemAsOption(aIndex); 129 } 130 131 HTMLOptionElement* HTMLOptionsCollection::NamedGetter(const nsAString& aName, 132 bool& aFound) { 133 if (aName.IsEmpty()) { 134 aFound = false; 135 return nullptr; 136 } 137 uint32_t count = mElements.Length(); 138 for (uint32_t i = 0; i < count; i++) { 139 HTMLOptionElement* content = mElements.ElementAt(i); 140 if (content && (content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::name, 141 aName, eCaseMatters) || 142 content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::id, 143 aName, eCaseMatters))) { 144 aFound = true; 145 return content; 146 } 147 } 148 149 aFound = false; 150 return nullptr; 151 } 152 153 nsINode* HTMLOptionsCollection::GetParentObject() { return mSelect; } 154 155 DocGroup* HTMLOptionsCollection::GetDocGroup() const { 156 return mSelect ? mSelect->GetDocGroup() : nullptr; 157 } 158 159 void HTMLOptionsCollection::GetSupportedNames(nsTArray<nsString>& aNames) { 160 AutoTArray<nsAtom*, 8> atoms; 161 for (uint32_t i = 0; i < mElements.Length(); ++i) { 162 HTMLOptionElement* content = mElements.ElementAt(i); 163 if (content) { 164 // Note: HasName means the names is exposed on the document, 165 // which is false for options, so we don't check it here. 166 const nsAttrValue* val = content->GetParsedAttr(nsGkAtoms::name); 167 if (val && val->Type() == nsAttrValue::eAtom) { 168 nsAtom* name = val->GetAtomValue(); 169 if (!atoms.Contains(name)) { 170 atoms.AppendElement(name); 171 } 172 } 173 if (content->HasID()) { 174 nsAtom* id = content->GetID(); 175 if (!atoms.Contains(id)) { 176 atoms.AppendElement(id); 177 } 178 } 179 } 180 } 181 182 uint32_t atomsLen = atoms.Length(); 183 nsString* names = aNames.AppendElements(atomsLen); 184 for (uint32_t i = 0; i < atomsLen; ++i) { 185 atoms[i]->ToString(names[i]); 186 } 187 } 188 189 void HTMLOptionsCollection::Add(const HTMLOptionOrOptGroupElement& aElement, 190 const Nullable<HTMLElementOrLong>& aBefore, 191 ErrorResult& aError) { 192 mSelect->Add(aElement, aBefore, aError); 193 } 194 195 void HTMLOptionsCollection::Remove(int32_t aIndex) { mSelect->Remove(aIndex); } 196 197 } // namespace mozilla::dom