Implementors of memoryCost() need to be thread-safe.
[WebKit-https.git] / Source / WebCore / html / HTMLCollection.h
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 public:
65     virtual ~HTMLCollection();
66
67     // DOM API
68     Element* item(unsigned index) const override = 0; // Tighten return type from NodeList::item().
69     virtual Element* namedItem(const AtomicString& name) const = 0;
70     const Vector<AtomicString>& supportedPropertyNames();
71     bool isSupportedPropertyName(const String& name);
72
73     // Non-DOM API
74     Vector<Ref<Element>> namedItems(const AtomicString& name) const;
75     size_t memoryCost() const override;
76
77     bool isRootedAtDocument() const;
78     NodeListInvalidationType invalidationType() const;
79     CollectionType type() const;
80     ContainerNode& ownerNode() const;
81     ContainerNode& rootNode() const;
82     void invalidateCacheForAttribute(const QualifiedName& attributeName);
83     virtual void invalidateCacheForDocument(Document&);
84     void invalidateCache() { invalidateCacheForDocument(document()); }
85
86     bool hasNamedElementCache() const;
87
88 protected:
89     HTMLCollection(ContainerNode& base, CollectionType);
90
91     virtual void updateNamedElementCache() const;
92     WEBCORE_EXPORT Element* namedItemSlow(const AtomicString& name) const;
93
94     void setNamedItemCache(std::unique_ptr<CollectionNamedElementCache>) const;
95     const CollectionNamedElementCache& namedItemCaches() const;
96
97     Document& document() const;
98
99     void invalidateNamedElementCache(Document&) const;
100
101     enum RootType { IsRootedAtNode, IsRootedAtDocument };
102     static RootType rootTypeFromCollectionType(CollectionType);
103
104     Ref<ContainerNode> m_ownerNode;
105
106     mutable std::unique_ptr<CollectionNamedElementCache> m_namedElementCache;
107     mutable Lock m_namedElementCacheAssignmentLock;
108     
109     const unsigned m_collectionType : 5;
110     const unsigned m_invalidationType : 4;
111     const unsigned m_rootType : 1;
112 };
113
114 inline ContainerNode& HTMLCollection::rootNode() const
115 {
116     if (isRootedAtDocument() && ownerNode().isConnected())
117         return ownerNode().document();
118
119     return ownerNode();
120 }
121
122 inline const Vector<Element*>* CollectionNamedElementCache::findElementsWithId(const AtomicString& id) const
123 {
124     return find(m_idMap, id);
125 }
126
127 inline const Vector<Element*>* CollectionNamedElementCache::findElementsWithName(const AtomicString& name) const
128 {
129     return find(m_nameMap, name);
130 }
131
132 inline void CollectionNamedElementCache::appendToIdCache(const AtomicString& id, Element& element)
133 {
134     append(m_idMap, id, element);
135 }
136
137 inline void CollectionNamedElementCache::appendToNameCache(const AtomicString& name, Element& element)
138 {
139     append(m_nameMap, name, element);
140 }
141
142 inline size_t CollectionNamedElementCache::memoryCost() const
143 {
144     // memoryCost() may be invoked concurrently from a GC thread, and we need to be careful about what data we access here and how.
145     // It is safe to access m_idMap.size(), m_nameMap.size(), and m_propertyNames.size() because they don't chase pointers.
146     return (m_idMap.size() + m_nameMap.size()) * sizeof(Element*) + m_propertyNames.size() * sizeof(AtomicString);
147 }
148
149 inline void CollectionNamedElementCache::didPopulate()
150 {
151 #if !ASSERT_DISABLED
152     m_didPopulate = true;
153 #endif
154     if (size_t cost = memoryCost())
155         reportExtraMemoryAllocatedForCollectionIndexCache(cost);
156 }
157
158 inline const Vector<Element*>* CollectionNamedElementCache::find(const StringToElementsMap& map, const AtomicString& key) const
159 {
160     ASSERT(m_didPopulate);
161     auto it = map.find(key.impl());
162     return it != map.end() ? &it->value : nullptr;
163 }
164
165 inline void CollectionNamedElementCache::append(StringToElementsMap& map, const AtomicString& key, Element& element)
166 {
167     if (!m_idMap.contains(key.impl()) && !m_nameMap.contains(key.impl()))
168         m_propertyNames.append(key);
169     map.add(key.impl(), Vector<Element*>()).iterator->value.append(&element);
170 }
171
172 inline size_t HTMLCollection::memoryCost() const
173 {
174     // memoryCost() may be invoked concurrently from a GC thread, and we need to be careful about what data we access here and how.
175     // Hence, we need to guard m_namedElementCache from being replaced while accessing it.
176     auto locker = holdLock(m_namedElementCacheAssignmentLock);
177     return m_namedElementCache ? m_namedElementCache->memoryCost() : 0;
178 }
179
180 inline bool HTMLCollection::isRootedAtDocument() const
181 {
182     return m_rootType == IsRootedAtDocument;
183 }
184
185 inline NodeListInvalidationType HTMLCollection::invalidationType() const
186 {
187     return static_cast<NodeListInvalidationType>(m_invalidationType);
188 }
189
190 inline CollectionType HTMLCollection::type() const
191 {
192     return static_cast<CollectionType>(m_collectionType);
193 }
194
195 inline ContainerNode& HTMLCollection::ownerNode() const
196 {
197     return m_ownerNode;
198 }
199
200 inline Document& HTMLCollection::document() const
201 {
202     return m_ownerNode->document();
203 }
204
205 inline void HTMLCollection::invalidateCacheForAttribute(const QualifiedName& attributeName)
206 {
207     if (shouldInvalidateTypeOnAttributeChange(invalidationType(), attributeName))
208         invalidateCache();
209     else if (hasNamedElementCache() && (attributeName == HTMLNames::idAttr || attributeName == HTMLNames::nameAttr))
210         invalidateNamedElementCache(document());
211 }
212
213 inline bool HTMLCollection::hasNamedElementCache() const
214 {
215     return !!m_namedElementCache;
216 }
217
218 inline void HTMLCollection::setNamedItemCache(std::unique_ptr<CollectionNamedElementCache> cache) const
219 {
220     ASSERT(cache);
221     ASSERT(!m_namedElementCache);
222     cache->didPopulate();
223     {
224         auto locker = holdLock(m_namedElementCacheAssignmentLock);
225         m_namedElementCache = WTFMove(cache);
226     }
227     document().collectionCachedIdNameMap(*this);
228 }
229
230 inline const CollectionNamedElementCache& HTMLCollection::namedItemCaches() const
231 {
232     ASSERT(!!m_namedElementCache);
233     return *m_namedElementCache;
234 }
235
236 } // namespace WebCore
237
238 #define SPECIALIZE_TYPE_TRAITS_HTMLCOLLECTION(ClassName, Type) \
239 SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::ClassName) \
240     static bool isType(const WebCore::HTMLCollection& collection) { return collection.type() == WebCore::Type; } \
241 SPECIALIZE_TYPE_TRAITS_END()