1 /* 2 * Copyright (C) 2008, 2010 Apple Inc. All rights reserved. 3 * Copyright (C) 2008 David Smith <catfish.man (at) 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: 49 void clearChildNodeListCache() 50 { 51 if (m_childNodeList && m_childNodeList->isChildNodeList()) 52 toChildNodeList(m_childNodeList)->invalidateCache(); 53 } 54 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 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) 74 void removeChildNodeList(ChildNodeList* list) 75 { 76 ASSERT(m_childNodeList == list); 77 if (deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(list->ownerNode())) 78 return; 79 m_childNodeList = nullptr; 80 } 81 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 { 92 static unsigned hash(const std::pair<unsigned char, StringImpl*>& entry) 93 { 94 return DefaultHash<StringImpl*>::Hash::hash(entry.second) + entry.first; 95 } 96 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> 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> 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> 142 T* cached(CollectionType collectionType) 143 { 144 return static_cast<T*>(m_atomicNameCaches.get(namedNodeListKey(collectionType, starAtom))); 145 } 146 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 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 178 static PassOwnPtrWillBeRawPtr<NodeListsNodeData> create() 179 { 180 return adoptPtrWillBeNoop(new NodeListsNodeData); 181 } 182 183 void invalidateCaches(const QualifiedName* attrName = 0); 184 185 bool isEmpty() const 186 { 187 return !m_childNodeList && m_atomicNameCaches.isEmpty() && m_tagCollectionCacheNS.isEmpty(); 188 } 189 190 void adoptTreeScope() 191 { 192 invalidateCaches(); 193 } 194 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: 216 NodeListsNodeData() 217 : m_childNodeList(nullptr) 218 { } 219 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 244 static PassOwnPtrWillBeRawPtr<NodeMutationObserverData> create() 245 { 246 return adoptPtrWillBeNoop(new NodeMutationObserverData); 247 } 248 249 void trace(Visitor* visitor) 250 { 251 visitor->trace(registry); 252 visitor->trace(transientRegistry); 253 } 254 255 private: 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: 263 static NodeRareData* create(RenderObject* renderer) 264 { 265 return new NodeRareData(renderer); 266 } 267 268 void clearNodeLists() { m_nodeLists.clear(); } 269 NodeListsNodeData* nodeLists() const { return m_nodeLists.get(); } 270 NodeListsNodeData& ensureNodeLists() 271 { 272 if (!m_nodeLists) 273 m_nodeLists = NodeListsNodeData::create(); 274 return *m_nodeLists; 275 } 276 277 NodeMutationObserverData* mutationObserverData() { return m_mutationObserverData.get(); } 278 NodeMutationObserverData& ensureMutationObserverData() 279 { 280 if (!m_mutationObserverData) 281 m_mutationObserverData = NodeMutationObserverData::create(); 282 return *m_mutationObserverData; 283 } 284 285 unsigned connectedSubframeCount() const { return m_connectedFrameCount; } 286 void incrementConnectedSubframeCount(unsigned amount) 287 { 288 m_connectedFrameCount += amount; 289 } 290 void decrementConnectedSubframeCount(unsigned amount) 291 { 292 ASSERT(m_connectedFrameCount); 293 ASSERT(amount <= m_connectedFrameCount); 294 m_connectedFrameCount -= amount; 295 } 296 297 bool hasElementFlag(ElementFlags mask) const { return m_elementFlags & mask; } 298 void setElementFlag(ElementFlags mask, bool value) { m_elementFlags = (m_elementFlags & ~mask) | (-(int32_t)value & mask); } 299 void clearElementFlag(ElementFlags mask) { m_elementFlags &= ~mask; } 300 301 bool hasRestyleFlag(DynamicRestyleFlags mask) const { return m_restyleFlags & mask; } 302 void setRestyleFlag(DynamicRestyleFlags mask) { m_restyleFlags |= mask; RELEASE_ASSERT(m_restyleFlags); } 303 bool hasRestyleFlags() const { return m_restyleFlags; } 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: 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) 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