Avoid copy-prone idiom "for (auto item : collection)"
[WebKit-https.git] / Source / WebCore / dom / NodeRareData.h
1 /*
2  * Copyright (C) 2008, 2010 Apple Inc. All rights reserved.
3  * Copyright (C) 2008 David Smith <catfish.man@gmail.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public License
16  * along with this library; see the file COPYING.LIB.  If not, write to
17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  *
20  */
21
22 #ifndef NodeRareData_h
23 #define NodeRareData_h
24
25 #include "ChildNodeList.h"
26 #include "ClassNodeList.h"
27 #include "DOMSettableTokenList.h"
28 #include "HTMLCollection.h"
29 #include "HTMLNames.h"
30 #include "LiveNodeList.h"
31 #include "MutationObserver.h"
32 #include "MutationObserverRegistration.h"
33 #include "Page.h"
34 #include "QualifiedName.h"
35 #include "TagNodeList.h"
36 #include <wtf/HashSet.h>
37 #include <wtf/OwnPtr.h>
38 #include <wtf/PassOwnPtr.h>
39 #include <wtf/text/AtomicString.h>
40 #include <wtf/text/StringHash.h>
41
42 #if ENABLE(VIDEO_TRACK)
43 #include "TextTrack.h"
44 #endif
45
46 namespace WebCore {
47
48 class LabelsNodeList;
49 class RadioNodeList;
50 class TreeScope;
51
52 class NodeListsNodeData {
53     WTF_MAKE_NONCOPYABLE(NodeListsNodeData); WTF_MAKE_FAST_ALLOCATED;
54 public:
55     void clearChildNodeListCache()
56     {
57         if (m_childNodeList)
58             m_childNodeList->invalidateCache();
59     }
60
61     PassRefPtr<ChildNodeList> ensureChildNodeList(ContainerNode& node)
62     {
63         ASSERT(!m_emptyChildNodeList);
64         if (m_childNodeList)
65             return m_childNodeList;
66         RefPtr<ChildNodeList> list = ChildNodeList::create(node);
67         m_childNodeList = list.get();
68         return list.release();
69     }
70
71     void removeChildNodeList(ChildNodeList* list)
72     {
73         ASSERT(m_childNodeList == list);
74         if (deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(list->ownerNode()))
75             return;
76         m_childNodeList = nullptr;
77     }
78
79     PassRefPtr<EmptyNodeList> ensureEmptyChildNodeList(Node& node)
80     {
81         ASSERT(!m_childNodeList);
82         if (m_emptyChildNodeList)
83             return m_emptyChildNodeList;
84         RefPtr<EmptyNodeList> list = EmptyNodeList::create(node);
85         m_emptyChildNodeList = list.get();
86         return list.release();
87     }
88
89     void removeEmptyChildNodeList(EmptyNodeList* list)
90     {
91         ASSERT(m_emptyChildNodeList == list);
92         if (deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(list->ownerNode()))
93             return;
94         m_emptyChildNodeList = nullptr;
95     }
96
97     template <typename StringType>
98     struct NodeListCacheMapEntryHash {
99         static unsigned hash(const std::pair<unsigned char, StringType>& entry)
100         {
101             return DefaultHash<StringType>::Hash::hash(entry.second) + entry.first;
102         }
103         static bool equal(const std::pair<unsigned char, StringType>& a, const std::pair<unsigned char, StringType>& b) { return a == b; }
104         static const bool safeToCompareToEmptyOrDeleted = DefaultHash<StringType>::Hash::safeToCompareToEmptyOrDeleted;
105     };
106
107     typedef HashMap<std::pair<unsigned char, AtomicString>, LiveNodeList*, NodeListCacheMapEntryHash<AtomicString>> NodeListAtomicNameCacheMap;
108     typedef HashMap<std::pair<unsigned char, String>, LiveNodeList*, NodeListCacheMapEntryHash<String>> NodeListNameCacheMap;
109     typedef HashMap<std::pair<unsigned char, AtomicString>, HTMLCollection*, NodeListCacheMapEntryHash<AtomicString>> CollectionCacheMap;
110     typedef HashMap<QualifiedName, TagNodeList*> TagNodeListCacheNS;
111
112     template<typename T, typename ContainerType>
113     PassRefPtr<T> addCacheWithAtomicName(ContainerType& container, LiveNodeList::Type type, const AtomicString& name)
114     {
115         NodeListAtomicNameCacheMap::AddResult result = m_atomicNameCaches.add(namedNodeListKey(type, name), nullptr);
116         if (!result.isNewEntry)
117             return static_cast<T*>(result.iterator->value);
118
119         RefPtr<T> list = T::create(container, type, name);
120         result.iterator->value = list.get();
121         return list.release();
122     }
123
124     template<typename T>
125     PassRefPtr<T> addCacheWithName(ContainerNode& node, LiveNodeList::Type type, const String& name)
126     {
127         NodeListNameCacheMap::AddResult result = m_nameCaches.add(namedNodeListKey(type, name), nullptr);
128         if (!result.isNewEntry)
129             return static_cast<T*>(result.iterator->value);
130
131         RefPtr<T> list = T::create(node, name);
132         result.iterator->value = list.get();
133         return list.release();
134     }
135
136     PassRefPtr<TagNodeList> addCacheWithQualifiedName(ContainerNode& node, const AtomicString& namespaceURI, const AtomicString& localName)
137     {
138         QualifiedName name(nullAtom, localName, namespaceURI);
139         TagNodeListCacheNS::AddResult result = m_tagNodeListCacheNS.add(name, nullptr);
140         if (!result.isNewEntry)
141             return result.iterator->value;
142
143         RefPtr<TagNodeList> list = TagNodeList::create(node, namespaceURI, localName);
144         result.iterator->value = list.get();
145         return list.release();
146     }
147
148     template<typename T, typename ContainerType>
149     PassRefPtr<T> addCachedCollection(ContainerType& container, CollectionType collectionType, const AtomicString& name)
150     {
151         CollectionCacheMap::AddResult result = m_cachedCollections.add(namedCollectionKey(collectionType, name), nullptr);
152         if (!result.isNewEntry)
153             return static_cast<T*>(result.iterator->value);
154
155         RefPtr<T> list = T::create(container, collectionType, name);
156         result.iterator->value = list.get();
157         return list.release();
158     }
159
160     template<typename T, typename ContainerType>
161     PassRefPtr<T> addCachedCollection(ContainerType& container, CollectionType collectionType)
162     {
163         CollectionCacheMap::AddResult result = m_cachedCollections.add(namedCollectionKey(collectionType, starAtom), nullptr);
164         if (!result.isNewEntry)
165             return static_cast<T*>(result.iterator->value);
166
167         RefPtr<T> list = T::create(container, collectionType);
168         result.iterator->value = list.get();
169         return list.release();
170     }
171
172     template<typename T>
173     T* cachedCollection(CollectionType collectionType)
174     {
175         return static_cast<T*>(m_cachedCollections.get(namedCollectionKey(collectionType, starAtom)));
176     }
177
178     void removeCacheWithAtomicName(LiveNodeList* list, const AtomicString& name = starAtom)
179     {
180         ASSERT(list == m_atomicNameCaches.get(namedNodeListKey(list->type(), name)));
181         if (deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(list->ownerNode()))
182             return;
183         m_atomicNameCaches.remove(namedNodeListKey(list->type(), name));
184     }
185
186     void removeCacheWithName(LiveNodeList* list, const String& name)
187     {
188         ASSERT(list == m_nameCaches.get(namedNodeListKey(list->type(), name)));
189         if (deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(list->ownerNode()))
190             return;
191         m_nameCaches.remove(namedNodeListKey(list->type(), name));
192     }
193
194     void removeCacheWithQualifiedName(LiveNodeList* list, const AtomicString& namespaceURI, const AtomicString& localName)
195     {
196         QualifiedName name(nullAtom, localName, namespaceURI);
197         ASSERT(list == m_tagNodeListCacheNS.get(name));
198         if (deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(list->ownerNode()))
199             return;
200         m_tagNodeListCacheNS.remove(name);
201     }
202
203     void removeCachedCollection(HTMLCollection* collection, const AtomicString& name = starAtom)
204     {
205         ASSERT(collection == m_cachedCollections.get(namedCollectionKey(collection->type(), name)));
206         if (deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(collection->ownerNode()))
207             return;
208         m_cachedCollections.remove(namedCollectionKey(collection->type(), name));
209     }
210
211     static PassOwnPtr<NodeListsNodeData> create()
212     {
213         return adoptPtr(new NodeListsNodeData);
214     }
215
216     void invalidateCaches(const QualifiedName* attrName = 0);
217     bool isEmpty() const
218     {
219         return m_atomicNameCaches.isEmpty() && m_nameCaches.isEmpty() && m_cachedCollections.isEmpty() && m_tagNodeListCacheNS.isEmpty();
220     }
221
222     void adoptTreeScope()
223     {
224         invalidateCaches();
225     }
226
227     void adoptDocument(Document* oldDocument, Document* newDocument)
228     {
229         ASSERT(oldDocument);
230         if (oldDocument == newDocument) {
231             invalidateCaches();
232             return;
233         }
234
235         for (auto& cache : m_atomicNameCaches.values())
236             cache->invalidateCache(*oldDocument);
237
238         for (auto& cache : m_nameCaches.values())
239             cache->invalidateCache(*oldDocument);
240
241         for (auto& list : m_tagNodeListCacheNS.values()) {
242             ASSERT(!list->isRootedAtDocument());
243             list->invalidateCache(*oldDocument);
244         }
245
246         for (auto& collection : m_cachedCollections.values())
247             collection->invalidateCache(*oldDocument);
248     }
249
250 private:
251     NodeListsNodeData()
252         : m_childNodeList(nullptr)
253         , m_emptyChildNodeList(nullptr)
254     {
255     }
256
257     std::pair<unsigned char, AtomicString> namedCollectionKey(CollectionType type, const AtomicString& name)
258     {
259         return std::pair<unsigned char, AtomicString>(type, name);
260     }
261
262     std::pair<unsigned char, String> namedNodeListKey(LiveNodeList::Type type, const String& name)
263     {
264         return std::pair<unsigned char, String>(static_cast<unsigned char>(type), name);
265     }
266
267     bool deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(Node&);
268
269     // These two are currently mutually exclusive and could be unioned. Not very important as this class is large anyway.
270     ChildNodeList* m_childNodeList;
271     EmptyNodeList* m_emptyChildNodeList;
272
273     NodeListAtomicNameCacheMap m_atomicNameCaches;
274     NodeListNameCacheMap m_nameCaches;
275     TagNodeListCacheNS m_tagNodeListCacheNS;
276     CollectionCacheMap m_cachedCollections;
277 };
278
279 class NodeMutationObserverData {
280     WTF_MAKE_NONCOPYABLE(NodeMutationObserverData); WTF_MAKE_FAST_ALLOCATED;
281 public:
282     Vector<OwnPtr<MutationObserverRegistration>> registry;
283     HashSet<MutationObserverRegistration*> transientRegistry;
284
285     static PassOwnPtr<NodeMutationObserverData> create() { return adoptPtr(new NodeMutationObserverData); }
286
287 private:
288     NodeMutationObserverData() { }
289 };
290
291 class NodeRareData : public NodeRareDataBase {
292     WTF_MAKE_NONCOPYABLE(NodeRareData); WTF_MAKE_FAST_ALLOCATED;
293 public:
294     static PassOwnPtr<NodeRareData> create(RenderObject* renderer) { return adoptPtr(new NodeRareData(renderer)); }
295
296     void clearNodeLists() { m_nodeLists.clear(); }
297     NodeListsNodeData* nodeLists() const { return m_nodeLists.get(); }
298     NodeListsNodeData& ensureNodeLists()
299     {
300         if (!m_nodeLists)
301             m_nodeLists = NodeListsNodeData::create();
302         return *m_nodeLists;
303     }
304
305     NodeMutationObserverData* mutationObserverData() { return m_mutationObserverData.get(); }
306     NodeMutationObserverData& ensureMutationObserverData()
307     {
308         if (!m_mutationObserverData)
309             m_mutationObserverData = NodeMutationObserverData::create();
310         return *m_mutationObserverData;
311     }
312
313     unsigned connectedSubframeCount() const { return m_connectedFrameCount; }
314     void incrementConnectedSubframeCount(unsigned amount)
315     {
316         m_connectedFrameCount += amount;
317     }
318     void decrementConnectedSubframeCount(unsigned amount)
319     {
320         ASSERT(m_connectedFrameCount);
321         ASSERT(amount <= m_connectedFrameCount);
322         m_connectedFrameCount -= amount;
323     }
324
325 protected:
326     NodeRareData(RenderObject* renderer)
327         : NodeRareDataBase(renderer)
328         , m_connectedFrameCount(0)
329     { }
330
331 private:
332     unsigned m_connectedFrameCount : 10; // Must fit Page::maxNumberOfFrames.
333
334     OwnPtr<NodeListsNodeData> m_nodeLists;
335     OwnPtr<NodeMutationObserverData> m_mutationObserverData;
336 };
337
338 inline bool NodeListsNodeData::deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(Node& ownerNode)
339 {
340     ASSERT(ownerNode.nodeLists() == this);
341     if ((m_childNodeList ? 1 : 0) + (m_emptyChildNodeList ? 1 : 0) + m_atomicNameCaches.size() + m_nameCaches.size()
342         + m_tagNodeListCacheNS.size() + m_cachedCollections.size() != 1)
343         return false;
344     ownerNode.clearNodeLists();
345     return true;
346 }
347
348 // Ensure the 10 bits reserved for the m_connectedFrameCount cannot overflow
349 COMPILE_ASSERT(Page::maxNumberOfFrames < 1024, Frame_limit_should_fit_in_rare_data_count);
350
351 } // namespace WebCore
352
353 #endif // NodeRareData_h