1 | /* |
2 | * Copyright (C) 1999 Lars Knoll (knoll@kde.org) |
3 | * (C) 1999 Antti Koivisto (koivisto@kde.org) |
4 | * Copyright (C) 2003-2017 Apple Inc. All rights reserved. |
5 | * |
6 | * This library is free software; you can redistribute it and/or |
7 | * modify it under the terms of the GNU Library General Public |
8 | * License as published by the Free Software Foundation; either |
9 | * version 2 of the License, or (at your option) any later version. |
10 | * |
11 | * This library is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | * Library General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU Library General Public License |
17 | * along with this library; see the file COPYING.LIB. If not, write to |
18 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
19 | * Boston, MA 02110-1301, USA. |
20 | * |
21 | */ |
22 | |
23 | #pragma once |
24 | |
25 | #include "CollectionIndexCache.h" |
26 | #include "HTMLNames.h" |
27 | #include "LiveNodeList.h" |
28 | #include <wtf/HashMap.h> |
29 | |
30 | namespace WebCore { |
31 | |
32 | class Element; |
33 | |
34 | class CollectionNamedElementCache { |
35 | WTF_MAKE_FAST_ALLOCATED; |
36 | public: |
37 | const Vector<Element*>* findElementsWithId(const AtomicString& id) const; |
38 | const Vector<Element*>* findElementsWithName(const AtomicString& name) const; |
39 | const Vector<AtomicString>& propertyNames() const { return m_propertyNames; } |
40 | |
41 | void appendToIdCache(const AtomicString& id, Element&); |
42 | void appendToNameCache(const AtomicString& name, Element&); |
43 | void didPopulate(); |
44 | |
45 | size_t memoryCost() const; |
46 | |
47 | private: |
48 | typedef HashMap<AtomicStringImpl*, Vector<Element*>> StringToElementsMap; |
49 | |
50 | const Vector<Element*>* find(const StringToElementsMap&, const AtomicString& key) const; |
51 | void append(StringToElementsMap&, const AtomicString& key, Element&); |
52 | |
53 | StringToElementsMap m_idMap; |
54 | StringToElementsMap m_nameMap; |
55 | Vector<AtomicString> m_propertyNames; |
56 | |
57 | #if !ASSERT_DISABLED |
58 | bool m_didPopulate { false }; |
59 | #endif |
60 | }; |
61 | |
62 | // HTMLCollection subclasses NodeList to maintain legacy ObjC API compatibility. |
63 | class HTMLCollection : public NodeList { |
64 | WTF_MAKE_ISO_ALLOCATED(HTMLCollection); |
65 | public: |
66 | virtual ~HTMLCollection(); |
67 | |
68 | // DOM API |
69 | Element* item(unsigned index) const override = 0; // Tighten return type from NodeList::item(). |
70 | virtual Element* namedItem(const AtomicString& name) const = 0; |
71 | const Vector<AtomicString>& supportedPropertyNames(); |
72 | bool isSupportedPropertyName(const String& name); |
73 | |
74 | // Non-DOM API |
75 | Vector<Ref<Element>> namedItems(const AtomicString& name) const; |
76 | size_t memoryCost() const override; |
77 | |
78 | bool isRootedAtDocument() const; |
79 | NodeListInvalidationType invalidationType() const; |
80 | CollectionType type() const; |
81 | ContainerNode& ownerNode() const; |
82 | ContainerNode& rootNode() const; |
83 | void invalidateCacheForAttribute(const QualifiedName& attributeName); |
84 | virtual void invalidateCacheForDocument(Document&); |
85 | void invalidateCache() { invalidateCacheForDocument(document()); } |
86 | |
87 | bool hasNamedElementCache() const; |
88 | |
89 | protected: |
90 | HTMLCollection(ContainerNode& base, CollectionType); |
91 | |
92 | virtual void updateNamedElementCache() const; |
93 | WEBCORE_EXPORT Element* namedItemSlow(const AtomicString& name) const; |
94 | |
95 | void setNamedItemCache(std::unique_ptr<CollectionNamedElementCache>) const; |
96 | const CollectionNamedElementCache& namedItemCaches() const; |
97 | |
98 | Document& document() const; |
99 | |
100 | void invalidateNamedElementCache(Document&) const; |
101 | |
102 | enum RootType { IsRootedAtNode, IsRootedAtDocument }; |
103 | static RootType rootTypeFromCollectionType(CollectionType); |
104 | |
105 | mutable Lock m_namedElementCacheAssignmentLock; |
106 | |
107 | const unsigned m_collectionType : 5; // CollectionType |
108 | const unsigned m_invalidationType : 4; // NodeListInvalidationType |
109 | const unsigned m_rootType : 1; // RootType |
110 | |
111 | Ref<ContainerNode> m_ownerNode; |
112 | |
113 | mutable std::unique_ptr<CollectionNamedElementCache> m_namedElementCache; |
114 | }; |
115 | |
116 | inline ContainerNode& HTMLCollection::rootNode() const |
117 | { |
118 | if (isRootedAtDocument() && ownerNode().isConnected()) |
119 | return ownerNode().document(); |
120 | |
121 | return ownerNode(); |
122 | } |
123 | |
124 | inline const Vector<Element*>* CollectionNamedElementCache::findElementsWithId(const AtomicString& id) const |
125 | { |
126 | return find(m_idMap, id); |
127 | } |
128 | |
129 | inline const Vector<Element*>* CollectionNamedElementCache::findElementsWithName(const AtomicString& name) const |
130 | { |
131 | return find(m_nameMap, name); |
132 | } |
133 | |
134 | inline void CollectionNamedElementCache::appendToIdCache(const AtomicString& id, Element& element) |
135 | { |
136 | append(m_idMap, id, element); |
137 | } |
138 | |
139 | inline void CollectionNamedElementCache::appendToNameCache(const AtomicString& name, Element& element) |
140 | { |
141 | append(m_nameMap, name, element); |
142 | } |
143 | |
144 | inline size_t CollectionNamedElementCache::memoryCost() const |
145 | { |
146 | // memoryCost() may be invoked concurrently from a GC thread, and we need to be careful about what data we access here and how. |
147 | // It is safe to access m_idMap.size(), m_nameMap.size(), and m_propertyNames.size() because they don't chase pointers. |
148 | return (m_idMap.size() + m_nameMap.size()) * sizeof(Element*) + m_propertyNames.size() * sizeof(AtomicString); |
149 | } |
150 | |
151 | inline void CollectionNamedElementCache::didPopulate() |
152 | { |
153 | #if !ASSERT_DISABLED |
154 | m_didPopulate = true; |
155 | #endif |
156 | if (size_t cost = memoryCost()) |
157 | reportExtraMemoryAllocatedForCollectionIndexCache(cost); |
158 | } |
159 | |
160 | inline const Vector<Element*>* CollectionNamedElementCache::find(const StringToElementsMap& map, const AtomicString& key) const |
161 | { |
162 | ASSERT(m_didPopulate); |
163 | auto it = map.find(key.impl()); |
164 | return it != map.end() ? &it->value : nullptr; |
165 | } |
166 | |
167 | inline void CollectionNamedElementCache::append(StringToElementsMap& map, const AtomicString& key, Element& element) |
168 | { |
169 | if (!m_idMap.contains(key.impl()) && !m_nameMap.contains(key.impl())) |
170 | m_propertyNames.append(key); |
171 | map.add(key.impl(), Vector<Element*>()).iterator->value.append(&element); |
172 | } |
173 | |
174 | inline size_t HTMLCollection::memoryCost() const |
175 | { |
176 | // memoryCost() may be invoked concurrently from a GC thread, and we need to be careful about what data we access here and how. |
177 | // Hence, we need to guard m_namedElementCache from being replaced while accessing it. |
178 | auto locker = holdLock(m_namedElementCacheAssignmentLock); |
179 | return m_namedElementCache ? m_namedElementCache->memoryCost() : 0; |
180 | } |
181 | |
182 | inline bool HTMLCollection::isRootedAtDocument() const |
183 | { |
184 | return m_rootType == IsRootedAtDocument; |
185 | } |
186 | |
187 | inline NodeListInvalidationType HTMLCollection::invalidationType() const |
188 | { |
189 | return static_cast<NodeListInvalidationType>(m_invalidationType); |
190 | } |
191 | |
192 | inline CollectionType HTMLCollection::type() const |
193 | { |
194 | return static_cast<CollectionType>(m_collectionType); |
195 | } |
196 | |
197 | inline ContainerNode& HTMLCollection::ownerNode() const |
198 | { |
199 | return m_ownerNode; |
200 | } |
201 | |
202 | inline Document& HTMLCollection::document() const |
203 | { |
204 | return m_ownerNode->document(); |
205 | } |
206 | |
207 | inline void HTMLCollection::invalidateCacheForAttribute(const QualifiedName& attributeName) |
208 | { |
209 | if (shouldInvalidateTypeOnAttributeChange(invalidationType(), attributeName)) |
210 | invalidateCache(); |
211 | else if (hasNamedElementCache() && (attributeName == HTMLNames::idAttr || attributeName == HTMLNames::nameAttr)) |
212 | invalidateNamedElementCache(document()); |
213 | } |
214 | |
215 | inline bool HTMLCollection::hasNamedElementCache() const |
216 | { |
217 | return !!m_namedElementCache; |
218 | } |
219 | |
220 | inline void HTMLCollection::setNamedItemCache(std::unique_ptr<CollectionNamedElementCache> cache) const |
221 | { |
222 | ASSERT(cache); |
223 | ASSERT(!m_namedElementCache); |
224 | cache->didPopulate(); |
225 | { |
226 | auto locker = holdLock(m_namedElementCacheAssignmentLock); |
227 | m_namedElementCache = WTFMove(cache); |
228 | } |
229 | document().collectionCachedIdNameMap(*this); |
230 | } |
231 | |
232 | inline const CollectionNamedElementCache& HTMLCollection::namedItemCaches() const |
233 | { |
234 | ASSERT(!!m_namedElementCache); |
235 | return *m_namedElementCache; |
236 | } |
237 | |
238 | } // namespace WebCore |
239 | |
240 | #define SPECIALIZE_TYPE_TRAITS_HTMLCOLLECTION(ClassName, Type) \ |
241 | SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::ClassName) \ |
242 | static bool isType(const WebCore::HTMLCollection& collection) { return collection.type() == WebCore::Type; } \ |
243 | SPECIALIZE_TYPE_TRAITS_END() |
244 | |