• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "core/dom/ChildNodeList.h"
26 #include "core/dom/EmptyNodeList.h"
27 #include "core/dom/LiveNodeList.h"
28 #include "core/dom/MutationObserverRegistration.h"
29 #include "core/dom/QualifiedName.h"
30 #include "core/dom/TagCollection.h"
31 #include "core/page/Page.h"
32 #include "platform/heap/Handle.h"
33 #include "wtf/HashSet.h"
34 #include "wtf/OwnPtr.h"
35 #include "wtf/PassOwnPtr.h"
36 #include "wtf/text/AtomicString.h"
37 #include "wtf/text/StringHash.h"
38 
39 namespace WebCore {
40 
41 class LabelsNodeList;
42 class RadioNodeList;
43 class TreeScope;
44 
45 class NodeListsNodeData FINAL : public NoBaseWillBeGarbageCollectedFinalized<NodeListsNodeData> {
46     WTF_MAKE_NONCOPYABLE(NodeListsNodeData);
47     WTF_MAKE_FAST_ALLOCATED_WILL_BE_REMOVED;
48 public:
clearChildNodeListCache()49     void clearChildNodeListCache()
50     {
51         if (m_childNodeList && m_childNodeList->isChildNodeList())
52             toChildNodeList(m_childNodeList)->invalidateCache();
53     }
54 
ensureChildNodeList(ContainerNode & node)55     PassRefPtrWillBeRawPtr<ChildNodeList> ensureChildNodeList(ContainerNode& node)
56     {
57         if (m_childNodeList)
58             return toChildNodeList(m_childNodeList);
59         RefPtrWillBeRawPtr<ChildNodeList> list = ChildNodeList::create(node);
60         m_childNodeList = list.get();
61         return list.release();
62     }
63 
ensureEmptyChildNodeList(Node & node)64     PassRefPtrWillBeRawPtr<EmptyNodeList> ensureEmptyChildNodeList(Node& node)
65     {
66         if (m_childNodeList)
67             return toEmptyNodeList(m_childNodeList);
68         RefPtrWillBeRawPtr<EmptyNodeList> list = EmptyNodeList::create(node);
69         m_childNodeList = list.get();
70         return list.release();
71     }
72 
73 #if !ENABLE(OILPAN)
removeChildNodeList(ChildNodeList * list)74     void removeChildNodeList(ChildNodeList* list)
75     {
76         ASSERT(m_childNodeList == list);
77         if (deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(list->ownerNode()))
78             return;
79         m_childNodeList = nullptr;
80     }
81 
removeEmptyChildNodeList(EmptyNodeList * list)82     void removeEmptyChildNodeList(EmptyNodeList* list)
83     {
84         ASSERT(m_childNodeList == list);
85         if (deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(list->ownerNode()))
86             return;
87         m_childNodeList = nullptr;
88     }
89 #endif
90 
91     struct NodeListAtomicCacheMapEntryHash {
hashNodeListAtomicCacheMapEntryHash92         static unsigned hash(const std::pair<unsigned char, StringImpl*>& entry)
93         {
94             return DefaultHash<StringImpl*>::Hash::hash(entry.second) + entry.first;
95         }
equalNodeListAtomicCacheMapEntryHash96         static bool equal(const std::pair<unsigned char, StringImpl*>& a, const std::pair<unsigned char, StringImpl*>& b) { return a == b; }
97         static const bool safeToCompareToEmptyOrDeleted = DefaultHash<StringImpl*>::Hash::safeToCompareToEmptyOrDeleted;
98     };
99 
100     // Oilpan: keep a weak reference to the collection objects.
101     // Explicit object unregistration in a non-Oilpan setting
102     // on object destruction is replaced by the garbage collector
103     // clearing out their weak reference.
104     typedef WillBeHeapHashMap<std::pair<unsigned char, StringImpl*>, RawPtrWillBeWeakMember<LiveNodeListBase>, NodeListAtomicCacheMapEntryHash> NodeListAtomicNameCacheMap;
105     typedef WillBeHeapHashMap<QualifiedName, RawPtrWillBeWeakMember<TagCollection> > TagCollectionCacheNS;
106 
107     template<typename T>
addCache(ContainerNode & node,CollectionType collectionType,const AtomicString & name)108     PassRefPtrWillBeRawPtr<T> addCache(ContainerNode& node, CollectionType collectionType, const AtomicString& name)
109     {
110         NodeListAtomicNameCacheMap::AddResult result = m_atomicNameCaches.add(namedNodeListKey(collectionType, name), nullptr);
111         if (!result.isNewEntry) {
112 #if ENABLE(OILPAN)
113             return static_cast<T*>(result.storedValue->value.get());
114 #else
115             return static_cast<T*>(result.storedValue->value);
116 #endif
117         }
118 
119         RefPtrWillBeRawPtr<T> list = T::create(node, collectionType, name);
120         result.storedValue->value = list.get();
121         return list.release();
122     }
123 
124     template<typename T>
addCache(ContainerNode & node,CollectionType collectionType)125     PassRefPtrWillBeRawPtr<T> addCache(ContainerNode& node, CollectionType collectionType)
126     {
127         NodeListAtomicNameCacheMap::AddResult result = m_atomicNameCaches.add(namedNodeListKey(collectionType, starAtom), nullptr);
128         if (!result.isNewEntry) {
129 #if ENABLE(OILPAN)
130             return static_cast<T*>(result.storedValue->value.get());
131 #else
132             return static_cast<T*>(result.storedValue->value);
133 #endif
134         }
135 
136         RefPtrWillBeRawPtr<T> list = T::create(node, collectionType);
137         result.storedValue->value = list.get();
138         return list.release();
139     }
140 
141     template<typename T>
cached(CollectionType collectionType)142     T* cached(CollectionType collectionType)
143     {
144         return static_cast<T*>(m_atomicNameCaches.get(namedNodeListKey(collectionType, starAtom)));
145     }
146 
addCache(ContainerNode & node,const AtomicString & namespaceURI,const AtomicString & localName)147     PassRefPtrWillBeRawPtr<TagCollection> addCache(ContainerNode& node, const AtomicString& namespaceURI, const AtomicString& localName)
148     {
149         QualifiedName name(nullAtom, localName, namespaceURI);
150         TagCollectionCacheNS::AddResult result = m_tagCollectionCacheNS.add(name, nullptr);
151         if (!result.isNewEntry)
152             return result.storedValue->value;
153 
154         RefPtrWillBeRawPtr<TagCollection> list = TagCollection::create(node, namespaceURI, localName);
155         result.storedValue->value = list.get();
156         return list.release();
157     }
158 
159 #if !ENABLE(OILPAN)
160     void removeCache(LiveNodeListBase* list, CollectionType collectionType, const AtomicString& name = starAtom)
161     {
162         ASSERT(list == m_atomicNameCaches.get(namedNodeListKey(collectionType, name)));
163         if (deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(list->ownerNode()))
164             return;
165         m_atomicNameCaches.remove(namedNodeListKey(collectionType, name));
166     }
167 
removeCache(LiveNodeListBase * list,const AtomicString & namespaceURI,const AtomicString & localName)168     void removeCache(LiveNodeListBase* list, const AtomicString& namespaceURI, const AtomicString& localName)
169     {
170         QualifiedName name(nullAtom, localName, namespaceURI);
171         ASSERT(list == m_tagCollectionCacheNS.get(name));
172         if (deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(list->ownerNode()))
173             return;
174         m_tagCollectionCacheNS.remove(name);
175     }
176 #endif
177 
create()178     static PassOwnPtrWillBeRawPtr<NodeListsNodeData> create()
179     {
180         return adoptPtrWillBeNoop(new NodeListsNodeData);
181     }
182 
183     void invalidateCaches(const QualifiedName* attrName = 0);
184 
isEmpty()185     bool isEmpty() const
186     {
187         return !m_childNodeList && m_atomicNameCaches.isEmpty() && m_tagCollectionCacheNS.isEmpty();
188     }
189 
adoptTreeScope()190     void adoptTreeScope()
191     {
192         invalidateCaches();
193     }
194 
adoptDocument(Document & oldDocument,Document & newDocument)195     void adoptDocument(Document& oldDocument, Document& newDocument)
196     {
197         ASSERT(oldDocument != newDocument);
198 
199         NodeListAtomicNameCacheMap::const_iterator atomicNameCacheEnd = m_atomicNameCaches.end();
200         for (NodeListAtomicNameCacheMap::const_iterator it = m_atomicNameCaches.begin(); it != atomicNameCacheEnd; ++it) {
201             LiveNodeListBase* list = it->value;
202             list->didMoveToDocument(oldDocument, newDocument);
203         }
204 
205         TagCollectionCacheNS::const_iterator tagEnd = m_tagCollectionCacheNS.end();
206         for (TagCollectionCacheNS::const_iterator it = m_tagCollectionCacheNS.begin(); it != tagEnd; ++it) {
207             LiveNodeListBase* list = it->value;
208             ASSERT(!list->isRootedAtDocument());
209             list->didMoveToDocument(oldDocument, newDocument);
210         }
211     }
212 
213     void trace(Visitor*);
214 
215 private:
NodeListsNodeData()216     NodeListsNodeData()
217         : m_childNodeList(nullptr)
218     { }
219 
namedNodeListKey(CollectionType type,const AtomicString & name)220     std::pair<unsigned char, StringImpl*> namedNodeListKey(CollectionType type, const AtomicString& name)
221     {
222         // Holding the raw StringImpl is safe because |name| is retained by the NodeList and the NodeList
223         // is reponsible for removing itself from the cache on deletion.
224         return std::pair<unsigned char, StringImpl*>(type, name.impl());
225     }
226 
227 #if !ENABLE(OILPAN)
228     bool deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(Node&);
229 #endif
230 
231     // Can be a ChildNodeList or an EmptyNodeList.
232     RawPtrWillBeWeakMember<NodeList> m_childNodeList;
233     NodeListAtomicNameCacheMap m_atomicNameCaches;
234     TagCollectionCacheNS m_tagCollectionCacheNS;
235 };
236 
237 class NodeMutationObserverData FINAL : public NoBaseWillBeGarbageCollected<NodeMutationObserverData> {
238     WTF_MAKE_NONCOPYABLE(NodeMutationObserverData);
239     WTF_MAKE_FAST_ALLOCATED_WILL_BE_REMOVED;
240 public:
241     WillBeHeapVector<OwnPtrWillBeMember<MutationObserverRegistration> > registry;
242     WillBeHeapHashSet<RawPtrWillBeMember<MutationObserverRegistration> > transientRegistry;
243 
create()244     static PassOwnPtrWillBeRawPtr<NodeMutationObserverData> create()
245     {
246         return adoptPtrWillBeNoop(new NodeMutationObserverData);
247     }
248 
trace(Visitor * visitor)249     void trace(Visitor* visitor)
250     {
251         visitor->trace(registry);
252         visitor->trace(transientRegistry);
253     }
254 
255 private:
NodeMutationObserverData()256     NodeMutationObserverData() { }
257 };
258 
259 class NodeRareData : public NoBaseWillBeGarbageCollectedFinalized<NodeRareData>, public NodeRareDataBase {
260     WTF_MAKE_NONCOPYABLE(NodeRareData);
261     WTF_MAKE_FAST_ALLOCATED_WILL_BE_REMOVED;
262 public:
create(RenderObject * renderer)263     static NodeRareData* create(RenderObject* renderer)
264     {
265         return new NodeRareData(renderer);
266     }
267 
clearNodeLists()268     void clearNodeLists() { m_nodeLists.clear(); }
nodeLists()269     NodeListsNodeData* nodeLists() const { return m_nodeLists.get(); }
ensureNodeLists()270     NodeListsNodeData& ensureNodeLists()
271     {
272         if (!m_nodeLists)
273             m_nodeLists = NodeListsNodeData::create();
274         return *m_nodeLists;
275     }
276 
mutationObserverData()277     NodeMutationObserverData* mutationObserverData() { return m_mutationObserverData.get(); }
ensureMutationObserverData()278     NodeMutationObserverData& ensureMutationObserverData()
279     {
280         if (!m_mutationObserverData)
281             m_mutationObserverData = NodeMutationObserverData::create();
282         return *m_mutationObserverData;
283     }
284 
connectedSubframeCount()285     unsigned connectedSubframeCount() const { return m_connectedFrameCount; }
incrementConnectedSubframeCount(unsigned amount)286     void incrementConnectedSubframeCount(unsigned amount)
287     {
288         m_connectedFrameCount += amount;
289     }
decrementConnectedSubframeCount(unsigned amount)290     void decrementConnectedSubframeCount(unsigned amount)
291     {
292         ASSERT(m_connectedFrameCount);
293         ASSERT(amount <= m_connectedFrameCount);
294         m_connectedFrameCount -= amount;
295     }
296 
hasElementFlag(ElementFlags mask)297     bool hasElementFlag(ElementFlags mask) const { return m_elementFlags & mask; }
setElementFlag(ElementFlags mask,bool value)298     void setElementFlag(ElementFlags mask, bool value) { m_elementFlags = (m_elementFlags & ~mask) | (-(int32_t)value & mask); }
clearElementFlag(ElementFlags mask)299     void clearElementFlag(ElementFlags mask) { m_elementFlags &= ~mask; }
300 
hasRestyleFlag(DynamicRestyleFlags mask)301     bool hasRestyleFlag(DynamicRestyleFlags mask) const { return m_restyleFlags & mask; }
setRestyleFlag(DynamicRestyleFlags mask)302     void setRestyleFlag(DynamicRestyleFlags mask) { m_restyleFlags |= mask; RELEASE_ASSERT(m_restyleFlags); }
hasRestyleFlags()303     bool hasRestyleFlags() const { return m_restyleFlags; }
clearRestyleFlags()304     void clearRestyleFlags() { m_restyleFlags = 0; }
305 
306     enum {
307         ConnectedFrameCountBits = 10, // Must fit Page::maxNumberOfFrames.
308     };
309 
310     void trace(Visitor*);
311 
312     void traceAfterDispatch(Visitor*);
313     void finalizeGarbageCollectedObject();
314 
315 protected:
NodeRareData(RenderObject * renderer)316     explicit NodeRareData(RenderObject* renderer)
317         : NodeRareDataBase(renderer)
318         , m_connectedFrameCount(0)
319         , m_elementFlags(0)
320         , m_restyleFlags(0)
321         , m_isElementRareData(false)
322     { }
323 
324 private:
325     OwnPtrWillBeMember<NodeListsNodeData> m_nodeLists;
326     OwnPtrWillBeMember<NodeMutationObserverData> m_mutationObserverData;
327 
328     unsigned m_connectedFrameCount : ConnectedFrameCountBits;
329     unsigned m_elementFlags : NumberOfElementFlags;
330     unsigned m_restyleFlags : NumberOfDynamicRestyleFlags;
331 protected:
332     unsigned m_isElementRareData : 1;
333 };
334 
335 #if !ENABLE(OILPAN)
deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(Node & ownerNode)336 inline bool NodeListsNodeData::deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(Node& ownerNode)
337 {
338     ASSERT(ownerNode.nodeLists() == this);
339     if ((m_childNodeList ? 1 : 0) + m_atomicNameCaches.size() + m_tagCollectionCacheNS.size() != 1)
340         return false;
341     ownerNode.clearNodeLists();
342     return true;
343 }
344 #endif
345 
346 } // namespace WebCore
347 
348 #endif // NodeRareData_h
349