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 NodeListsNodeData_h 23 #define NodeListsNodeData_h 24 25 #include "core/dom/ChildNodeList.h" 26 #include "core/dom/EmptyNodeList.h" 27 #include "core/dom/QualifiedName.h" 28 #include "core/dom/TagCollection.h" 29 #include "core/html/CollectionType.h" 30 #include "platform/heap/Handle.h" 31 #include "wtf/text/AtomicString.h" 32 #include "wtf/text/StringHash.h" 33 34 namespace blink { 35 36 class NodeListsNodeData FINAL : public NoBaseWillBeGarbageCollectedFinalized<NodeListsNodeData> { 37 WTF_MAKE_NONCOPYABLE(NodeListsNodeData); 38 WTF_MAKE_FAST_ALLOCATED_WILL_BE_REMOVED; 39 public: 40 ChildNodeList* childNodeList(ContainerNode& node) 41 { 42 ASSERT_UNUSED(node, !m_childNodeList || node == m_childNodeList->virtualOwnerNode()); 43 return toChildNodeList(m_childNodeList); 44 } 45 46 PassRefPtrWillBeRawPtr<ChildNodeList> ensureChildNodeList(ContainerNode& node) 47 { 48 if (m_childNodeList) 49 return toChildNodeList(m_childNodeList); 50 RefPtrWillBeRawPtr<ChildNodeList> list = ChildNodeList::create(node); 51 m_childNodeList = list.get(); 52 return list.release(); 53 } 54 55 PassRefPtrWillBeRawPtr<EmptyNodeList> ensureEmptyChildNodeList(Node& node) 56 { 57 if (m_childNodeList) 58 return toEmptyNodeList(m_childNodeList); 59 RefPtrWillBeRawPtr<EmptyNodeList> list = EmptyNodeList::create(node); 60 m_childNodeList = list.get(); 61 return list.release(); 62 } 63 64 #if !ENABLE(OILPAN) 65 void removeChildNodeList(ChildNodeList* list) 66 { 67 ASSERT(m_childNodeList == list); 68 if (deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(list->ownerNode())) 69 return; 70 m_childNodeList = nullptr; 71 } 72 73 void removeEmptyChildNodeList(EmptyNodeList* list) 74 { 75 ASSERT(m_childNodeList == list); 76 if (deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(list->ownerNode())) 77 return; 78 m_childNodeList = nullptr; 79 } 80 #endif 81 82 struct NodeListAtomicCacheMapEntryHash { 83 static unsigned hash(const std::pair<unsigned char, StringImpl*>& entry) 84 { 85 return DefaultHash<StringImpl*>::Hash::hash(entry.second) + entry.first; 86 } 87 static bool equal(const std::pair<unsigned char, StringImpl*>& a, const std::pair<unsigned char, StringImpl*>& b) { return a == b; } 88 static const bool safeToCompareToEmptyOrDeleted = DefaultHash<StringImpl*>::Hash::safeToCompareToEmptyOrDeleted; 89 }; 90 91 // Oilpan: keep a weak reference to the collection objects. 92 // Explicit object unregistration in a non-Oilpan setting 93 // on object destruction is replaced by the garbage collector 94 // clearing out their weak reference. 95 typedef WillBeHeapHashMap<std::pair<unsigned char, StringImpl*>, RawPtrWillBeWeakMember<LiveNodeListBase>, NodeListAtomicCacheMapEntryHash> NodeListAtomicNameCacheMap; 96 typedef WillBeHeapHashMap<QualifiedName, RawPtrWillBeWeakMember<TagCollection> > TagCollectionCacheNS; 97 98 template<typename T> 99 PassRefPtrWillBeRawPtr<T> addCache(ContainerNode& node, CollectionType collectionType, const AtomicString& name) 100 { 101 NodeListAtomicNameCacheMap::AddResult result = m_atomicNameCaches.add(namedNodeListKey(collectionType, name), nullptr); 102 if (!result.isNewEntry) { 103 #if ENABLE(OILPAN) 104 return static_cast<T*>(result.storedValue->value.get()); 105 #else 106 return static_cast<T*>(result.storedValue->value); 107 #endif 108 } 109 110 RefPtrWillBeRawPtr<T> list = T::create(node, collectionType, name); 111 result.storedValue->value = list.get(); 112 return list.release(); 113 } 114 115 template<typename T> 116 PassRefPtrWillBeRawPtr<T> addCache(ContainerNode& node, CollectionType collectionType) 117 { 118 NodeListAtomicNameCacheMap::AddResult result = m_atomicNameCaches.add(namedNodeListKey(collectionType, starAtom), nullptr); 119 if (!result.isNewEntry) { 120 #if ENABLE(OILPAN) 121 return static_cast<T*>(result.storedValue->value.get()); 122 #else 123 return static_cast<T*>(result.storedValue->value); 124 #endif 125 } 126 127 RefPtrWillBeRawPtr<T> list = T::create(node, collectionType); 128 result.storedValue->value = list.get(); 129 return list.release(); 130 } 131 132 template<typename T> 133 T* cached(CollectionType collectionType) 134 { 135 return static_cast<T*>(m_atomicNameCaches.get(namedNodeListKey(collectionType, starAtom))); 136 } 137 138 PassRefPtrWillBeRawPtr<TagCollection> addCache(ContainerNode& node, const AtomicString& namespaceURI, const AtomicString& localName) 139 { 140 QualifiedName name(nullAtom, localName, namespaceURI); 141 TagCollectionCacheNS::AddResult result = m_tagCollectionCacheNS.add(name, nullptr); 142 if (!result.isNewEntry) 143 return result.storedValue->value; 144 145 RefPtrWillBeRawPtr<TagCollection> list = TagCollection::create(node, namespaceURI, localName); 146 result.storedValue->value = list.get(); 147 return list.release(); 148 } 149 150 #if !ENABLE(OILPAN) 151 void removeCache(LiveNodeListBase* list, CollectionType collectionType, const AtomicString& name = starAtom) 152 { 153 ASSERT(list == m_atomicNameCaches.get(namedNodeListKey(collectionType, name))); 154 if (deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(list->ownerNode())) 155 return; 156 m_atomicNameCaches.remove(namedNodeListKey(collectionType, name)); 157 } 158 159 void removeCache(LiveNodeListBase* list, const AtomicString& namespaceURI, const AtomicString& localName) 160 { 161 QualifiedName name(nullAtom, localName, namespaceURI); 162 ASSERT(list == m_tagCollectionCacheNS.get(name)); 163 if (deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(list->ownerNode())) 164 return; 165 m_tagCollectionCacheNS.remove(name); 166 } 167 #endif 168 169 static PassOwnPtrWillBeRawPtr<NodeListsNodeData> create() 170 { 171 return adoptPtrWillBeNoop(new NodeListsNodeData); 172 } 173 174 void invalidateCaches(const QualifiedName* attrName = 0); 175 176 bool isEmpty() const 177 { 178 return !m_childNodeList && m_atomicNameCaches.isEmpty() && m_tagCollectionCacheNS.isEmpty(); 179 } 180 181 void adoptTreeScope() 182 { 183 invalidateCaches(); 184 } 185 186 void adoptDocument(Document& oldDocument, Document& newDocument) 187 { 188 ASSERT(oldDocument != newDocument); 189 190 NodeListAtomicNameCacheMap::const_iterator atomicNameCacheEnd = m_atomicNameCaches.end(); 191 for (NodeListAtomicNameCacheMap::const_iterator it = m_atomicNameCaches.begin(); it != atomicNameCacheEnd; ++it) { 192 LiveNodeListBase* list = it->value; 193 list->didMoveToDocument(oldDocument, newDocument); 194 } 195 196 TagCollectionCacheNS::const_iterator tagEnd = m_tagCollectionCacheNS.end(); 197 for (TagCollectionCacheNS::const_iterator it = m_tagCollectionCacheNS.begin(); it != tagEnd; ++it) { 198 LiveNodeListBase* list = it->value; 199 ASSERT(!list->isRootedAtDocument()); 200 list->didMoveToDocument(oldDocument, newDocument); 201 } 202 } 203 204 void trace(Visitor*); 205 206 private: 207 NodeListsNodeData() 208 : m_childNodeList(nullptr) 209 { } 210 211 std::pair<unsigned char, StringImpl*> namedNodeListKey(CollectionType type, const AtomicString& name) 212 { 213 // Holding the raw StringImpl is safe because |name| is retained by the NodeList and the NodeList 214 // is reponsible for removing itself from the cache on deletion. 215 return std::pair<unsigned char, StringImpl*>(type, name.impl()); 216 } 217 218 #if !ENABLE(OILPAN) 219 bool deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(Node&); 220 #endif 221 222 // Can be a ChildNodeList or an EmptyNodeList. 223 RawPtrWillBeWeakMember<NodeList> m_childNodeList; 224 NodeListAtomicNameCacheMap m_atomicNameCaches; 225 TagCollectionCacheNS m_tagCollectionCacheNS; 226 }; 227 228 #if !ENABLE(OILPAN) 229 inline bool NodeListsNodeData::deleteThisAndUpdateNodeRareDataIfAboutToRemoveLastList(Node& ownerNode) 230 { 231 ASSERT(ownerNode.nodeLists() == this); 232 if ((m_childNodeList ? 1 : 0) + m_atomicNameCaches.size() + m_tagCollectionCacheNS.size() != 1) 233 return false; 234 ownerNode.clearNodeLists(); 235 return true; 236 } 237 #endif 238 239 template <typename Collection> 240 inline PassRefPtrWillBeRawPtr<Collection> ContainerNode::ensureCachedCollection(CollectionType type) 241 { 242 return ensureNodeLists().addCache<Collection>(*this, type); 243 } 244 245 template <typename Collection> 246 inline PassRefPtrWillBeRawPtr<Collection> ContainerNode::ensureCachedCollection(CollectionType type, const AtomicString& name) 247 { 248 return ensureNodeLists().addCache<Collection>(*this, type, name); 249 } 250 251 template <typename Collection> 252 inline PassRefPtrWillBeRawPtr<Collection> ContainerNode::ensureCachedCollection(CollectionType type, const AtomicString& namespaceURI, const AtomicString& localName) 253 { 254 ASSERT_UNUSED(type, type == TagCollectionType); 255 return ensureNodeLists().addCache(*this, namespaceURI, localName); 256 } 257 258 template <typename Collection> 259 inline Collection* ContainerNode::cachedCollection(CollectionType type) 260 { 261 NodeListsNodeData* nodeLists = this->nodeLists(); 262 return nodeLists ? nodeLists->cached<Collection>(type) : 0; 263 } 264 265 } // namespace blink 266 267 #endif // NodeListsNodeData_h 268