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 "jsfriendapi.h"
10 #include "mozilla/dom/HTMLAllCollectionBinding.h"
11 #include "mozilla/dom/Nullable.h"
12 #include "mozilla/dom/Element.h"
13 #include "nsContentList.h"
14 #include "nsGenericHTMLElement.h"
16 namespace mozilla::dom
{
18 HTMLAllCollection::HTMLAllCollection(mozilla::dom::Document
* aDocument
)
19 : mDocument(aDocument
) {
20 MOZ_ASSERT(mDocument
);
23 HTMLAllCollection::~HTMLAllCollection() = default;
25 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(HTMLAllCollection
, mDocument
, mCollection
,
28 NS_IMPL_CYCLE_COLLECTING_ADDREF(HTMLAllCollection
)
29 NS_IMPL_CYCLE_COLLECTING_RELEASE(HTMLAllCollection
)
31 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(HTMLAllCollection
)
32 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
33 NS_INTERFACE_MAP_ENTRY(nsISupports
)
36 nsINode
* HTMLAllCollection::GetParentObject() const { return mDocument
; }
38 uint32_t HTMLAllCollection::Length() { return Collection()->Length(true); }
40 Element
* HTMLAllCollection::Item(uint32_t aIndex
) {
41 nsIContent
* item
= Collection()->Item(aIndex
);
42 return item
? item
->AsElement() : nullptr;
45 void HTMLAllCollection::Item(const Optional
<nsAString
>& aNameOrIndex
,
46 Nullable
<OwningHTMLCollectionOrElement
>& aResult
) {
47 if (!aNameOrIndex
.WasPassed()) {
52 const nsAString
& nameOrIndex
= aNameOrIndex
.Value();
54 if (js::StringIsArrayIndex(nameOrIndex
.BeginReading(), nameOrIndex
.Length(),
56 Element
* element
= Item(indexVal
);
58 aResult
.SetValue().SetAsElement() = element
;
65 NamedItem(nameOrIndex
, aResult
);
68 nsContentList
* HTMLAllCollection::Collection() {
70 Document
* document
= mDocument
;
71 mCollection
= document
->GetElementsByTagName(u
"*"_ns
);
72 MOZ_ASSERT(mCollection
);
77 static bool IsAllNamedElement(nsIContent
* aContent
) {
78 return aContent
->IsAnyOfHTMLElements(
79 nsGkAtoms::a
, nsGkAtoms::button
, nsGkAtoms::embed
, nsGkAtoms::form
,
80 nsGkAtoms::iframe
, nsGkAtoms::img
, nsGkAtoms::input
, nsGkAtoms::map
,
81 nsGkAtoms::meta
, nsGkAtoms::object
, nsGkAtoms::select
,
82 nsGkAtoms::textarea
, nsGkAtoms::frame
, nsGkAtoms::frameset
);
85 static bool DocAllResultMatch(Element
* aElement
, int32_t aNamespaceID
,
86 nsAtom
* aAtom
, void* aData
) {
87 if (aElement
->GetID() == aAtom
) {
91 nsGenericHTMLElement
* elm
= nsGenericHTMLElement::FromNode(aElement
);
96 if (!IsAllNamedElement(elm
)) {
100 const nsAttrValue
* val
= elm
->GetParsedAttr(nsGkAtoms::name
);
101 return val
&& val
->Type() == nsAttrValue::eAtom
&&
102 val
->GetAtomValue() == aAtom
;
105 nsContentList
* HTMLAllCollection::GetDocumentAllList(const nsAString
& aID
) {
107 .LookupOrInsertWith(aID
,
109 RefPtr
<nsAtom
> id
= NS_Atomize(aID
);
110 return new nsContentList(mDocument
,
111 DocAllResultMatch
, nullptr,
117 void HTMLAllCollection::NamedGetter(
118 const nsAString
& aID
, bool& aFound
,
119 Nullable
<OwningHTMLCollectionOrElement
>& aResult
) {
126 nsContentList
* docAllList
= GetDocumentAllList(aID
);
133 // Check if there are more than 1 entries. Do this by getting the second one
134 // rather than the length since getting the length always requires walking
135 // the entire document.
136 if (docAllList
->Item(1, true)) {
138 aResult
.SetValue().SetAsHTMLCollection() = docAllList
;
142 // There's only 0 or 1 items. Return the first one or null.
143 if (nsIContent
* node
= docAllList
->Item(0, true)) {
145 aResult
.SetValue().SetAsElement() = node
->AsElement();
153 void HTMLAllCollection::GetSupportedNames(nsTArray
<nsString
>& aNames
) {
154 // XXXbz this is very similar to nsContentList::GetSupportedNames,
155 // but has to check IsAllNamedElement for the name case.
156 AutoTArray
<nsAtom
*, 8> atoms
;
157 for (uint32_t i
= 0; i
< Length(); ++i
) {
158 nsIContent
* content
= Item(i
);
159 if (content
->HasID()) {
160 nsAtom
* id
= content
->GetID();
161 MOZ_ASSERT(id
!= nsGkAtoms::_empty
, "Empty ids don't get atomized");
162 if (!atoms
.Contains(id
)) {
163 atoms
.AppendElement(id
);
167 nsGenericHTMLElement
* el
= nsGenericHTMLElement::FromNode(content
);
169 // Note: nsINode::HasName means the name is exposed on the document,
170 // which is false for options, so we don't check it here.
171 const nsAttrValue
* val
= el
->GetParsedAttr(nsGkAtoms::name
);
172 if (val
&& val
->Type() == nsAttrValue::eAtom
&&
173 IsAllNamedElement(content
)) {
174 nsAtom
* name
= val
->GetAtomValue();
175 MOZ_ASSERT(name
!= nsGkAtoms::_empty
, "Empty names don't get atomized");
176 if (!atoms
.Contains(name
)) {
177 atoms
.AppendElement(name
);
183 uint32_t atomsLen
= atoms
.Length();
184 nsString
* names
= aNames
.AppendElements(atomsLen
);
185 for (uint32_t i
= 0; i
< atomsLen
; ++i
) {
186 atoms
[i
]->ToString(names
[i
]);
190 JSObject
* HTMLAllCollection::WrapObject(JSContext
* aCx
,
191 JS::Handle
<JSObject
*> aGivenProto
) {
192 return HTMLAllCollection_Binding::Wrap(aCx
, this, aGivenProto
);
195 } // namespace mozilla::dom