Bug 1560374 - Set testharness and reftest web-platform-tests to Tier-1; r=jmaher...
[gecko.git] / dom / html / HTMLAllCollection.cpp
blob413d25a4199887cb1cde0b99bf85e287a0b31cb3
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/HTMLAllCollection.h"
9 #include "mozilla/dom/HTMLAllCollectionBinding.h"
10 #include "mozilla/dom/Nullable.h"
11 #include "mozilla/dom/Document.h"
12 #include "mozilla/dom/Element.h"
13 #include "nsContentList.h"
14 #include "nsGenericHTMLElement.h"
16 namespace mozilla {
17 namespace dom {
19 HTMLAllCollection::HTMLAllCollection(mozilla::dom::Document* aDocument)
20 : mDocument(aDocument) {
21 MOZ_ASSERT(mDocument);
24 HTMLAllCollection::~HTMLAllCollection() {}
26 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(HTMLAllCollection, mDocument, mCollection,
27 mNamedMap)
29 NS_IMPL_CYCLE_COLLECTING_ADDREF(HTMLAllCollection)
30 NS_IMPL_CYCLE_COLLECTING_RELEASE(HTMLAllCollection)
32 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(HTMLAllCollection)
33 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
34 NS_INTERFACE_MAP_ENTRY(nsISupports)
35 NS_INTERFACE_MAP_END
37 nsINode* HTMLAllCollection::GetParentObject() const { return mDocument; }
39 uint32_t HTMLAllCollection::Length() { return Collection()->Length(true); }
41 Element* HTMLAllCollection::Item(uint32_t aIndex) {
42 nsIContent* item = Collection()->Item(aIndex);
43 return item ? item->AsElement() : nullptr;
46 void HTMLAllCollection::Item(const Optional<nsAString>& aNameOrIndex,
47 Nullable<OwningHTMLCollectionOrElement>& aResult) {
48 if (!aNameOrIndex.WasPassed()) {
49 aResult.SetNull();
50 return;
53 const nsAString& nameOrIndex = aNameOrIndex.Value();
54 uint32_t indexVal;
55 if (js::StringIsArrayIndex(nameOrIndex.BeginReading(), nameOrIndex.Length(),
56 &indexVal)) {
57 Element* element = Item(indexVal);
58 if (element) {
59 aResult.SetValue().SetAsElement() = element;
60 } else {
61 aResult.SetNull();
63 return;
66 NamedItem(nameOrIndex, aResult);
69 nsContentList* HTMLAllCollection::Collection() {
70 if (!mCollection) {
71 Document* document = mDocument;
72 mCollection = document->GetElementsByTagName(NS_LITERAL_STRING("*"));
73 MOZ_ASSERT(mCollection);
75 return mCollection;
78 static bool IsAllNamedElement(nsIContent* aContent) {
79 return aContent->IsAnyOfHTMLElements(
80 nsGkAtoms::a, nsGkAtoms::button, nsGkAtoms::embed, nsGkAtoms::form,
81 nsGkAtoms::iframe, nsGkAtoms::img, nsGkAtoms::input, nsGkAtoms::map,
82 nsGkAtoms::meta, nsGkAtoms::object, nsGkAtoms::select,
83 nsGkAtoms::textarea, nsGkAtoms::frame, nsGkAtoms::frameset);
86 static bool DocAllResultMatch(Element* aElement, int32_t aNamespaceID,
87 nsAtom* aAtom, void* aData) {
88 if (aElement->GetID() == aAtom) {
89 return true;
92 nsGenericHTMLElement* elm = nsGenericHTMLElement::FromNode(aElement);
93 if (!elm) {
94 return false;
97 if (!IsAllNamedElement(elm)) {
98 return false;
101 const nsAttrValue* val = elm->GetParsedAttr(nsGkAtoms::name);
102 return val && val->Type() == nsAttrValue::eAtom &&
103 val->GetAtomValue() == aAtom;
106 nsContentList* HTMLAllCollection::GetDocumentAllList(const nsAString& aID) {
107 return mNamedMap.LookupForAdd(aID).OrInsert([this, &aID]() {
108 RefPtr<nsAtom> id = NS_Atomize(aID);
109 return new nsContentList(mDocument, DocAllResultMatch, nullptr, nullptr,
110 true, id);
114 void HTMLAllCollection::NamedGetter(
115 const nsAString& aID, bool& aFound,
116 Nullable<OwningHTMLCollectionOrElement>& aResult) {
117 if (aID.IsEmpty()) {
118 aFound = false;
119 aResult.SetNull();
120 return;
123 nsContentList* docAllList = GetDocumentAllList(aID);
124 if (!docAllList) {
125 aFound = false;
126 aResult.SetNull();
127 return;
130 // Check if there are more than 1 entries. Do this by getting the second one
131 // rather than the length since getting the length always requires walking
132 // the entire document.
133 if (docAllList->Item(1, true)) {
134 aFound = true;
135 aResult.SetValue().SetAsHTMLCollection() = docAllList;
136 return;
139 // There's only 0 or 1 items. Return the first one or null.
140 if (nsIContent* node = docAllList->Item(0, true)) {
141 aFound = true;
142 aResult.SetValue().SetAsElement() = node->AsElement();
143 return;
146 aFound = false;
147 aResult.SetNull();
150 void HTMLAllCollection::GetSupportedNames(nsTArray<nsString>& aNames) {
151 // XXXbz this is very similar to nsContentList::GetSupportedNames,
152 // but has to check IsAllNamedElement for the name case.
153 AutoTArray<nsAtom*, 8> atoms;
154 for (uint32_t i = 0; i < Length(); ++i) {
155 nsIContent* content = Item(i);
156 if (content->HasID()) {
157 nsAtom* id = content->GetID();
158 MOZ_ASSERT(id != nsGkAtoms::_empty, "Empty ids don't get atomized");
159 if (!atoms.Contains(id)) {
160 atoms.AppendElement(id);
164 nsGenericHTMLElement* el = nsGenericHTMLElement::FromNode(content);
165 if (el) {
166 // Note: nsINode::HasName means the name is exposed on the document,
167 // which is false for options, so we don't check it here.
168 const nsAttrValue* val = el->GetParsedAttr(nsGkAtoms::name);
169 if (val && val->Type() == nsAttrValue::eAtom &&
170 IsAllNamedElement(content)) {
171 nsAtom* name = val->GetAtomValue();
172 MOZ_ASSERT(name != nsGkAtoms::_empty, "Empty names don't get atomized");
173 if (!atoms.Contains(name)) {
174 atoms.AppendElement(name);
180 uint32_t atomsLen = atoms.Length();
181 nsString* names = aNames.AppendElements(atomsLen);
182 for (uint32_t i = 0; i < atomsLen; ++i) {
183 atoms[i]->ToString(names[i]);
187 JSObject* HTMLAllCollection::WrapObject(JSContext* aCx,
188 JS::Handle<JSObject*> aGivenProto) {
189 return HTMLAllCollection_Binding::Wrap(aCx, this, aGivenProto);
192 } // namespace dom
193 } // namespace mozilla