Line data Source code
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/HTMLAllCollection.h"
8 :
9 : #include "mozilla/dom/HTMLAllCollectionBinding.h"
10 : #include "mozilla/dom/Nullable.h"
11 : #include "mozilla/dom/Element.h"
12 : #include "nsHTMLDocument.h"
13 :
14 : namespace mozilla {
15 : namespace dom {
16 :
17 0 : HTMLAllCollection::HTMLAllCollection(nsHTMLDocument* aDocument)
18 0 : : mDocument(aDocument)
19 : {
20 0 : MOZ_ASSERT(mDocument);
21 0 : }
22 :
23 0 : HTMLAllCollection::~HTMLAllCollection()
24 : {
25 0 : }
26 :
27 0 : NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(HTMLAllCollection,
28 : mDocument,
29 : mCollection,
30 : mNamedMap)
31 :
32 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(HTMLAllCollection)
33 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(HTMLAllCollection)
34 :
35 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(HTMLAllCollection)
36 0 : NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
37 0 : NS_INTERFACE_MAP_ENTRY(nsISupports)
38 0 : NS_INTERFACE_MAP_END
39 :
40 : nsINode*
41 0 : HTMLAllCollection::GetParentObject() const
42 : {
43 0 : return mDocument;
44 : }
45 :
46 : uint32_t
47 0 : HTMLAllCollection::Length()
48 : {
49 0 : return Collection()->Length(true);
50 : }
51 :
52 : nsIContent*
53 0 : HTMLAllCollection::Item(uint32_t aIndex)
54 : {
55 0 : return Collection()->Item(aIndex);
56 : }
57 :
58 : nsContentList*
59 0 : HTMLAllCollection::Collection()
60 : {
61 0 : if (!mCollection) {
62 0 : nsIDocument* document = mDocument;
63 0 : mCollection = document->GetElementsByTagName(NS_LITERAL_STRING("*"));
64 0 : MOZ_ASSERT(mCollection);
65 : }
66 0 : return mCollection;
67 : }
68 :
69 : static bool
70 0 : IsAllNamedElement(nsIContent* aContent)
71 : {
72 0 : return aContent->IsAnyOfHTMLElements(nsGkAtoms::a,
73 : nsGkAtoms::applet,
74 : nsGkAtoms::button,
75 : nsGkAtoms::embed,
76 : nsGkAtoms::form,
77 : nsGkAtoms::iframe,
78 : nsGkAtoms::img,
79 : nsGkAtoms::input,
80 : nsGkAtoms::map,
81 : nsGkAtoms::meta,
82 : nsGkAtoms::object,
83 : nsGkAtoms::select,
84 : nsGkAtoms::textarea,
85 : nsGkAtoms::frame,
86 0 : nsGkAtoms::frameset);
87 : }
88 :
89 : static bool
90 0 : DocAllResultMatch(Element* aElement, int32_t aNamespaceID, nsIAtom* aAtom,
91 : void* aData)
92 : {
93 0 : if (aElement->GetID() == aAtom) {
94 0 : return true;
95 : }
96 :
97 0 : nsGenericHTMLElement* elm = nsGenericHTMLElement::FromContent(aElement);
98 0 : if (!elm) {
99 0 : return false;
100 : }
101 :
102 0 : if (!IsAllNamedElement(elm)) {
103 0 : return false;
104 : }
105 :
106 0 : const nsAttrValue* val = elm->GetParsedAttr(nsGkAtoms::name);
107 0 : return val && val->Type() == nsAttrValue::eAtom &&
108 0 : val->GetAtomValue() == aAtom;
109 : }
110 :
111 : nsContentList*
112 0 : HTMLAllCollection::GetDocumentAllList(const nsAString& aID)
113 : {
114 0 : return mNamedMap.LookupForAdd(aID).OrInsert(
115 0 : [this, &aID] () {
116 0 : nsCOMPtr<nsIAtom> id = NS_Atomize(aID);
117 : return new nsContentList(mDocument, DocAllResultMatch, nullptr,
118 0 : nullptr, true, id);
119 0 : });
120 : }
121 :
122 : void
123 0 : HTMLAllCollection::NamedGetter(const nsAString& aID,
124 : bool& aFound,
125 : Nullable<OwningNodeOrHTMLCollection>& aResult)
126 : {
127 0 : if (aID.IsEmpty()) {
128 0 : aFound = false;
129 0 : aResult.SetNull();
130 0 : return;
131 : }
132 :
133 0 : nsContentList* docAllList = GetDocumentAllList(aID);
134 0 : if (!docAllList) {
135 0 : aFound = false;
136 0 : aResult.SetNull();
137 0 : return;
138 : }
139 :
140 : // Check if there are more than 1 entries. Do this by getting the second one
141 : // rather than the length since getting the length always requires walking
142 : // the entire document.
143 0 : if (docAllList->Item(1, true)) {
144 0 : aFound = true;
145 0 : aResult.SetValue().SetAsHTMLCollection() = docAllList;
146 0 : return;
147 : }
148 :
149 : // There's only 0 or 1 items. Return the first one or null.
150 0 : if (nsIContent* node = docAllList->Item(0, true)) {
151 0 : aFound = true;
152 0 : aResult.SetValue().SetAsNode() = node;
153 0 : return;
154 : }
155 :
156 0 : aFound = false;
157 0 : aResult.SetNull();
158 : }
159 :
160 : void
161 0 : HTMLAllCollection::GetSupportedNames(nsTArray<nsString>& aNames)
162 : {
163 : // XXXbz this is very similar to nsContentList::GetSupportedNames,
164 : // but has to check IsAllNamedElement for the name case.
165 0 : AutoTArray<nsIAtom*, 8> atoms;
166 0 : for (uint32_t i = 0; i < Length(); ++i) {
167 0 : nsIContent *content = Item(i);
168 0 : if (content->HasID()) {
169 0 : nsIAtom* id = content->GetID();
170 0 : MOZ_ASSERT(id != nsGkAtoms::_empty,
171 : "Empty ids don't get atomized");
172 0 : if (!atoms.Contains(id)) {
173 0 : atoms.AppendElement(id);
174 : }
175 : }
176 :
177 0 : nsGenericHTMLElement* el = nsGenericHTMLElement::FromContent(content);
178 0 : if (el) {
179 : // Note: nsINode::HasName means the name is exposed on the document,
180 : // which is false for options, so we don't check it here.
181 0 : const nsAttrValue* val = el->GetParsedAttr(nsGkAtoms::name);
182 0 : if (val && val->Type() == nsAttrValue::eAtom &&
183 0 : IsAllNamedElement(content)) {
184 0 : nsIAtom* name = val->GetAtomValue();
185 0 : MOZ_ASSERT(name != nsGkAtoms::_empty,
186 : "Empty names don't get atomized");
187 0 : if (!atoms.Contains(name)) {
188 0 : atoms.AppendElement(name);
189 : }
190 : }
191 : }
192 : }
193 :
194 0 : uint32_t atomsLen = atoms.Length();
195 0 : nsString* names = aNames.AppendElements(atomsLen);
196 0 : for (uint32_t i = 0; i < atomsLen; ++i) {
197 0 : atoms[i]->ToString(names[i]);
198 : }
199 0 : }
200 :
201 :
202 : JSObject*
203 0 : HTMLAllCollection::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
204 : {
205 0 : return HTMLAllCollectionBinding::Wrap(aCx, this, aGivenProto);
206 : }
207 :
208 : } // namespace dom
209 : } // namespace mozilla
|