Make `JSGlobalObject*` threading change more stabilized by adding tests and assertions
[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 AtomString& id) const;
38     const Vector<Element*>* findElementsWithName(const AtomString& name) const;
39     const Vector<AtomString>& propertyNames() const { return m_propertyNames; }
40     
41     void appendToIdCache(const AtomString& id, Element&);
42     void appendToNameCache(const AtomString& name, Element&);
43     void didPopulate();
44
45     size_t memoryCost() const;
46
47 private:
48     typedef HashMap<AtomStringImpl*, Vector<Element*>> StringToElementsMap;
49
50     const Vector<Element*>* find(const StringToElementsMap&, const AtomString& key) const;
51     void append(StringToElementsMap&, const AtomString& key, Element&);
52
53     StringToElementsMap m_idMap;
54     StringToElementsMap m_nameMap;
55     Vector<AtomString> 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 AtomString& name) const = 0;
71     const Vector<AtomString>& supportedPropertyNames();
72     bool isSupportedPropertyName(const String& name);
73
74     // Non-DOM API
75     Vector<Ref<Element>> namedItems(const AtomString& 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 AtomString& 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 AtomString& id) const
125 {
126     return find(m_idMap, id);
127 }
128
129 inline const Vector<Element*>* CollectionNamedElementCache::findElementsWithName(const AtomString& name) const
130 {
131     return find(m_nameMap, name);
132 }
133
134 inline void CollectionNamedElementCache::appendToIdCache(const AtomString& id, Element& element)
135 {
136     append(m_idMap, id, element);
137 }
138
139 inline void CollectionNamedElementCache::appendToNameCache(const AtomString& 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(AtomString);
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 AtomString& 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 AtomString& 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()