6803fb0aeb552455eece34e8df8db1ecb4971f1b
[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 #pragma once
23
24 #include "ChildNodeList.h"
25 #include "HTMLCollection.h"
26 #include "HTMLNames.h"
27 #include "LiveNodeList.h"
28 #include "MutationObserverRegistration.h"
29 #include "Page.h"
30 #include "QualifiedName.h"
31 #include "TagCollection.h"
32 #include <wtf/HashSet.h>
33 #include <wtf/text/AtomicString.h>
34
35 namespace WebCore {
36
37 class LabelsNodeList;
38 class NameNodeList;
39 class RadioNodeList;
40 class TreeScope;
41
42 template <class ListType> struct NodeListTypeIdentifier;
43 template <> struct NodeListTypeIdentifier<NameNodeList> { static int value() { return 0; } };
44 template <> struct NodeListTypeIdentifier<RadioNodeList> { static int value() { return 1; } };
45 template <> struct NodeListTypeIdentifier<LabelsNodeList> { static int value() { return 2; } };
46
47 class NodeListsNodeData {
48     WTF_MAKE_NONCOPYABLE(NodeListsNodeData); WTF_MAKE_FAST_ALLOCATED;
49 public:
50     NodeListsNodeData()
51         : m_childNodeList(nullptr)
52         , m_emptyChildNodeList(nullptr)
53     {
54     }
55
56     void clearChildNodeListCache()
57     {
58         if (m_childNodeList)
59             m_childNodeList->invalidateCache();
60     }
61
62     Ref<ChildNodeList> ensureChildNodeList(ContainerNode& node)
63     {
64         ASSERT(!m_emptyChildNodeList);
65         if (m_childNodeList)
66             return *m_childNodeList;
67         auto list = ChildNodeList::create(node);
68         m_childNodeList = list.ptr();
69         return list;
70     }
71
72     void removeChildNodeList(ChildNodeList* list)
73     {
74         ASSERT(m_childNodeList == list);
75         if (deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(list->ownerNode()))
76             return;
77         m_childNodeList = nullptr;
78     }
79
80     Ref<EmptyNodeList> ensureEmptyChildNodeList(Node& node)
81     {
82         ASSERT(!m_childNodeList);
83         if (m_emptyChildNodeList)
84             return *m_emptyChildNodeList;
85         auto list = EmptyNodeList::create(node);
86         m_emptyChildNodeList = list.ptr();
87         return list;
88     }
89
90     void removeEmptyChildNodeList(EmptyNodeList* list)
91     {
92         ASSERT(m_emptyChildNodeList == list);
93         if (deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(list->ownerNode()))
94             return;
95         m_emptyChildNodeList = nullptr;
96     }
97
98     struct NodeListCacheMapEntryHash {
99         static unsigned hash(const std::pair<unsigned char, AtomicString>& entry)
100         {
101             return DefaultHash<AtomicString>::Hash::hash(entry.second) + entry.first;
102         }
103         static bool equal(const std::pair<unsigned char, AtomicString>& a, const std::pair<unsigned char, AtomicString>& b) { return a.first == b.first && DefaultHash<AtomicString>::Hash::equal(a.second, b.second); }
104         static const bool safeToCompareToEmptyOrDeleted = DefaultHash<AtomicString>::Hash::safeToCompareToEmptyOrDeleted;
105     };
106
107     typedef HashMap<std::pair<unsigned char, AtomicString>, LiveNodeList*, NodeListCacheMapEntryHash> NodeListAtomicNameCacheMap;
108     typedef HashMap<std::pair<unsigned char, AtomicString>, HTMLCollection*, NodeListCacheMapEntryHash> CollectionCacheMap;
109     typedef HashMap<QualifiedName, TagCollectionNS*> TagCollectionNSCache;
110
111     template<typename T, typename ContainerType>
112     ALWAYS_INLINE Ref<T> addCacheWithAtomicName(ContainerType& container, const AtomicString& name)
113     {
114         NodeListAtomicNameCacheMap::AddResult result = m_atomicNameCaches.fastAdd(namedNodeListKey<T>(name), nullptr);
115         if (!result.isNewEntry)
116             return static_cast<T&>(*result.iterator->value);
117
118         auto list = T::create(container, name);
119         result.iterator->value = &list.get();
120         return list;
121     }
122
123     ALWAYS_INLINE Ref<TagCollectionNS> addCachedTagCollectionNS(ContainerNode& node, const AtomicString& namespaceURI, const AtomicString& localName)
124     {
125         QualifiedName name(nullAtom, localName, namespaceURI);
126         TagCollectionNSCache::AddResult result = m_tagCollectionNSCache.fastAdd(name, nullptr);
127         if (!result.isNewEntry)
128             return *result.iterator->value;
129
130         auto list = TagCollectionNS::create(node, namespaceURI, localName);
131         result.iterator->value = list.ptr();
132         return list;
133     }
134
135     template<typename T, typename ContainerType>
136     ALWAYS_INLINE Ref<T> addCachedCollection(ContainerType& container, CollectionType collectionType, const AtomicString& name)
137     {
138         CollectionCacheMap::AddResult result = m_cachedCollections.fastAdd(namedCollectionKey(collectionType, name), nullptr);
139         if (!result.isNewEntry)
140             return static_cast<T&>(*result.iterator->value);
141
142         auto list = T::create(container, collectionType, name);
143         result.iterator->value = &list.get();
144         return list;
145     }
146
147     template<typename T, typename ContainerType>
148     ALWAYS_INLINE Ref<T> addCachedCollection(ContainerType& container, CollectionType collectionType)
149     {
150         CollectionCacheMap::AddResult result = m_cachedCollections.fastAdd(namedCollectionKey(collectionType, starAtom), nullptr);
151         if (!result.isNewEntry)
152             return static_cast<T&>(*result.iterator->value);
153
154         auto list = T::create(container, collectionType);
155         result.iterator->value = &list.get();
156         return list;
157     }
158
159     template<typename T>
160     T* cachedCollection(CollectionType collectionType)
161     {
162         return static_cast<T*>(m_cachedCollections.get(namedCollectionKey(collectionType, starAtom)));
163     }
164
165     template <class NodeListType>
166     void removeCacheWithAtomicName(NodeListType* list, const AtomicString& name = starAtom)
167     {
168         ASSERT(list == m_atomicNameCaches.get(namedNodeListKey<NodeListType>(name)));
169         if (deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(list->ownerNode()))
170             return;
171         m_atomicNameCaches.remove(namedNodeListKey<NodeListType>(name));
172     }
173
174     void removeCachedTagCollectionNS(HTMLCollection& collection, const AtomicString& namespaceURI, const AtomicString& localName)
175     {
176         QualifiedName name(nullAtom, localName, namespaceURI);
177         ASSERT(&collection == m_tagCollectionNSCache.get(name));
178         if (deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(collection.ownerNode()))
179             return;
180         m_tagCollectionNSCache.remove(name);
181     }
182
183     void removeCachedCollection(HTMLCollection* collection, const AtomicString& name = starAtom)
184     {
185         ASSERT(collection == m_cachedCollections.get(namedCollectionKey(collection->type(), name)));
186         if (deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(collection->ownerNode()))
187             return;
188         m_cachedCollections.remove(namedCollectionKey(collection->type(), name));
189     }
190
191     void invalidateCaches();
192     void invalidateCachesForAttribute(const QualifiedName& attrName);
193
194     void adoptTreeScope()
195     {
196         invalidateCaches();
197     }
198
199     void adoptDocument(Document& oldDocument, Document& newDocument)
200     {
201         if (&oldDocument == &newDocument) {
202             invalidateCaches();
203             return;
204         }
205
206         for (auto& cache : m_atomicNameCaches.values())
207             cache->invalidateCacheForDocument(oldDocument);
208
209         for (auto& list : m_tagCollectionNSCache.values()) {
210             ASSERT(!list->isRootedAtDocument());
211             list->invalidateCacheForDocument(oldDocument);
212         }
213
214         for (auto& collection : m_cachedCollections.values())
215             collection->invalidateCacheForDocument(oldDocument);
216     }
217
218 private:
219     std::pair<unsigned char, AtomicString> namedCollectionKey(CollectionType type, const AtomicString& name)
220     {
221         return std::pair<unsigned char, AtomicString>(type, name);
222     }
223
224     template <class NodeListType>
225     std::pair<unsigned char, AtomicString> namedNodeListKey(const AtomicString& name)
226     {
227         return std::pair<unsigned char, AtomicString>(NodeListTypeIdentifier<NodeListType>::value(), name);
228     }
229
230     bool deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(Node&);
231
232     // These two are currently mutually exclusive and could be unioned. Not very important as this class is large anyway.
233     ChildNodeList* m_childNodeList;
234     EmptyNodeList* m_emptyChildNodeList;
235
236     NodeListAtomicNameCacheMap m_atomicNameCaches;
237     TagCollectionNSCache m_tagCollectionNSCache;
238     CollectionCacheMap m_cachedCollections;
239 };
240
241 class NodeMutationObserverData {
242     WTF_MAKE_NONCOPYABLE(NodeMutationObserverData); WTF_MAKE_FAST_ALLOCATED;
243 public:
244     Vector<std::unique_ptr<MutationObserverRegistration>> registry;
245     HashSet<MutationObserverRegistration*> transientRegistry;
246
247     NodeMutationObserverData() { }
248 };
249
250 class NodeRareData : public NodeRareDataBase {
251     WTF_MAKE_NONCOPYABLE(NodeRareData); WTF_MAKE_FAST_ALLOCATED;
252 public:
253     NodeRareData(RenderObject* renderer)
254         : NodeRareDataBase(renderer)
255         , m_connectedFrameCount(0)
256     { }
257
258     void clearNodeLists() { m_nodeLists = nullptr; }
259     NodeListsNodeData* nodeLists() const { return m_nodeLists.get(); }
260     NodeListsNodeData& ensureNodeLists()
261     {
262         if (!m_nodeLists)
263             m_nodeLists = std::make_unique<NodeListsNodeData>();
264         return *m_nodeLists;
265     }
266
267     NodeMutationObserverData* mutationObserverData() { return m_mutationObserverData.get(); }
268     NodeMutationObserverData& ensureMutationObserverData()
269     {
270         if (!m_mutationObserverData)
271             m_mutationObserverData = std::make_unique<NodeMutationObserverData>();
272         return *m_mutationObserverData;
273     }
274
275     unsigned connectedSubframeCount() const { return m_connectedFrameCount; }
276     void incrementConnectedSubframeCount(unsigned amount)
277     {
278         m_connectedFrameCount += amount;
279     }
280     void decrementConnectedSubframeCount(unsigned amount)
281     {
282         ASSERT(m_connectedFrameCount);
283         ASSERT(amount <= m_connectedFrameCount);
284         m_connectedFrameCount -= amount;
285     }
286
287 private:
288     unsigned m_connectedFrameCount : 10; // Must fit Page::maxNumberOfFrames.
289
290     std::unique_ptr<NodeListsNodeData> m_nodeLists;
291     std::unique_ptr<NodeMutationObserverData> m_mutationObserverData;
292 };
293
294 inline bool NodeListsNodeData::deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(Node& ownerNode)
295 {
296     ASSERT(ownerNode.nodeLists() == this);
297     if ((m_childNodeList ? 1 : 0) + (m_emptyChildNodeList ? 1 : 0) + m_atomicNameCaches.size()
298         + m_tagCollectionNSCache.size() + m_cachedCollections.size() != 1)
299         return false;
300     ownerNode.clearNodeLists();
301     return true;
302 }
303
304 inline NodeRareData* Node::rareData() const
305 {
306     ASSERT_WITH_SECURITY_IMPLICATION(hasRareData());
307     return static_cast<NodeRareData*>(m_data.m_rareData);
308 }
309
310 inline NodeRareData& Node::ensureRareData()
311 {
312     if (!hasRareData())
313         materializeRareData();
314     return *rareData();
315 }
316
317 // Ensure the 10 bits reserved for the m_connectedFrameCount cannot overflow
318 static_assert(Page::maxNumberOfFrames < 1024, "Frame limit should fit in rare data count");
319
320 } // namespace WebCore