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"
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
,
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
)
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()) {
53 const nsAString
& nameOrIndex
= aNameOrIndex
.Value();
55 if (js::StringIsArrayIndex(nameOrIndex
.BeginReading(), nameOrIndex
.Length(),
57 Element
* element
= Item(indexVal
);
59 aResult
.SetValue().SetAsElement() = element
;
66 NamedItem(nameOrIndex
, aResult
);
69 nsContentList
* HTMLAllCollection::Collection() {
71 Document
* document
= mDocument
;
72 mCollection
= document
->GetElementsByTagName(NS_LITERAL_STRING("*"));
73 MOZ_ASSERT(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
) {
92 nsGenericHTMLElement
* elm
= nsGenericHTMLElement::FromNode(aElement
);
97 if (!IsAllNamedElement(elm
)) {
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,
114 void HTMLAllCollection::NamedGetter(
115 const nsAString
& aID
, bool& aFound
,
116 Nullable
<OwningHTMLCollectionOrElement
>& aResult
) {
123 nsContentList
* docAllList
= GetDocumentAllList(aID
);
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)) {
135 aResult
.SetValue().SetAsHTMLCollection() = docAllList
;
139 // There's only 0 or 1 items. Return the first one or null.
140 if (nsIContent
* node
= docAllList
->Item(0, true)) {
142 aResult
.SetValue().SetAsElement() = node
->AsElement();
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
);
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
);
193 } // namespace mozilla