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